X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fextract.c;h=c01c88d12b58d10749d4a0a6b848c7d7a6c0765c;hb=48ce2946d97a7eea01cdde404042ad7b7c3c3151;hp=9be0a8b1392b95ae200192bdb131c86f0963f532;hpb=d659cbaccdc1f3279c49107cf15f15a639738529;p=chaz%2Ftar diff --git a/src/extract.c b/src/extract.c index 9be0a8b..c01c88d 100644 --- a/src/extract.c +++ b/src/extract.c @@ -48,8 +48,8 @@ enum permstatus /* List of directories whose statuses we need to extract after we've 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 + represents an element where AFTER_LINKS is nonzero and [^D] + represents an element where AFTER_LINKS 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. */ @@ -57,32 +57,41 @@ enum permstatus struct delayed_set_stat { struct delayed_set_stat *next; - struct stat stat_info; + dev_t dev; + ino_t ino; + mode_t mode; + uid_t uid; + gid_t gid; + struct timespec atime; + struct timespec mtime; size_t file_name_len; mode_t invert_permissions; enum permstatus permstatus; - bool after_symlinks; + bool after_links; char file_name[1]; }; static struct delayed_set_stat *delayed_set_stat_head; -/* List of symbolic links whose creation we have delayed. */ -struct delayed_symlink +/* List of links whose creation we have delayed. */ +struct delayed_link { - /* The next delayed symbolic link in the list. */ - struct delayed_symlink *next; + /* The next delayed link in the list. */ + struct delayed_link *next; /* The device, inode number and last-modified time of the placeholder. */ dev_t dev; ino_t ino; - time_t mtime; + struct timespec mtime; - /* The desired owner and group of the symbolic link. */ + /* True if the link is symbolic. */ + bool is_symlink; + + /* The desired owner and group of the link, if it is a symlink. */ uid_t uid; gid_t gid; - /* A list of sources for this symlink. The sources are all to be + /* A list of sources for this link. The sources are all to be hard-linked together. */ struct string_list *sources; @@ -90,7 +99,7 @@ struct delayed_symlink char target[1]; }; -static struct delayed_symlink *delayed_symlink_head; +static struct delayed_link *delayed_link_head; struct string_list { @@ -106,19 +115,6 @@ extr_init (void) same_permissions_option += we_are_root; same_owner_option += we_are_root; - /* Save 'root device' to avoid purging mount points. - FIXME: Should the same be done after handling -C option ? */ - if (one_file_system_option) - { - struct stat st; - char *dir = xgetcwd (); - - if (deref_stat (true, dir, &st)) - stat_diag (dir); - else - root_device = st.st_dev; - } - /* Option -p clears the kernel umask, so it does not affect proper restoration of file permissions. New intermediate directories will comply with umask at start of program. */ @@ -197,29 +193,29 @@ check_time (char const *file_name, struct timespec t) if (t.tv_sec <= 0) WARN ((0, 0, _("%s: implausibly old time stamp %s"), file_name, tartime (t, true))); - else if (timespec_lt (start_time, t)) + else if (timespec_cmp (start_time, t) < 0) { struct timespec now; gettime (&now); - if (timespec_lt (now, t)) + if (timespec_cmp (now, t) < 0) { - unsigned long int ds = t.tv_sec - now.tv_sec; - int dns = t.tv_nsec - now.tv_nsec; - char dnsbuf[sizeof ".FFFFFFFFF"]; - if (dns < 0) + char buf[TIMESPEC_STRSIZE_BOUND]; + struct timespec diff; + diff.tv_sec = t.tv_sec - now.tv_sec; + diff.tv_nsec = t.tv_nsec - now.tv_nsec; + if (diff.tv_nsec < 0) { - dns += 1000000000; - ds--; + diff.tv_nsec += BILLION; + diff.tv_sec--; } - code_ns_fraction (dns, dnsbuf); - WARN ((0, 0, _("%s: time stamp %s is %lu%s s in the future"), - file_name, tartime (t, true), ds, dnsbuf)); + WARN ((0, 0, _("%s: time stamp %s is %s s in the future"), + file_name, tartime (t, true), code_timespec (diff, buf))); } } } /* Restore stat attributes (owner, group, mode and times) for - FILE_NAME, using information given in *STAT_INFO. + FILE_NAME, using information given in *ST. If CUR_INFO is nonzero, *CUR_INFO is the file's currernt status. If not restoring permissions, invert the @@ -234,7 +230,7 @@ check_time (char const *file_name, struct timespec t) static void set_stat (char const *file_name, - struct stat const *stat_info, + struct tar_stat_info const *st, struct stat const *cur_info, mode_t invert_permissions, enum permstatus permstatus, char typeflag) @@ -253,8 +249,11 @@ set_stat (char const *file_name, /* FIXME: incremental_option should set ctime too, but how? */ struct timespec ts[2]; - ts[0] = incremental_option ? get_stat_atime (stat_info) : start_time; - ts[1] = get_stat_mtime (stat_info); + if (incremental_option) + ts[0] = st->atime; + else + ts[0] = start_time; + ts[1] = st->mtime; if (utimens (file_name, ts) != 0) utime_error (file_name); @@ -269,7 +268,7 @@ set_stat (char const *file_name, 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, cur_info, + set_mode (file_name, &st->stat, cur_info, invert_permissions, permstatus, typeflag); } @@ -283,48 +282,66 @@ set_stat (char const *file_name, if (typeflag == SYMTYPE) { #if HAVE_LCHOWN - if (lchown (file_name, stat_info->st_uid, stat_info->st_gid) < 0) + if (lchown (file_name, st->stat.st_uid, st->stat.st_gid) < 0) chown_error_details (file_name, - stat_info->st_uid, stat_info->st_gid); + st->stat.st_uid, st->stat.st_gid); #endif } else { - if (chown (file_name, stat_info->st_uid, stat_info->st_gid) < 0) + if (chown (file_name, st->stat.st_uid, st->stat.st_gid) < 0) chown_error_details (file_name, - stat_info->st_uid, stat_info->st_gid); + st->stat.st_uid, st->stat.st_gid); /* On a few systems, and in particular, those allowing to give files 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, 0, + if (st->stat.st_mode & (S_ISUID | S_ISGID | S_ISVTX)) + set_mode (file_name, &st->stat, 0, invert_permissions, permstatus, typeflag); } } } /* Remember to restore stat attributes (owner, group, mode and times) - for the directory FILE_NAME, using information given in *STAT_INFO, + for the directory FILE_NAME, using information given in *ST, once we stop extracting files into that directory. If not restoring permissions, remember to invert the INVERT_PERMISSIONS bits from the file's current permissions. - PERMSTATUS specifies the status of the file's permissions. */ + PERMSTATUS specifies the status of the file's permissions. + + NOTICE: this works only if the archive has usual member order, i.e. + directory, then the files in that directory. Incremental archive have + somewhat reversed order: first go subdirectories, then all other + members. To help cope with this case the variable + delay_directory_restore_option is set by prepare_to_extract. + + If an archive was explicitely created so that its member order is + reversed, some directory timestamps can be restored incorrectly, + e.g.: + tar --no-recursion -cf archive dir dir/file1 foo dir/file2 +*/ static void -delay_set_stat (char const *file_name, struct stat const *stat_info, +delay_set_stat (char const *file_name, struct tar_stat_info const *st, mode_t invert_permissions, enum permstatus permstatus) { size_t file_name_len = strlen (file_name); struct delayed_set_stat *data = xmalloc (offsetof (struct delayed_set_stat, file_name) + file_name_len + 1); + data->next = delayed_set_stat_head; + data->dev = st->stat.st_dev; + data->ino = st->stat.st_ino; + data->mode = st->stat.st_mode; + data->uid = st->stat.st_uid; + data->gid = st->stat.st_gid; + data->atime = st->atime; + data->mtime = st->mtime; data->file_name_len = file_name_len; - 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; + data->after_links = 0; + strcpy (data->file_name, file_name); delayed_set_stat_head = data; } @@ -349,7 +366,13 @@ repair_delayed_set_stat (char const *dir, if (st.st_dev == dir_stat_info->st_dev && st.st_ino == dir_stat_info->st_ino) { - data->stat_info = current_stat_info.stat; + data->dev = current_stat_info.stat.st_dev; + data->ino = current_stat_info.stat.st_ino; + data->mode = current_stat_info.stat.st_mode; + data->uid = current_stat_info.stat.st_uid; + data->gid = current_stat_info.stat.st_gid; + data->atime = current_stat_info.atime; + data->mtime = current_stat_info.mtime; data->invert_permissions = (MODE_RWX & (current_stat_info.stat.st_mode ^ st.st_mode)); data->permstatus = ARCHIVED_PERMSTATUS; @@ -361,7 +384,7 @@ repair_delayed_set_stat (char const *dir, quotearg_colon (dir))); } -/* After a file/link/symlink/directory creation has failed, see if +/* 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 non-zero if a directory was created. */ @@ -405,7 +428,7 @@ make_directories (char *file_name) invert_permissions is zero, because repair_delayed_set_stat may need to update the struct. */ delay_set_stat (file_name, - ¤t_stat_info.stat /* ignored */, + ¤t_stat_info /* ignored */, invert_permissions, INTERDIR_PERMSTATUS); print_for_mkdir (file_name, cursor - file_name, mode); @@ -445,7 +468,7 @@ file_newer_p (const char *file_name, struct tar_stat_info *tar_stat) return errno != ENOENT; } if (!S_ISDIR (st.st_mode) - && st.st_mtime >= tar_stat->stat.st_mtime) + && tar_timespec_cmp (tar_stat->mtime, get_stat_mtime (&st)) <= 0) { return true; } @@ -513,12 +536,12 @@ maybe_recoverable (char *file_name, int *interdir_made) } /* Fix the statuses of all directories whose statuses need fixing, and - which are not ancestors of FILE_NAME. If AFTER_SYMLINKS is + which are not ancestors of FILE_NAME. If AFTER_LINKS 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. */ + links are applied. */ static void -apply_nonancestor_delayed_set_stat (char const *file_name, bool after_symlinks) +apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links) { size_t file_name_len = strlen (file_name); bool check_for_renamed_directories = 0; @@ -530,9 +553,9 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_symlinks) struct stat st; struct stat const *cur_info = 0; - check_for_renamed_directories |= data->after_symlinks; + check_for_renamed_directories |= data->after_links; - if (after_symlinks < data->after_symlinks + if (after_links < data->after_links || (data->file_name_len < file_name_len && file_name[data->file_name_len] && (ISSLASH (file_name[data->file_name_len]) @@ -548,8 +571,7 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_symlinks) 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))) + else if (! (st.st_dev == data->dev && st.st_ino == data->ino)) { ERROR ((0, 0, _("%s: Directory renamed before its status could be extracted"), @@ -559,8 +581,16 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_symlinks) } if (! skip_this_one) - set_stat (data->file_name, &data->stat_info, cur_info, - data->invert_permissions, data->permstatus, DIRTYPE); + { + struct tar_stat_info st; + st.stat.st_mode = data->mode; + st.stat.st_uid = data->uid; + st.stat.st_gid = data->gid; + st.atime = data->atime; + st.mtime = data->mtime; + set_stat (data->file_name, &st, cur_info, + data->invert_permissions, data->permstatus, DIRTYPE); + } delayed_set_stat_head = data->next; free (data); @@ -578,13 +608,27 @@ extract_dir (char *file_name, int typeflag) mode_t mode; int interdir_made = 0; + /* Save 'root device' to avoid purging mount points. */ + if (one_file_system_option && root_device == 0) + { + struct stat st; + char *dir = xgetcwd (); + + if (deref_stat (true, dir, &st)) + stat_diag (dir); + else + root_device = st.st_dev; + free (dir); + } + if (incremental_option) /* Read the entry and delete files that aren't listed in the archive. */ purge_directory (file_name); else if (typeflag == GNUTYPE_DUMPDIR) skip_member (); - mode = (current_stat_info.stat.st_mode | (we_are_root ? 0 : MODE_WXUSR)) & MODE_RWX; + mode = (current_stat_info.stat.st_mode | + (we_are_root ? 0 : MODE_WXUSR)) & MODE_RWX; while ((status = mkdir (file_name, mode))) { @@ -624,7 +668,7 @@ extract_dir (char *file_name, int typeflag) if (status == 0 || old_files_option == DEFAULT_OLD_FILES || old_files_option == OVERWRITE_OLD_FILES) - delay_set_stat (file_name, ¤t_stat_info.stat, + delay_set_stat (file_name, ¤t_stat_info, MODE_RWX & (mode ^ current_stat_info.stat.st_mode), (status == 0 ? ARCHIVED_PERMSTATUS @@ -708,37 +752,33 @@ extract_file (char *file_name, int typeflag) } } + mv_begin (¤t_stat_info); if (current_stat_info.is_sparse) sparse_extract_file (fd, ¤t_stat_info, &size); else for (size = current_stat_info.stat.st_size; size > 0; ) { - if (multi_volume_option) - { - assign_string (&save_name, current_stat_info.orig_file_name); - save_totsize = current_stat_info.stat.st_size; - save_sizeleft = size; - } - + mv_size_left (size); + /* Locate data, determine max length writeable, write it, block that we have used the data, then check if the write worked. */ - + data_block = find_next_block (); if (! data_block) { ERROR ((0, 0, _("Unexpected EOF in archive"))); break; /* FIXME: What happens, then? */ } - + written = available_space_after (data_block); - + if (written > size) written = size; errno = 0; count = full_write (fd, data_block->buffer, written); size -= written; - + set_next_block_after ((union block *) (data_block->buffer + written - 1)); if (count != written) @@ -752,9 +792,8 @@ extract_file (char *file_name, int typeflag) skip_file (size); - if (multi_volume_option) - assign_string (&save_name, 0); - + mv_end (); + /* If writing to stdout, don't try to do anything to the filename; it doesn't exist, or we don't want to touch it anyway. */ @@ -768,7 +807,7 @@ extract_file (char *file_name, int typeflag) if (to_command_option) sys_wait_command (); else - set_stat (file_name, ¤t_stat_info.stat, 0, 0, + set_stat (file_name, ¤t_stat_info, NULL, 0, (old_files_option == OVERWRITE_OLD_FILES ? UNKNOWN_PERMSTATUS : ARCHIVED_PERMSTATUS), typeflag); @@ -776,6 +815,82 @@ extract_file (char *file_name, int typeflag) return status; } +/* Create a placeholder file with name FILE_NAME, which will be + replaced after other extraction is done by a symbolic link if + IS_SYMLINK is true, and by a hard link otherwise. Set + *INTERDIR_MADE if an intermediate directory is made in the + process. */ + +static int +create_placeholder_file (char *file_name, bool is_symlink, int *interdir_made) +{ + int fd; + struct stat st; + + while ((fd = open (file_name, O_WRONLY | O_CREAT | O_EXCL, 0)) < 0) + if (! maybe_recoverable (file_name, interdir_made)) + break; + + if (fd < 0) + open_error (file_name); + else if (fstat (fd, &st) != 0) + { + stat_error (file_name); + close (fd); + } + else if (close (fd) != 0) + close_error (file_name); + else + { + struct delayed_set_stat *h; + struct delayed_link *p = + xmalloc (offsetof (struct delayed_link, target) + + strlen (current_stat_info.link_name) + + 1); + p->next = delayed_link_head; + delayed_link_head = p; + p->dev = st.st_dev; + p->ino = st.st_ino; + p->mtime = get_stat_mtime (&st); + p->is_symlink = is_symlink; + if (is_symlink) + { + p->uid = current_stat_info.stat.st_uid; + p->gid = current_stat_info.stat.st_gid; + } + p->sources = xmalloc (offsetof (struct string_list, string) + + strlen (file_name) + 1); + p->sources->next = 0; + strcpy (p->sources->string, file_name); + strcpy (p->target, current_stat_info.link_name); + + h = delayed_set_stat_head; + if (h && ! h->after_links + && strncmp (file_name, h->file_name, h->file_name_len) == 0 + && ISSLASH (file_name[h->file_name_len]) + && (base_name (file_name) == file_name + h->file_name_len + 1)) + { + do + { + h->after_links = 1; + + if (stat (h->file_name, &st) != 0) + stat_error (h->file_name); + else + { + h->dev = st.st_dev; + h->ino = st.st_ino; + } + } + while ((h = h->next) && ! h->after_links); + } + + return 0; + } + + return -1; +} + static int extract_link (char *file_name, int typeflag) { @@ -783,6 +898,9 @@ extract_link (char *file_name, int typeflag) true, absolute_names_option); int interdir_made = 0; + if (! absolute_names_option && contains_dot_dot (link_name)) + return create_placeholder_file (file_name, false, &interdir_made); + do { struct stat st1, st2; @@ -792,12 +910,12 @@ extract_link (char *file_name, int typeflag) if (status == 0) { - struct delayed_symlink *ds = delayed_symlink_head; + struct delayed_link *ds = delayed_link_head; if (ds && lstat (link_name, &st1) == 0) for (; ds; ds = ds->next) if (ds->dev == st1.st_dev && ds->ino == st1.st_ino - && ds->mtime == st1.st_mtime) + && timespec_cmp (ds->mtime, get_stat_mtime (&st1)) == 0) { struct string_list *p = xmalloc (offsetof (struct string_list, string) + strlen (file_name) + 1); @@ -831,87 +949,22 @@ static int extract_symlink (char *file_name, int typeflag) { #ifdef HAVE_SYMLINK - int status, fd; + int status; int interdir_made = 0; - if (absolute_names_option - || ! (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name) - || contains_dot_dot (current_stat_info.link_name))) - { - while ((status = symlink (current_stat_info.link_name, file_name))) - if (!maybe_recoverable (file_name, &interdir_made)) - break; + if (! absolute_names_option + && (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name) + || contains_dot_dot (current_stat_info.link_name))) + return create_placeholder_file (file_name, true, &interdir_made); - if (status == 0) - set_stat (file_name, ¤t_stat_info.stat, 0, 0, 0, SYMTYPE); - else - symlink_error (current_stat_info.link_name, file_name); - } - else - { - /* This symbolic link is potentially dangerous. Don't - create it now; instead, create a placeholder file, which - will be replaced after other extraction is done. */ - struct stat st; - - while ((fd = open (file_name, O_WRONLY | O_CREAT | O_EXCL, 0)) < 0) - if (! maybe_recoverable (file_name, &interdir_made)) - break; - - status = -1; - if (fd < 0) - open_error (file_name); - else if (fstat (fd, &st) != 0) - { - stat_error (file_name); - close (fd); - } - else if (close (fd) != 0) - close_error (file_name); - else - { - struct delayed_set_stat *h; - struct delayed_symlink *p = xmalloc (offsetof (struct delayed_symlink, target) - + strlen (current_stat_info.link_name) - + 1); - p->next = delayed_symlink_head; - delayed_symlink_head = p; - p->dev = st.st_dev; - p->ino = st.st_ino; - p->mtime = st.st_mtime; - p->uid = current_stat_info.stat.st_uid; - p->gid = current_stat_info.stat.st_gid; - p->sources = xmalloc (offsetof (struct string_list, string) - + strlen (file_name) + 1); - p->sources->next = 0; - strcpy (p->sources->string, file_name); - strcpy (p->target, current_stat_info.link_name); - - h = delayed_set_stat_head; - if (h && ! h->after_symlinks - && strncmp (file_name, h->file_name, h->file_name_len) == 0 - && ISSLASH (file_name[h->file_name_len]) - && (base_name (file_name) == file_name + h->file_name_len + 1)) - { - do - { - 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; - } - } - while ((h = h->next) && ! h->after_symlinks); - } - - status = 0; - } - } + while ((status = symlink (current_stat_info.link_name, file_name))) + if (!maybe_recoverable (file_name, &interdir_made)) + break; + if (status == 0) + set_stat (file_name, ¤t_stat_info, NULL, 0, 0, SYMTYPE); + else + symlink_error (current_stat_info.link_name, file_name); return status; #else @@ -941,7 +994,8 @@ extract_node (char *file_name, int typeflag) if (status != 0) mknod_error (file_name); else - set_stat (file_name, ¤t_stat_info.stat, 0, 0, ARCHIVED_PERMSTATUS, typeflag); + set_stat (file_name, ¤t_stat_info, NULL, 0, + ARCHIVED_PERMSTATUS, typeflag); return status; } #endif @@ -958,7 +1012,7 @@ extract_fifo (char *file_name, int typeflag) break; if (status == 0) - set_stat (file_name, ¤t_stat_info.stat, NULL, 0, + set_stat (file_name, ¤t_stat_info, NULL, 0, ARCHIVED_PERMSTATUS, typeflag); else mkfifo_error (file_name); @@ -1048,6 +1102,8 @@ prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun) case DIRTYPE: case GNUTYPE_DUMPDIR: *fun = extract_dir; + if (current_stat_info.is_dumpdir) + delay_directory_restore_option = true; break; case GNUTYPE_VOLHDR: @@ -1139,7 +1195,8 @@ extract_archive (void) false, absolute_names_option); if (strip_name_components) { - size_t prefix_len = stripped_prefix_len (file_name, strip_name_components); + size_t prefix_len = stripped_prefix_len (file_name, + strip_name_components); if (prefix_len == (size_t) -1) { skip_member (); @@ -1148,8 +1205,12 @@ extract_archive (void) file_name += prefix_len; } - apply_nonancestor_delayed_set_stat (file_name, 0); - + /* Restore stats for all non-ancestor directories, unless + it is an incremental archive. + (see NOTICE in the comment to delay_set_stat above) */ + if (!delay_directory_restore_option) + apply_nonancestor_delayed_set_stat (file_name, 0); + /* Take a safety backup of a previously existing file. */ if (backup_option) @@ -1163,7 +1224,6 @@ extract_archive (void) } /* Extract the archive entry according to its type. */ - /* KLUDGE */ typeflag = sparse_member_p (¤t_stat_info) ? GNUTYPE_SPARSE : current_header->header.typeflag; @@ -1180,11 +1240,11 @@ extract_archive (void) /* Extract the symbolic links whose final extraction were delayed. */ static void -apply_delayed_symlinks (void) +apply_delayed_links (void) { - struct delayed_symlink *ds; + struct delayed_link *ds; - for (ds = delayed_symlink_head; ds; ) + for (ds = delayed_link_head; ds; ) { struct string_list *sources = ds->sources; char const *valid_source = 0; @@ -1195,12 +1255,12 @@ apply_delayed_symlinks (void) struct stat st; /* Make sure the placeholder file is still there. If not, - don't create a symlink, as the placeholder was probably + don't create a link, as the placeholder was probably removed by a later extraction. */ if (lstat (source, &st) == 0 && st.st_dev == ds->dev && st.st_ino == ds->ino - && st.st_mtime == ds->mtime) + && timespec_cmp (get_stat_mtime (&st), ds->mtime) == 0) { /* Unlink the placeholder, then create a hard link if possible, a symbolic link otherwise. */ @@ -1208,14 +1268,20 @@ apply_delayed_symlinks (void) unlink_error (source); else if (valid_source && link (valid_source, source) == 0) ; + else if (!ds->is_symlink) + { + if (link (ds->target, source) != 0) + link_error (ds->target, source); + } else if (symlink (ds->target, source) != 0) symlink_error (ds->target, source); else { + struct tar_stat_info st1; + st1.stat.st_uid = ds->uid; + st1.stat.st_gid = ds->gid; + set_stat (source, &st1, NULL, 0, 0, SYMTYPE); valid_source = source; - st.st_uid = ds->uid; - st.st_gid = ds->gid; - set_stat (source, &st, 0, 0, 0, SYMTYPE); } } } @@ -1228,13 +1294,13 @@ apply_delayed_symlinks (void) } { - struct delayed_symlink *next = ds->next; + struct delayed_link *next = ds->next; free (ds); ds = next; } } - delayed_symlink_head = 0; + delayed_link_head = 0; } /* Finish the extraction of an archive. */ @@ -1244,12 +1310,12 @@ extract_finish (void) /* 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 + /* Then, apply delayed links, so that they don't affect delayed directory status-setting for ordinary directories. */ - apply_delayed_symlinks (); + apply_delayed_links (); /* Finally, fix the status of directories that are ancestors - of delayed symlinks. */ + of delayed links. */ apply_nonancestor_delayed_set_stat ("", 1); }