summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2018-11-04 11:26:42 +0000
committerEdward Thomson <ethomson@edwardthomson.com>2018-11-05 15:53:59 +0000
commit12f9ac17a63986f64799098bfd3b18c91b9c08a1 (patch)
tree9e07f6e28cc2e1c3717d8ad66a543781c258839c
parentb73a42f6aabe3f00ac01d9843c053c2393573d2b (diff)
downloadlibgit2-12f9ac17a63986f64799098bfd3b18c91b9c08a1.tar.gz
apply: validate unchanged mode when applying both
When applying to both the index and the working directory, ensure that the working directory's mode matches the index's mode. It's not sufficient to look only at the hashed object id to determine that the file is unchanged, git also takes the mode into account.
-rw-r--r--src/reader.c13
-rw-r--r--tests/apply/both.c17
2 files changed, 30 insertions, 0 deletions
diff --git a/src/reader.c b/src/reader.c
index 5de731bea..026bc2b51 100644
--- a/src/reader.c
+++ b/src/reader.c
@@ -79,6 +79,8 @@ static int workdir_reader_read(
{
workdir_reader *reader = (workdir_reader *)_reader;
git_buf path = GIT_BUF_INIT;
+ struct stat st;
+ git_filemode_t filemode;
git_filter_list *filters = NULL;
const git_index_entry *idx_entry;
git_oid id;
@@ -88,6 +90,16 @@ static int workdir_reader_read(
git_repository_workdir(reader->repo), filename)) < 0)
goto done;
+ if ((error = p_lstat(path.ptr, &st)) < 0) {
+ if (error == -1 && errno == ENOENT)
+ error = GIT_ENOTFOUND;
+
+ giterr_set(GITERR_OS, "could not stat '%s'", path.ptr);
+ goto done;
+ }
+
+ filemode = git_futils_canonical_mode(st.st_mode);
+
/*
* Patch application - for example - uses the filtered version of
* the working directory data to match git. So we will run the
@@ -108,6 +120,7 @@ static int workdir_reader_read(
if (reader->index) {
if (!(idx_entry = git_index_get_bypath(reader->index, filename, 0)) ||
+ filemode != idx_entry->mode ||
!git_oid_equal(&id, &idx_entry->id)) {
error = GIT_READER_MISMATCH;
goto done;
diff --git a/tests/apply/both.c b/tests/apply/both.c
index e8d13c763..abbabbdcb 100644
--- a/tests/apply/both.c
+++ b/tests/apply/both.c
@@ -204,6 +204,23 @@ void test_apply_both__index_must_match_workdir(void)
git_diff_free(diff);
}
+void test_apply_both__index_mode_must_match_workdir(void)
+{
+ git_diff *diff;
+
+ if (!cl_is_chmod_supported())
+ clar__skip();
+
+ /* Set a file in the working directory executable. */
+ cl_must_pass(p_chmod("merge-recursive/asparagus.txt", 0755));
+
+ cl_git_pass(git_diff_from_buffer(&diff, DIFF_MODIFY_TWO_FILES,
+ strlen(DIFF_MODIFY_TWO_FILES)));
+ cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ git_diff_free(diff);
+}
+
void test_apply_both__application_failure_leaves_workdir_unmodified(void)
{
git_diff *diff;