From: Paul Eggert Date: Wed, 12 Sep 2001 06:46:23 +0000 (+0000) Subject: (struct delayed_set_stat): New member 'after_symlinks'. X-Git-Url: https://git.brokenzipper.com/gitweb?a=commitdiff_plain;h=95dda72cc6231e3966fcaeaa16d6b55a45f602ed;p=chaz%2Ftar (struct delayed_set_stat): New member 'after_symlinks'. (delay_set_stat): Initialize it to 0. (set_mode): New arg current_stat_info. Use it (if nonnull) to avoid taking an extra stat ourselves. All callers changed. (set_stat): Likewise. (apply_nonancestor_delayed_set_stat): New arg 'after_symlinks'. If false, stop when encountering a struct whose 'after_symlinks' member is true. Otherwise, go through all structures but check them more carefully. All callers changed. (extract_archive): When extracting a deferred symlink, if its parent directory's status needs fixing, then mark the directory as needing to be fixed after symlinks. (extract_finish): Fix status of ordinary directories, then apply delayed symlinks, then fix the status of directories that are ancestors of delayed symlinks. --- diff --git a/src/extract.c b/src/extract.c index ee3720d..eac86cc 100644 --- a/src/extract.c +++ b/src/extract.c @@ -53,9 +53,14 @@ enum permstatus }; /* List of directories whose statuses we need to extract after we've - finished extracting their subsidiary files. The head of the list - has the longest name; each non-head element in the list is an - ancestor (in the directory hierarchy) of the preceding element. */ + finished extracting their subsidiary files. If you consider each + contiguous subsequence of elements of the form [D]?[^D]*, where [D] + represents an element where AFTER_SYMLINKS is nonzero and [^D] + represents an element where AFTER_SYMLINKS is zero, then the head + of the subsequence has the longest name, and each non-head element + in the prefix is an ancestor (in the directory hierarchy) of the + preceding element. */ + struct delayed_set_stat { struct delayed_set_stat *next; @@ -63,6 +68,7 @@ struct delayed_set_stat size_t file_name_len; mode_t invert_permissions; enum permstatus permstatus; + bool after_symlinks; char file_name[1]; }; @@ -123,12 +129,14 @@ extr_init (void) } /* If restoring permissions, restore the mode for FILE_NAME from - information given in *STAT_INFO; otherwise invert the + information given in *STAT_INFO (where *CURRENT_STAT_INFO gives + the current status if CURRENT_STAT_INFO is nonzero); otherwise invert the INVERT_PERMISSIONS bits from the file's current permissions. PERMSTATUS specifies the status of the file's permissions. TYPEFLAG specifies the type of the file. */ static void set_mode (char const *file_name, struct stat const *stat_info, + struct stat const *current_stat_info, mode_t invert_permissions, enum permstatus permstatus, char typeflag) { @@ -160,12 +168,16 @@ set_mode (char const *file_name, struct stat const *stat_info, that we created, so there's no point optimizing this code for other cases. */ struct stat st; - if (stat (file_name, &st) != 0) + if (! current_stat_info) { - stat_error (file_name); - return; + if (stat (file_name, &st) != 0) + { + stat_error (file_name); + return; + } + current_stat_info = &st; } - mode = st.st_mode ^ invert_permissions; + mode = current_stat_info->st_mode ^ invert_permissions; } if (chmod (file_name, mode) != 0) @@ -184,6 +196,8 @@ check_time (char const *file_name, time_t t) /* Restore stat attributes (owner, group, mode and times) for FILE_NAME, using information given in *STAT_INFO. + If CURRENT_STAT_INFO is nonzero, *CURRENT_STAT_INFO is the + file's currernt status. If not restoring permissions, invert the INVERT_PERMISSIONS bits from the file's current permissions. PERMSTATUS specifies the status of the file's permissions. @@ -196,6 +210,7 @@ check_time (char const *file_name, time_t t) static void set_stat (char const *file_name, struct stat const *stat_info, + struct stat const *current_stat_info, mode_t invert_permissions, enum permstatus permstatus, char typeflag) { @@ -234,7 +249,7 @@ set_stat (char const *file_name, struct stat const *stat_info, done, it is not possible anymore to change file permissions, so we have to set permissions prior to possibly giving files away. */ - set_mode (file_name, stat_info, + set_mode (file_name, stat_info, current_stat_info, invert_permissions, permstatus, typeflag); } @@ -263,7 +278,7 @@ set_stat (char const *file_name, struct stat const *stat_info, away, changing the owner or group destroys the suid or sgid bits. So let's attempt setting these bits once more. */ if (stat_info->st_mode & (S_ISUID | S_ISGID | S_ISVTX)) - set_mode (file_name, stat_info, + set_mode (file_name, stat_info, 0, invert_permissions, permstatus, typeflag); } } @@ -287,6 +302,7 @@ delay_set_stat (char const *file_name, struct stat const *stat_info, strcpy (data->file_name, file_name); data->invert_permissions = invert_permissions; data->permstatus = permstatus; + data->after_symlinks = 0; data->stat_info = *stat_info; data->next = delayed_set_stat_head; delayed_set_stat_head = data; @@ -519,24 +535,55 @@ extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name) } /* Fix the statuses of all directories whose statuses need fixing, and - which are not ancestors of FILE_NAME. */ + which are not ancestors of FILE_NAME. If AFTER_SYMLINKS is + nonzero, do this for all such directories; otherwise, stop at the + first directory that is marked to be fixed up only after delayed + symlinks are applied. */ static void -apply_nonancestor_delayed_set_stat (char const *file_name) +apply_nonancestor_delayed_set_stat (char const *file_name, bool after_symlinks) { size_t file_name_len = strlen (file_name); + bool check_for_renamed_directories = 0; while (delayed_set_stat_head) { struct delayed_set_stat *data = delayed_set_stat_head; - if (data->file_name_len < file_name_len - && file_name[data->file_name_len] - && (ISSLASH (file_name[data->file_name_len]) - || ISSLASH (file_name[data->file_name_len - 1])) - && memcmp (file_name, data->file_name, data->file_name_len) == 0) + bool skip_this_one = 0; + check_for_renamed_directories |= data->after_symlinks; + struct stat st; + struct stat const *current_stat_info = 0; + + if (after_symlinks < data->after_symlinks + || (data->file_name_len < file_name_len + && file_name[data->file_name_len] + && (ISSLASH (file_name[data->file_name_len]) + || ISSLASH (file_name[data->file_name_len - 1])) + && memcmp (file_name, data->file_name, data->file_name_len) == 0)) break; + + if (check_for_renamed_directories) + { + current_stat_info = &st; + if (stat (data->file_name, &st) != 0) + { + stat_error (data->file_name); + skip_this_one = 1; + } + else if (! (st.st_dev == data->stat_info.st_dev + && (st.st_ino == data->stat_info.st_ino))) + { + ERROR ((0, 0, + _("%s: Directory renamed before its status could be extracted"), + quotearg_colon (data->file_name))); + skip_this_one = 1; + } + } + + if (! skip_this_one) + set_stat (data->file_name, &data->stat_info, current_stat_info, + data->invert_permissions, data->permstatus, DIRTYPE); + delayed_set_stat_head = data->next; - set_stat (data->file_name, &data->stat_info, - data->invert_permissions, data->permstatus, DIRTYPE); free (data); } } @@ -606,7 +653,7 @@ extract_archive (void) } } - apply_nonancestor_delayed_set_stat (CURRENT_FILE_NAME); + apply_nonancestor_delayed_set_stat (CURRENT_FILE_NAME, 0); /* Take a safety backup of a previously existing file. */ @@ -849,7 +896,7 @@ extract_archive (void) undo_last_backup (); } - set_stat (CURRENT_FILE_NAME, ¤t_stat, 0, + set_stat (CURRENT_FILE_NAME, ¤t_stat, 0, 0, (old_files_option == OVERWRITE_OLD_FILES ? UNKNOWN_PERMSTATUS : ARCHIVED_PERMSTATUS), @@ -872,7 +919,7 @@ extract_archive (void) break; if (status == 0) - set_stat (CURRENT_FILE_NAME, ¤t_stat, 0, 0, SYMTYPE); + set_stat (CURRENT_FILE_NAME, ¤t_stat, 0, 0, 0, SYMTYPE); else symlink_error (current_link_name, CURRENT_FILE_NAME); } @@ -900,6 +947,7 @@ extract_archive (void) close_error (CURRENT_FILE_NAME); else { + struct delayed_set_stat *h; struct delayed_symlink *p = xmalloc (offsetof (struct delayed_symlink, target) + strlen (current_link_name) + 1); @@ -915,6 +963,25 @@ extract_archive (void) p->sources->next = 0; strcpy (p->sources->string, CURRENT_FILE_NAME); strcpy (p->target, current_link_name); + + h = delayed_set_stat_head; + if (h && ! h->after_symlinks + && strncmp (CURRENT_FILE_NAME, h->file_name, h->file_name_len) == 0 + && ISSLASH (CURRENT_FILE_NAME[h->file_name_len]) + && (base_name (CURRENT_FILE_NAME) + == CURRENT_FILE_NAME + h->file_name_len + 1)) + { + h->after_symlinks = 1; + + if (stat (h->file_name, &st) != 0) + stat_error (h->file_name); + else + { + h->stat_info.st_dev = st.st_dev; + h->stat_info.st_ino = st.st_ino; + } + } + status = 0; } } @@ -1015,7 +1082,7 @@ extract_archive (void) undo_last_backup (); break; }; - set_stat (CURRENT_FILE_NAME, ¤t_stat, 0, + set_stat (CURRENT_FILE_NAME, ¤t_stat, 0, 0, ARCHIVED_PERMSTATUS, typeflag); break; #endif @@ -1031,7 +1098,7 @@ extract_archive (void) break; if (status == 0) - set_stat (CURRENT_FILE_NAME, ¤t_stat, 0, + set_stat (CURRENT_FILE_NAME, ¤t_stat, 0, 0, ARCHIVED_PERMSTATUS, typeflag); else { @@ -1190,7 +1257,7 @@ apply_delayed_symlinks (void) valid_source = source; st.st_uid = ds->uid; st.st_gid = ds->gid; - set_stat (source, &st, 0, 0, SYMTYPE); + set_stat (source, &st, 0, 0, 0, SYMTYPE); } } } @@ -1216,10 +1283,16 @@ apply_delayed_symlinks (void) void extract_finish (void) { - /* Apply delayed symlinks last, so that they don't affect - delayed directory status-setting. */ - apply_nonancestor_delayed_set_stat (""); + /* First, fix the status of ordinary directories that need fixing. */ + apply_nonancestor_delayed_set_stat ("", 0); + + /* Then, apply delayed symlinks, so that they don't affect delayed + directory status-setting for ordinary directories. */ apply_delayed_symlinks (); + + /* Finally, fix the status of directories that are ancestors + of delayed symlinks. */ + apply_nonancestor_delayed_set_stat ("", 1); } void