From b1735b1ab7743700064af528a998cdc952c8b9a1 Mon Sep 17 00:00:00 2001 From: Clemens Buchacher Date: Sat, 9 Oct 2010 15:53:00 +0200 Subject: [PATCH] do not overwrite files in leading path If the work tree contains an untracked file x, and unpack-trees wants to checkout a path x/*, the file x is removed unconditionally. Instead, apply the same checks that are normally used for untracked files, and abort if the file cannot be removed. Signed-off-by: Clemens Buchacher Signed-off-by: Junio C Hamano --- cache.h | 2 +- symlinks.c | 21 ++++++++++++++++----- t/t7607-merge-overwrite.sh | 2 +- unpack-trees.c | 16 +++++++++++++--- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/cache.h b/cache.h index 33decd942d..d85ce86f7f 100644 --- a/cache.h +++ b/cache.h @@ -859,7 +859,7 @@ struct cache_def { extern int has_symlink_leading_path(const char *name, int len); extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int); -extern int has_symlink_or_noent_leading_path(const char *name, int len); +extern int check_leading_path(const char *name, int len); extern int has_dirs_only_path(const char *name, int len, int prefix_len); extern void schedule_dir_for_removal(const char *name, int len); extern void remove_scheduled_dirs(void); diff --git a/symlinks.c b/symlinks.c index b7343ebd62..3cacebd91a 100644 --- a/symlinks.c +++ b/symlinks.c @@ -209,15 +209,26 @@ int has_symlink_leading_path(const char *name, int len) } /* - * Return non-zero if path 'name' has a leading symlink component or + * Return zero if path 'name' has a leading symlink component or * if some leading path component does not exists. + * + * Return -1 if leading path exists and is a directory. + * + * Return path length if leading path exists and is neither a + * directory nor a symlink. */ -int has_symlink_or_noent_leading_path(const char *name, int len) +int check_leading_path(const char *name, int len) { struct cache_def *cache = &default_cache; /* FIXME */ - return lstat_cache(cache, name, len, - FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) & - (FL_SYMLINK|FL_NOENT); + int flags; + int match_len = lstat_cache_matchlen(cache, name, len, &flags, + FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT); + if (flags & (FL_SYMLINK|FL_NOENT)) + return 0; + else if (flags & FL_DIR) + return -1; + else + return match_len; } /* diff --git a/t/t7607-merge-overwrite.sh b/t/t7607-merge-overwrite.sh index f8e8ff0b6f..d4a499dee2 100755 --- a/t/t7607-merge-overwrite.sh +++ b/t/t7607-merge-overwrite.sh @@ -100,7 +100,7 @@ test_expect_success 'will not overwrite untracked subtree' ' test_cmp important sub/f/important ' -test_expect_failure 'will not overwrite untracked file in leading path' ' +test_expect_success 'will not overwrite untracked file in leading path' ' git reset --hard c0 && rm -rf sub && cp important sub && diff --git a/unpack-trees.c b/unpack-trees.c index df1c9209d4..6816113420 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -182,7 +182,7 @@ static void display_error_msgs(struct unpack_trees_options *o) */ static void unlink_entry(struct cache_entry *ce) { - if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce))) + if (!check_leading_path(ce->name, ce_namelen(ce))) return; if (remove_or_warn(ce->ce_mode, ce->name)) return; @@ -1194,18 +1194,28 @@ static int verify_absent_1(struct cache_entry *ce, enum unpack_trees_error_types error_type, struct unpack_trees_options *o) { + int len; struct stat st; if (o->index_only || o->reset || !o->update) return 0; - if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce))) + len = check_leading_path(ce->name, ce_namelen(ce)); + if (!len) return 0; + else if (len > 0) { + char path[PATH_MAX + 1]; + memcpy(path, ce->name, len); + path[len] = 0; + lstat(path, &st); - if (!lstat(ce->name, &st)) + return check_ok_to_remove(path, len, DT_UNKNOWN, NULL, &st, + error_type, o); + } else if (!lstat(ce->name, &st)) return check_ok_to_remove(ce->name, ce_namelen(ce), ce_to_dtype(ce), ce, &st, error_type, o); + return 0; }