X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fextract.c;h=5aaeb1bf603192d01693f330043f3225a2e52690;hb=15c02c2b9d383446b3ea35dbea5a048e136b020d;hp=3d8ba10dbf539f50e3407eb0b56e8dd47213b997;hpb=28b44aaacb45680aa6640e0a6d7ceab04cfdbf11;p=chaz%2Ftar diff --git a/src/extract.c b/src/extract.c index 3d8ba10..5aaeb1b 100644 --- a/src/extract.c +++ b/src/extract.c @@ -1,7 +1,7 @@ /* Extract files from a tar archive. - Copyright 1988, 1992-1994, 1996-2001, 2003-2007, 2010, 2012-2013 - Free Software Foundation, Inc. + Copyright 1988, 1992-1994, 1996-2001, 2003-2007, 2010, 2012-2014 Free + Software Foundation, Inc. This file is part of GNU tar. @@ -537,6 +537,38 @@ repair_delayed_set_stat (char const *dir, quotearg_colon (dir))); } +static void +free_delayed_set_stat (struct delayed_set_stat *data) +{ + xheader_xattr_free (data->xattr_map, data->xattr_map_size); + free (data->cntx_name); + free (data->acls_a_ptr); + free (data->acls_d_ptr); + free (data); +} + +void +remove_delayed_set_stat (const char *fname) +{ + struct delayed_set_stat *data, *next, *prev = NULL; + for (data = delayed_set_stat_head; data; data = next) + { + next = data->next; + if (chdir_current == data->change_dir + && strcmp (data->file_name, fname) == 0) + { + free_delayed_set_stat (data); + if (prev) + prev->next = next; + else + delayed_set_stat_head = next; + return; + } + else + prev = data; + } +} + /* After a file/link/directory creation has failed, see if it's because some required directory was not present, and if so, create all required directories. Return zero if all the required @@ -846,16 +878,26 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links) } delayed_set_stat_head = data->next; - xheader_xattr_free (data->xattr_map, data->xattr_map_size); - free (data->cntx_name); - free (data->acls_a_ptr); - free (data->acls_d_ptr); - free (data); + free_delayed_set_stat (data); } } - +static bool +is_directory_link (const char *file_name) +{ + struct stat st; + int e = errno; + int res; + + res = (fstatat (chdir_fd, file_name, &st, AT_SYMLINK_NOFOLLOW) == 0 && + S_ISLNK (st.st_mode) && + fstatat (chdir_fd, file_name, &st, 0) == 0 && + S_ISDIR (st.st_mode)); + errno = e; + return res; +} + /* Extractor functions for various member types */ static int @@ -911,10 +953,15 @@ extract_dir (char *file_name, int typeflag) if (errno == EEXIST && (interdir_made + || keep_directory_symlink_option || old_files_option == DEFAULT_OLD_FILES || old_files_option == OVERWRITE_OLD_FILES)) { struct stat st; + + if (keep_directory_symlink_option && is_directory_link (file_name)) + return 0; + if (deref_stat (file_name, &st) == 0) { current_mode = st.st_mode;