X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fextract.c;h=b6fdbbba96534b61daf6acd7766757e32c32183a;hb=2af87fa2776c8125a587a9b0c2c4fae3bf921ff7;hp=319aaa86b29cdf2dd71830901e36305dda478315;hpb=87369861f9afc8380e866ead3348d630821bcc02;p=chaz%2Ftar diff --git a/src/extract.c b/src/extract.c index 319aaa8..b6fdbbb 100644 --- a/src/extract.c +++ b/src/extract.c @@ -191,6 +191,35 @@ extr_init (void) umask (newdir_umask); /* restore the kernel umask */ current_umask = newdir_umask; } + + /* If the user wants to guarantee that everything is under one directory, + determine its name now and let it be created later. */ + if (one_top_level_option) + { + int i; + char *base = base_name (archive_name_array[0]); + + for (i = strlen (base) - 1; i > 2; i--) + if (!strncmp (base + i - 3, ".tar", 4) || + !strncmp (base + i - 3, ".taz", 4) || + !strncmp (base + i - 3, ".tbz", 4) || + !strncmp (base + i - 3, ".tb2", 4) || + !strncmp (base + i - 3, ".tgz", 4) || + !strncmp (base + i - 3, ".tlz", 4) || + !strncmp (base + i - 3, ".txz", 4)) break; + + if (i <= 3) + { + one_top_level_option = false; + free (base); + return; + } + + one_top_level = xmalloc (i - 2); + strncpy (one_top_level, base, i - 3); + one_top_level[i - 3] = '\0'; + free (base); + } } /* Use fchmod if possible, fchmodat otherwise. */ @@ -297,7 +326,7 @@ set_mode (char const *file_name, static void check_time (char const *file_name, struct timespec t) { - if (t.tv_sec <= 0) + if (t.tv_sec < 0) WARNOPT (WARN_TIMESTAMP, (0, 0, _("%s: implausibly old time stamp %s"), file_name, tartime (t, true))); @@ -855,7 +884,21 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links) } - +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 +954,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; @@ -1559,6 +1607,33 @@ prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun) return 1; } +void +maybe_prepend_name (char **file_name) +{ + int i; + + for (i = 0; i < strlen (*file_name); i++) + if (!ISSLASH ((*file_name)[i]) && (*file_name)[i] != '.') break; + + if (i == strlen (*file_name)) + return; + + if (!strncmp (*file_name + i, one_top_level, strlen (one_top_level))) + { + int pos = i + strlen (one_top_level); + if (ISSLASH ((*file_name)[pos]) || (*file_name)[pos] == '\0') return; + } + + char *new_name = xmalloc (strlen (one_top_level) + strlen (*file_name) + 2); + + strcpy (new_name, one_top_level); + strcat (new_name, "/"); + strcat (new_name, *file_name); + + free (*file_name); + *file_name = new_name; +} + /* Extract a file from the archive. */ void extract_archive (void) @@ -1609,6 +1684,9 @@ extract_archive (void) typeflag = sparse_member_p (¤t_stat_info) ? GNUTYPE_SPARSE : current_header->header.typeflag; + if (one_top_level_option) + maybe_prepend_name (¤t_stat_info.file_name); + if (prepare_to_extract (current_stat_info.file_name, typeflag, &fun)) { if (fun && (*fun) (current_stat_info.file_name, typeflag)