Merge branch 'bc/object-id'
[gitweb.git] / builtin / apply.c
index 955cc794995ac1a7269c0f5175f55999e75ad641..146be97a1a879242aa12eb066c5c69cd6af92409 100644 (file)
@@ -51,6 +51,7 @@ static int apply_verbosely;
 static int allow_overlap;
 static int no_add;
 static int threeway;
+static int unsafe_paths;
 static const char *fake_ancestor;
 static int line_termination = '\n';
 static unsigned int p_context = UINT_MAX;
@@ -207,7 +208,7 @@ struct patch {
        struct patch *next;
 
        /* three-way fallback result */
-       unsigned char threeway_stage[3][20];
+       struct object_id threeway_stage[3];
 };
 
 static void free_fragment_list(struct fragment *list)
@@ -1600,6 +1601,9 @@ static int parse_fragment(const char *line, unsigned long size,
                        if (!deleted && !added)
                                leading++;
                        trailing++;
+                       if (!apply_in_reverse &&
+                           ws_error_action == correct_ws_error)
+                               check_whitespace(line, len, patch->ws_rule);
                        break;
                case '-':
                        if (apply_in_reverse &&
@@ -2772,7 +2776,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                default:
                        if (apply_verbosely)
                                error(_("invalid start of line: '%c'"), first);
-                       return -1;
+                       applied_pos = -1;
+                       goto out;
                }
                if (added_blank_line) {
                        if (!new_blank_lines_at_end)
@@ -2911,6 +2916,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
                              (int)(old - oldlines), oldlines);
        }
 
+out:
        free(oldlines);
        strbuf_release(&newlines);
        free(preimage.line_allocated);
