X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fextract.c;h=0f50d918c4e081937bf9454c8b969e5bfc47262f;hb=48ff44dfc71404b4ddedd28eadb5b38c3d83bb32;hp=35331249599d5a2f52cf63e9065574dec9bef013;hpb=6e9d1539b665c8f3c173b36702ca1650cac977ad;p=chaz%2Ftar diff --git a/src/extract.c b/src/extract.c index 3533124..0f50d91 100644 --- a/src/extract.c +++ b/src/extract.c @@ -1,5 +1,5 @@ /* Extract files from a tar archive. - Copyright 1988, 92,93,94,96,97,98, 1999 Free Software Foundation, Inc. + Copyright 1988,92,93,94,96,97,98,99,2000,2001 Free Software Foundation, Inc. Written by John Gilmore, on 1985-11-19. This program is free software; you can redistribute it and/or modify it @@ -31,7 +31,7 @@ struct utimbuf #include "common.h" -static int we_are_root; /* true if our effective uid == 0 */ +int we_are_root; /* true if our effective uid == 0 */ static mode_t newdir_umask; /* umask when creating new directories */ static mode_t current_umask; /* current umask (which is set to 0 if -p) */ @@ -65,16 +65,37 @@ struct delayed_set_stat static struct delayed_set_stat *delayed_set_stat_head; -/*--------------------------. -| Set up to extract files. | -`--------------------------*/ +/* List of symbolic links whose creation we have delayed. */ +struct delayed_symlink + { + /* The next delayed symbolic link in the list. */ + struct delayed_symlink *next; + + /* The device, inode number and last-modified time of the + placeholder symbolic link. */ + dev_t dev; + ino_t ino; + time_t mtime; + + /* The desired owner and group of the symbolic link. */ + uid_t uid; + gid_t gid; + + /* The location and desired target of the desired link, as two + adjacent character strings, both null-terminated. */ + char names[1]; + }; +static struct delayed_symlink *delayed_symlink_head; + +/* Set up to extract files. */ void extr_init (void) { we_are_root = geteuid () == 0; same_permissions_option += we_are_root; same_owner_option += we_are_root; + xalloc_fail_func = extract_finish; /* Option -p clears the kernel umask, so it does not affect proper restoration of file permissions. New intermediate directories will @@ -96,7 +117,7 @@ extr_init (void) PERMSTATUS specifies the status of the file's permissions. TYPEFLAG specifies the type of the file. */ static void -set_mode (char *file_name, struct stat *stat_info, +set_mode (char const *file_name, struct stat const *stat_info, mode_t invert_permissions, enum permstatus permstatus, char typeflag) { @@ -137,11 +158,17 @@ set_mode (char *file_name, struct stat *stat_info, } if (chmod (file_name, mode) != 0) - { - int e = errno; - ERROR ((0, e, _("%s: Cannot change mode to %04lo"), - quotearg_colon (file_name), (unsigned long) mode)); - } + chmod_error_details (file_name, mode); +} + +/* Check time after successfully setting FILE_NAME's time stamp to T. */ +static void +check_time (char const *file_name, time_t t) +{ + time_t now; + if (start_time < t && (now = time (0)) < t) + WARN ((0, 0, _("%s: time stamp %s is %lu s in the future"), + file_name, tartime (t), (unsigned long) (t - now))); } /* Restore stat attributes (owner, group, mode and times) for @@ -157,7 +184,7 @@ set_mode (char *file_name, struct stat *stat_info, punt for the rest. Sigh! */ static void -set_stat (char *file_name, struct stat *stat_info, +set_stat (char const *file_name, struct stat const *stat_info, mode_t invert_permissions, enum permstatus permstatus, char typeflag) { @@ -184,11 +211,11 @@ set_stat (char *file_name, struct stat *stat_info, utimbuf.modtime = stat_info->st_mtime; if (utime (file_name, &utimbuf) < 0) + utime_error (file_name); + else { - int e = errno; - ERROR ((0, e, - _("%s: Cannot change access and modification times"), - quotearg_colon (file_name))); + check_time (file_name, stat_info->st_atime); + check_time (file_name, stat_info->st_mtime); } } @@ -211,25 +238,15 @@ set_stat (char *file_name, struct stat *stat_info, { #if HAVE_LCHOWN if (lchown (file_name, stat_info->st_uid, stat_info->st_gid) < 0) - { - int e = errno; - ERROR ((0, e, _("%s: Cannot lchown to uid %lu gid %lu"), - quotearg_colon (file_name), - (unsigned long) stat_info->st_uid, - (unsigned long) stat_info->st_gid)); - } + chown_error_details (file_name, + stat_info->st_uid, stat_info->st_gid); #endif } else { if (chown (file_name, stat_info->st_uid, stat_info->st_gid) < 0) - { - int e = errno; - ERROR ((0, e, _("%s: Cannot chown to uid %lu gid %lu"), - quotearg_colon (file_name), - (unsigned long) stat_info->st_uid, - (unsigned long) stat_info->st_gid)); - } + chown_error_details (file_name, + stat_info->st_uid, stat_info->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. @@ -263,9 +280,10 @@ delay_set_stat (char const *file_name, struct stat const *stat_info, } /* Update the delayed_set_stat info for an intermediate directory - created on the path to DIR_NAME. The intermediate directory - turned out to be the same as this directory, due to ".." or - symbolic links. *DIR_STAT_INFO is the status of the directory. */ + created on the path to DIR_NAME. The intermediate directory turned + out to be the same as this directory, e.g. due trailing slash or + ".." or symbolic links. *DIR_STAT_INFO is the status of the + directory. */ static void repair_delayed_set_stat (char const *dir_name, struct stat const *dir_stat_info) @@ -291,41 +309,41 @@ repair_delayed_set_stat (char const *dir_name, } } - ERROR ((0, 0, _("Unexpected inconsistency when making directory %s"), - quote (dir_name))); + ERROR ((0, 0, _("%s: Unexpected inconsistency when making directory"), + quotearg_colon (dir_name))); } -/*-----------------------------------------------------------------------. -| After a file/link/symlink/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. | -`-----------------------------------------------------------------------*/ - +/* After a file/link/symlink/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. */ static int make_directories (char *file_name) { + char *cursor0 = file_name + FILESYSTEM_PREFIX_LEN (file_name); char *cursor; /* points into path */ int did_something = 0; /* did we do anything yet? */ - int saved_errno = errno; /* remember caller's errno */ int mode; int invert_permissions; int status; - for (cursor = strchr (file_name, '/'); - cursor; - cursor = strchr (cursor + 1, '/')) + + for (cursor = cursor0; *cursor; cursor++) { + if (! ISSLASH (*cursor)) + continue; + /* Avoid mkdir of empty string, if leading or double '/'. */ - if (cursor == file_name || cursor[-1] == '/') + if (cursor == cursor0 || ISSLASH (cursor[-1])) continue; /* Avoid mkdir where last part of path is "." or "..". */ if (cursor[-1] == '.' - && (cursor == file_name + 1 || cursor[-2] == '/' + && (cursor == cursor0 + 1 || ISSLASH (cursor[-2]) || (cursor[-2] == '.' - && (cursor == file_name + 2 || cursor[-3] == '/')))) + && (cursor == cursor0 + 2 || ISSLASH (cursor[-3]))))) continue; *cursor = '\0'; /* truncate the path there */ @@ -364,7 +382,6 @@ make_directories (char *file_name) break; } - errno = saved_errno; return did_something; /* tell them to retry if we made one */ } @@ -379,7 +396,7 @@ prepare_to_extract (char const *file_name) if (old_files_option == UNLINK_FIRST_OLD_FILES && !remove_any_file (file_name, recursive_unlink_option) - && errno != ENOENT) + && errno && errno != ENOENT) { unlink_error (file_name); return 0; @@ -388,13 +405,10 @@ prepare_to_extract (char const *file_name) return 1; } -/*--------------------------------------------------------------------. -| Attempt repairing what went wrong with the extraction. Delete an | -| already existing file or create missing intermediate directories. | -| Return nonzero if we somewhat increased our chances at a successful | -| extraction. errno is properly restored on zero return. | -`--------------------------------------------------------------------*/ - +/* Attempt repairing what went wrong with the extraction. Delete an + already existing file or create missing intermediate directories. + Return nonzero if we somewhat increased our chances at a successful + extraction. errno is properly restored on zero return. */ static int maybe_recoverable (char *file_name, int *interdir_made) { @@ -423,7 +437,10 @@ maybe_recoverable (char *file_name, int *interdir_made) case ENOENT: /* Attempt creating missing intermediate directories. */ if (! make_directories (file_name)) - return 0; + { + errno = ENOENT; + return 0; + } *interdir_made = 1; return 1; @@ -434,99 +451,101 @@ maybe_recoverable (char *file_name, int *interdir_made) } } -/*---. -| ? | -`---*/ - static void extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name) { int sparse_ind = 0; - size_t written; - ssize_t count; /* assuming sizeleft is initially totalsize */ while (*sizeleft > 0) { + size_t written; + size_t count; union block *data_block = find_next_block (); if (! data_block) { - ERROR ((0, 0, _("Unexpected EOF on archive file"))); + ERROR ((0, 0, _("Unexpected EOF in archive"))); return; } if (lseek (fd, sparsearray[sparse_ind].offset, SEEK_SET) < 0) { - char buf[UINTMAX_STRSIZE_BOUND]; - int e = errno; - ERROR ((0, e, _("%s: lseek error at byte %s"), - quotearg_colon (name), - STRINGIFY_BIGINT (sparsearray[sparse_ind].offset, buf))); + seek_error_details (name, sparsearray[sparse_ind].offset); return; } written = sparsearray[sparse_ind++].numbytes; while (written > BLOCKSIZE) { count = full_write (fd, data_block->buffer, BLOCKSIZE); - if (count < 0) - write_error (name); written -= count; *sizeleft -= count; + if (count != BLOCKSIZE) + { + write_error_details (name, count, BLOCKSIZE); + return; + } set_next_block_after (data_block); data_block = find_next_block (); if (! data_block) { - ERROR ((0, 0, _("Unexpected EOF on archive file"))); + ERROR ((0, 0, _("Unexpected EOF in archive"))); return; } } count = full_write (fd, data_block->buffer, written); + *sizeleft -= count; - if (count < 0) - write_error (name); - else if (count != written) + if (count != written) { - char buf1[UINTMAX_STRSIZE_BOUND]; - char buf2[UINTMAX_STRSIZE_BOUND]; - ERROR ((0, 0, _("%s: Could only write %s of %s bytes"), - quotearg_colon (name), - STRINGIFY_BIGINT (totalsize - *sizeleft, buf1), - STRINGIFY_BIGINT (totalsize, buf2))); - skip_file (*sizeleft); + write_error_details (name, count, written); + return; } - written -= count; - *sizeleft -= count; set_next_block_after (data_block); } - - free (sparsearray); } -/*----------------------------------. -| Extract a file from the archive. | -`----------------------------------*/ +/* Fix the statuses of all directories whose statuses need fixing, and + which are not ancestors of FILE_NAME. */ +static void +apply_nonancestor_delayed_set_stat (char const *file_name) +{ + size_t file_name_len = strlen (file_name); + + 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) + break; + delayed_set_stat_head = data->next; + set_stat (data->file_name, &data->stat_info, + data->invert_permissions, data->permstatus, DIRTYPE); + free (data); + } +} +/* Extract a file from the archive. */ void extract_archive (void) { union block *data_block; int fd; int status; - ssize_t sstatus; + size_t count; size_t name_length; size_t written; int openflag; mode_t mode; off_t size; - int skipcrud; + size_t skipcrud; int counter; int interdir_made = 0; char typeflag; -#if 0 - int sparse_ind = 0; -#endif union block *exhdr; #define CURRENT_FILE_NAME (skipcrud + current_file_name) @@ -536,13 +555,11 @@ extract_archive (void) if (interactive_option && !confirm ("extract", current_file_name)) { - if (current_header->oldgnu_header.isextended) - skip_extended_headers (); - skip_file (current_stat.st_size); + skip_member (); return; } - /* Print the block from `current_header' and `current_stat'. */ + /* Print the block from current_header and current_stat. */ if (verbose_option) print_header (); @@ -552,29 +569,33 @@ extract_archive (void) skipcrud = 0; if (! absolute_names_option) { - while (CURRENT_FILE_NAME[0] == '/') + if (contains_dot_dot (CURRENT_FILE_NAME)) + { + ERROR ((0, 0, _("%s: Member name contains `..'"), + quotearg_colon (CURRENT_FILE_NAME))); + skip_member (); + return; + } + + skipcrud = FILESYSTEM_PREFIX_LEN (current_file_name); + while (ISSLASH (CURRENT_FILE_NAME[0])) + skipcrud++; + + if (skipcrud) { static int warned_once; if (!warned_once) { warned_once = 1; - WARN ((0, 0, _("Removing leading `/' from member names"))); + WARN ((0, 0, _("Removing leading `%.*s' from member names"), + (int) skipcrud, current_file_name)); } - skipcrud++; /* force relative path */ - } - - if (contains_dot_dot (CURRENT_FILE_NAME)) - { - ERROR ((0, 0, _("%s: Member name contains `..'"), - quotearg_colon (CURRENT_FILE_NAME))); - if (current_header->oldgnu_header.isextended) - skip_extended_headers (); - skip_file (current_stat.st_size); - return; } } + apply_nonancestor_delayed_set_stat (CURRENT_FILE_NAME); + /* Take a safety backup of a previously existing file. */ if (backup_option && !to_stdout_option) @@ -583,9 +604,7 @@ extract_archive (void) int e = errno; ERROR ((0, e, _("%s: Was unable to backup this file"), quotearg_colon (CURRENT_FILE_NAME))); - if (current_header->oldgnu_header.isextended) - skip_extended_headers (); - skip_file (current_stat.st_size); + skip_member (); return; } @@ -635,7 +654,7 @@ extract_archive (void) exhdr = find_next_block (); if (! exhdr) { - ERROR ((0, 0, _("Unexpected EOF on archive file"))); + ERROR ((0, 0, _("Unexpected EOF in archive"))); return; } for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++) @@ -678,7 +697,8 @@ extract_archive (void) suffix means a directory. */ name_length = strlen (CURRENT_FILE_NAME); - if (name_length && CURRENT_FILE_NAME[name_length - 1] == '/') + if (FILESYSTEM_PREFIX_LEN (CURRENT_FILE_NAME) < name_length + && CURRENT_FILE_NAME[name_length - 1] == '/') goto really_dir; /* FIXME: deal with protection issues. */ @@ -698,9 +718,7 @@ extract_archive (void) if (! prepare_to_extract (CURRENT_FILE_NAME)) { - if (current_header->oldgnu_header.isextended) - skip_extended_headers (); - skip_file (current_stat.st_size); + skip_member (); if (backup_option) undo_last_backup (); break; @@ -737,9 +755,7 @@ extract_archive (void) goto again_file; open_error (CURRENT_FILE_NAME); - if (current_header->oldgnu_header.isextended) - skip_extended_headers (); - skip_file (current_stat.st_size); + skip_member (); if (backup_option) undo_last_backup (); break; @@ -762,11 +778,10 @@ extract_archive (void) memcpy (name, CURRENT_FILE_NAME, name_length_bis); size = current_stat.st_size; extract_sparse_file (fd, &size, current_stat.st_size, name); + free (sparsearray); } else - for (size = current_stat.st_size; - size > 0; - size -= written) + for (size = current_stat.st_size; size > 0; ) { if (multi_volume_option) { @@ -782,7 +797,7 @@ extract_archive (void) data_block = find_next_block (); if (! data_block) { - ERROR ((0, 0, _("Unexpected EOF on archive file"))); + ERROR ((0, 0, _("Unexpected EOF in archive"))); break; /* FIXME: What happens, then? */ } @@ -791,27 +806,20 @@ extract_archive (void) if (written > size) written = size; errno = 0; - sstatus = full_write (fd, data_block->buffer, written); + count = full_write (fd, data_block->buffer, written); + size -= count; set_next_block_after ((union block *) (data_block->buffer + written - 1)); - if (sstatus == written) - continue; - - /* Error in writing to file. Print it, skip to next file in - archive. */ - - if (sstatus < 0) - write_error (CURRENT_FILE_NAME); - else - ERROR ((0, 0, _("%s: Could only write %lu of %lu bytes"), - quotearg_colon (CURRENT_FILE_NAME), - (unsigned long) sstatus, - (unsigned long) written)); - skip_file (size - written); - break; /* still do the close, mod time, chmod, etc */ + if (count != written) + { + write_error_details (CURRENT_FILE_NAME, count, written); + break; + } } + skip_file (size); + if (multi_volume_option) assign_string (&save_name, 0); @@ -841,30 +849,64 @@ extract_archive (void) if (! prepare_to_extract (CURRENT_FILE_NAME)) break; - while (status = symlink (current_link_name, CURRENT_FILE_NAME), - status != 0) - if (!maybe_recoverable (CURRENT_FILE_NAME, &interdir_made)) - break; - - if (status == 0) - - /* Setting the attributes of symbolic links might, on some systems, - change the pointed to file, instead of the symbolic link itself. - At least some of these systems have a lchown call, and the - set_stat routine knows about this. */ - - set_stat (CURRENT_FILE_NAME, ¤t_stat, 0, - ARCHIVED_PERMSTATUS, typeflag); + if (absolute_names_option + || (ISSLASH (current_link_name + [FILESYSTEM_PREFIX_LEN (current_link_name)]) + && ! contains_dot_dot (current_link_name))) + { + while (status = symlink (current_link_name, CURRENT_FILE_NAME), + status != 0) + if (!maybe_recoverable (CURRENT_FILE_NAME, &interdir_made)) + break; + if (status == 0) + set_stat (CURRENT_FILE_NAME, ¤t_stat, 0, 0, SYMTYPE); + else + symlink_error (current_link_name, CURRENT_FILE_NAME); + } else { - int e = errno; - ERROR ((0, e, _("%s: Cannot create symlink to %s"), - quotearg_colon (CURRENT_FILE_NAME), - quote (current_link_name))); - if (backup_option) - undo_last_backup (); + /* 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 (CURRENT_FILE_NAME, O_WRONLY | O_CREAT | O_EXCL, 0), + fd < 0) + if (! maybe_recoverable (CURRENT_FILE_NAME, &interdir_made)) + break; + + status = -1; + if (fd < 0) + open_error (CURRENT_FILE_NAME); + else if (fstat (fd, &st) != 0) + { + stat_error (CURRENT_FILE_NAME); + close (fd); + } + else if (close (fd) != 0) + close_error (CURRENT_FILE_NAME); + else + { + size_t filelen = strlen (CURRENT_FILE_NAME); + size_t linklen = strlen (current_link_name); + struct delayed_symlink *p = + xmalloc (sizeof *p + filelen + linklen + 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.st_uid; + p->gid = current_stat.st_gid; + memcpy (p->names, CURRENT_FILE_NAME, filelen + 1); + memcpy (p->names + filelen + 1, current_link_name, linklen + 1); + status = 0; + } } + + if (status != 0 && backup_option) + undo_last_backup (); break; #else @@ -976,19 +1018,6 @@ extract_archive (void) name_length = strlen (CURRENT_FILE_NAME); really_dir: - /* Remove trailing "/" and "/.", unless that would result in the - empty string. */ - for (;;) - { - if (1 < name_length && CURRENT_FILE_NAME[name_length - 1] == '/') - CURRENT_FILE_NAME[--name_length] = '\0'; - else if (2 < name_length - && CURRENT_FILE_NAME[name_length - 1] == '.' - && CURRENT_FILE_NAME[name_length - 2] == '/') - CURRENT_FILE_NAME[name_length -= 2] = '\0'; - else - break; - } if (incremental_option) { @@ -998,7 +1027,7 @@ extract_archive (void) gnu_restore (skipcrud); } else if (typeflag == GNUTYPE_DUMPDIR) - skip_file (current_stat.st_size); + skip_member (); if (! prepare_to_extract (CURRENT_FILE_NAME)) break; @@ -1008,29 +1037,38 @@ extract_archive (void) & MODE_RWX); again_dir: - status = mkdir (CURRENT_FILE_NAME, mode); + { + /* Do not pass redundant trailing "/" to mkdir, as POSIX does + not allow mkdir to ignore it. */ + size_t len = name_length; + char ch = '\0'; + while (FILESYSTEM_PREFIX_LEN (CURRENT_FILE_NAME) < len + && CURRENT_FILE_NAME[len - 1] == '/') + len--, ch = '/'; + CURRENT_FILE_NAME[len] = '\0'; + status = mkdir (CURRENT_FILE_NAME, mode); + CURRENT_FILE_NAME[len] = ch; + } + if (status != 0) { - if (errno == EEXIST && interdir_made - && contains_dot_dot (CURRENT_FILE_NAME)) + if (errno == EEXIST && interdir_made) { - int e = errno; struct stat st; if (stat (CURRENT_FILE_NAME, &st) == 0) { repair_delayed_set_stat (CURRENT_FILE_NAME, &st); break; } - e = errno; + errno = EEXIST; } if (maybe_recoverable (CURRENT_FILE_NAME, &interdir_made)) goto again_dir; - if (errno != EEXIST || old_files_option == KEEP_OLD_FILES) + if (errno != EEXIST) { - int e = errno; - ERROR ((0, e, "%s: mkdir", quotearg_colon (CURRENT_FILE_NAME))); + mkdir_error (CURRENT_FILE_NAME); if (backup_option) undo_last_backup (); break; @@ -1057,9 +1095,9 @@ extract_archive (void) case GNUTYPE_MULTIVOL: ERROR ((0, 0, - _("Cannot extract %s -- file is continued from another volume"), - quote (current_file_name))); - skip_file (current_stat.st_size); + _("%s: Cannot extract -- file is continued from another volume"), + quotearg_colon (current_file_name))); + skip_member (); if (backup_option) undo_last_backup (); break; @@ -1067,45 +1105,78 @@ extract_archive (void) case GNUTYPE_LONGNAME: case GNUTYPE_LONGLINK: ERROR ((0, 0, _("Visible long name error"))); - skip_file (current_stat.st_size); + skip_member (); if (backup_option) undo_last_backup (); break; default: WARN ((0, 0, - _("Unknown file type '%c' for %s, extracted as normal file"), - typeflag, quote (CURRENT_FILE_NAME))); + _("%s: Unknown file type '%c', extracted as normal file"), + quotearg_colon (CURRENT_FILE_NAME), typeflag)); goto again_file; } #undef CURRENT_FILE_NAME } -/* Fix the statuses of all directories that are not ancestors of FILE_NAME. */ -void -apply_delayed_set_stat (char const *file_name) +/* Extract the symbolic links whose final extraction were delayed. */ +static void +apply_delayed_symlinks (void) { - size_t file_name_len = strlen (file_name); + struct delayed_symlink *p; + struct delayed_symlink *next; - while (delayed_set_stat_head) + for (p = delayed_symlink_head; p; p = next) { - struct delayed_set_stat *data = delayed_set_stat_head; - delayed_set_stat_head = data->next; - if (data->file_name_len < file_name_len - && file_name[data->file_name_len] == '/' - && memcmp (file_name, data->file_name, data->file_name_len) == 0) - break; - set_stat (data->file_name, &data->stat_info, - data->invert_permissions, data->permstatus, DIRTYPE); - free (data); + char const *file = p->names; + struct stat st; + + /* Before doing anything, make sure the placeholder file is still + there. If the placeholder isn't there, don't worry about it, as + it may have been removed by a later extraction. */ + if (lstat (file, &st) == 0 + && st.st_dev == p->dev + && st.st_ino == p->ino + && st.st_mtime == p->mtime) + { + if (unlink (file) != 0) + unlink_error (file); + else + { + char const *contents = file + strlen (file) + 1; + if (symlink (contents, file) != 0) + symlink_error (contents, file); + else + { + st.st_uid = p->uid; + st.st_gid = p->gid; + set_stat (file, &st, 0, 0, SYMTYPE); + } + } + } + + next = p->next; + free (p); } + + delayed_symlink_head = 0; +} + +/* Finish the extraction of an archive. */ +void +extract_finish (void) +{ + /* Apply delayed symlinks last, so that they don't affect + delayed directory status-setting. */ + apply_nonancestor_delayed_set_stat (""); + apply_delayed_symlinks (); } void fatal_exit (void) { - apply_delayed_set_stat (""); + extract_finish (); error (TAREXIT_FAILURE, 0, _("Error is not recoverable: exiting now")); abort (); }