@@ -3221,7 +3227,7 @@ static int load_patch_target(struct strbuf *buf,
                             const char *name,
                             unsigned expected_mode)
 {
-       if (cached) {
+       if (cached || check_index) {
                if (read_file_or_gitlink(ce, buf))
                        return error(_("read of %s failed"), name);
        } else if (name) {
@@ -3230,6 +3236,8 @@ static int load_patch_target(struct strbuf *buf,
                                return read_file_or_gitlink(ce, buf);
                        else
                                return SUBMODULE_PATCH_WITHOUT_INDEX;
+               } else if (has_symlink_leading_path(name, strlen(name))) {
+                       return error(_("reading from '%s' beyond a symbolic link"), name);
                } else {
                        if (read_old_data(st, name, buf))
                                return error(_("read of %s failed"), name);
@@ -3418,11 +3426,11 @@ static int try_threeway(struct image *image, struct patch *patch,
        if (status) {
                patch->conflicted_threeway = 1;
                if (patch->is_new)
-                       hashclr(patch->threeway_stage[0]);
+                       oidclr(&patch->threeway_stage[0]);
                else
-                       hashcpy(patch->threeway_stage[0], pre_sha1);
-               hashcpy(patch->threeway_stage[1], our_sha1);
-               hashcpy(patch->threeway_stage[2], post_sha1);
+                       hashcpy(patch->threeway_stage[0].hash, pre_sha1);
+               hashcpy(patch->threeway_stage[1].hash, our_sha1);
+               hashcpy(patch->threeway_stage[2].hash, post_sha1);
                fprintf(stderr, "Applied patch to '%s' with conflicts.\n", patch->new_name);
        } else {
                fprintf(stderr, "Applied patch to '%s' cleanly.\n", patch->new_name);
@@ -3569,6 +3577,121 @@ static int check_to_create(const char *new_name, int ok_if_exists)
        return 0;
 }
 
+/*
+ * We need to keep track of how symlinks in the preimage are
+ * manipulated by the patches.  A patch to add a/b/c where a/b
+ * is a symlink should not be allowed to affect the directory
+ * the symlink points at, but if the same patch removes a/b,
+ * it is perfectly fine, as the patch removes a/b to make room
+ * to create a directory a/b so that a/b/c can be created.
+ */
+static struct string_list symlink_changes;
+#define SYMLINK_GOES_AWAY 01
+#define SYMLINK_IN_RESULT 02
+
+static uintptr_t register_symlink_changes(const char *path, uintptr_t what)
+{
+       struct string_list_item *ent;
+
+       ent = string_list_lookup(&symlink_changes, path);
+       if (!ent) {
+               ent = string_list_insert(&symlink_changes, path);
+               ent->util = (void *)0;
+       }
+       ent->util = (void *)(what | ((uintptr_t)ent->util));
+       return (uintptr_t)ent->util;
+}
+
+static uintptr_t check_symlink_changes(const char *path)
+{
+       struct string_list_item *ent;
+
+       ent = string_list_lookup(&symlink_changes, path);
+       if (!ent)
+               return 0;
+       return (uintptr_t)ent->util;
+}
+
+static void prepare_symlink_changes(struct patch *patch)
+{
+       for ( ; patch; patch = patch->next) {
+               if ((patch->old_name && S_ISLNK(patch->old_mode)) &&
+                   (patch->is_rename || patch->is_delete))
+                       /* the symlink at patch->old_name is removed */
+                       register_symlink_changes(patch->old_name, SYMLINK_GOES_AWAY);
+
+               if (patch->new_name && S_ISLNK(patch->new_mode))
+                       /* the symlink at patch->new_name is created or remains */
+                       register_symlink_changes(patch->new_name, SYMLINK_IN_RESULT);
+       }
+}
+
+static int path_is_beyond_symlink_1(struct strbuf *name)
+{
+       do {
+               unsigned int change;
+
+               while (--name->len && name->buf[name->len] != '/')
+                       ; /* scan backwards */
+               if (!name->len)
+                       break;
+               name->buf[name->len] = '\0';
+               change = check_symlink_changes(name->buf);
+               if (change & SYMLINK_IN_RESULT)
+                       return 1;
+               if (change & SYMLINK_GOES_AWAY)
+                       /*
+                        * This cannot be "return 0", because we may
+                        * see a new one created at a higher level.
+                        */
+                       continue;
+
+               /* otherwise, check the preimage */
+               if (check_index) {
+                       struct cache_entry *ce;
+
+                       ce = cache_file_exists(name->buf, name->len, ignore_case);
+                       if (ce && S_ISLNK(ce->ce_mode))
+                               return 1;
+               } else {
+                       struct stat st;
+                       if (!lstat(name->buf, &st) && S_ISLNK(st.st_mode))
+                               return 1;
+               }
+       } while (1);
+       return 0;
+}
+
+static int path_is_beyond_symlink(const char *name_)
+{
+       int ret;
+       struct strbuf name = STRBUF_INIT;
+
+       assert(*name_ != '\0');
+       strbuf_addstr(&name, name_);
+       ret = path_is_beyond_symlink_1(&name);
+       strbuf_release(&name);
+
+       return ret;
+}
+
+static void die_on_unsafe_path(struct patch *patch)
+{
+       const char *old_name = NULL;
+       const char *new_name = NULL;
+       if (patch->is_delete)
+               old_name = patch->old_name;
+       else if (!patch->is_new && !patch->is_copy)
+               old_name = patch->old_name;
+       if (!patch->is_delete)
+               new_name = patch->new_name;
+
+       if (old_name && !verify_path(old_name))
+               die(_("invalid path '%s'"), old_name);
+       if (new_name && !verify_path(new_name))
+               die(_("invalid path '%s'"), new_name);
+}
+
 /*
  * Check and apply the patch in-core; leave the result in patch->result
  * for the caller to write it out to the final destination.
@@ -3656,6 +3779,22 @@ static int check_patch(struct patch *patch)
                }
        }
 
+       if (!unsafe_paths)
+               die_on_unsafe_path(patch);
+
+       /*
+        * An attempt to read from or delete a path that is beyond a
+        * symbolic link will be prevented by load_patch_target() that
+        * is called at the beginning of apply_data() so we do not
+        * have to worry about a patch marked with "is_delete" bit
+        * here.  We however need to make sure that the patch result
+        * is not deposited to a path that is beyond a symbolic link
+        * here.
+        */
+       if (!patch->is_delete && path_is_beyond_symlink(patch->new_name))
+               return error(_("affected file '%s' is beyond a symbolic link"),
+                            patch->new_name);
+
        if (apply_data(patch, &st, ce) < 0)
                return error(_("%s: patch does not apply"), name);
        patch->rejected = 0;
@@ -3666,6 +3805,7 @@ static int check_patch_list(struct patch *patch)
 {
        int err = 0;
 
+       prepare_symlink_changes(patch);
        prepare_fn_table(patch);
        while (patch) {
                if (apply_verbosely)
@@ -4046,14 +4186,14 @@ static void add_conflicted_stages_file(struct patch *patch)
 
        remove_file_from_cache(patch->new_name);
        for (stage = 1; stage < 4; stage++) {
-               if (is_null_sha1(patch->threeway_stage[stage - 1]))
+               if (is_null_oid(&patch->threeway_stage[stage - 1]))
                        continue;
                ce = xcalloc(1, ce_size);
                memcpy(ce->name, patch->new_name, namelen);
                ce->ce_mode = create_ce_mode(mode);
                ce->ce_flags = create_ce_flags(stage);
                ce->ce_namelen = namelen;
-               hashcpy(ce->sha1, patch->threeway_stage[stage - 1]);
+               hashcpy(ce->sha1, patch->threeway_stage[stage - 1].hash);
                if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
                        die(_("unable to add cache entry for %s"), patch->new_name);
        }
@@ -4404,6 +4544,8 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
                        N_("make sure the patch is applicable to the current index")),
                OPT_BOOL(0, "cached", &cached,
                        N_("apply a patch without touching the working tree")),
+               OPT_BOOL(0, "unsafe-paths", &unsafe_paths,
+                       N_("accept a patch that touches outside the working area")),
                OPT_BOOL(0, "apply", &force_apply,
                        N_("also apply the patch (use with --stat/--summary/--check)")),
                OPT_BOOL('3', "3way", &threeway,
@@ -4476,6 +4618,9 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
                        die(_("--cached outside a repository"));
                check_index = 1;
        }
+       if (check_index)
+               unsafe_paths = 0;
+
        for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
                int fd;