X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fextract.c;h=0728a99f8a8d16453c0507ce5bb9d8bab4ff194c;hb=1521a94b11b0b7a2fe2a8318207d7d6b4bdbf76b;hp=ff86642673b3a3575e5057e85875b0081287fbad;hpb=5b394b2c7eb52752e8ccb0ba36f580c46c6c8baa;p=chaz%2Ftar diff --git a/src/extract.c b/src/extract.c index ff86642..0728a99 100644 --- a/src/extract.c +++ b/src/extract.c @@ -1,821 +1,1027 @@ /* Extract files from a tar archive. - Copyright (C) 1988, 1992 Free Software Foundation - -This file is part of GNU Tar. - -GNU Tar is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2, or (at your option) -any later version. - -GNU Tar is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU Tar; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ - -/* - * Extract files from a tar archive. - * - * Written 19 Nov 1985 by John Gilmore, ihnp4!hoptoad!gnu. - */ - -#include -#include -#ifndef STDC_HEADERS -extern int errno; -#endif -#include + Copyright (C) 1988, 92,93,94,96,97,98, 1999 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 + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any later + version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "system.h" + #include -time_t time(); +time_t time (); -#ifdef BSD42 -#include +#if HAVE_UTIME_H +# include #else -#ifndef V7 -#include -#endif +struct utimbuf + { + long actime; + long modtime; + }; #endif -#ifdef NO_OPEN3 -/* We need the #define's even though we don't use them. */ -#include "open3.h" -#endif +#include "common.h" -#ifdef EMUL_OPEN3 -/* Simulated 3-argument open for systems that don't have it */ -#include "open3.h" -#endif +static time_t now; /* current time */ +static 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) */ -#include "tar.h" -#include "port.h" +#if 0 +/* "Scratch" space to store the information about a sparse file before + writing the info into the header or extended header. */ +struct sp_array *sparsearray; -#if defined(_POSIX_VERSION) -#include +/* Number of elts storable in the sparsearray. */ +int sp_array_size = 10; #endif -extern FILE *msg_file; +struct delayed_set_stat + { + struct delayed_set_stat *next; + char *file_name; + struct stat stat_info; + }; -extern union record *head; /* Points to current tape header */ -extern struct stat hstat; /* Stat struct corresponding */ -extern int head_standard; /* Tape header is in ANSI format */ +static struct delayed_set_stat *delayed_set_stat_head; -extern char *save_name; -extern long save_totsize; -extern long save_sizeleft; +/*--------------------------. +| Set up to extract files. | +`--------------------------*/ -int confirm(); -void decode_header(); -void extract_mangle(); -void extract_sparse_file(); -long from_oct(); -void gnu_restore(); -extern void print_header(); -extern void skip_file(); -extern void skip_extended_headers(); -extern void pr_mkdir(); -void saverec(); +void +extr_init (void) +{ + now = time ((time_t *) 0); + we_are_root = geteuid () == 0; -int make_dirs(); /* Makes required directories */ + /* 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. */ -static time_t now = 0; /* Current time */ -static we_are_root = 0; /* True if our effective uid == 0 */ -static int notumask = ~0; /* Masks out bits user doesn't want */ + newdir_umask = umask (0); + if (same_permissions_option) + current_umask = 0; + else + { + umask (newdir_umask); /* restore the kernel umask */ + current_umask = newdir_umask; + } -/* - * "Scratch" space to store the information about a sparse file before - * writing the info into the header or extended header - */ -/*struct sp_array *sparsearray;*/ + /* FIXME: Just make sure we can add files in directories we create. Maybe + should we later remove permissions we are adding, here? */ + newdir_umask &= ~0300; +} -/* number of elts storable in the sparsearray */ -/*int sp_array_size = 10;*/ +/*------------------------------------------------------------------. +| Restore mode for FILE_NAME, from information given in STAT_INFO. | +`------------------------------------------------------------------*/ -struct saved_dir_info +static void +set_mode (char *file_name, struct stat *stat_info) { - char *path; - int mode; - int atime; - int mtime; - saved_dir_info *next; -}; - -struct saved_dir_info *saved_dir_info_head; - -/* - * Set up to extract files. - */ -void -extr_init() + /* We ought to force permission when -k is not selected, because if the + file already existed, open or creat would save the permission bits from + the previously created file, ignoring the ones we specified. + + But with -k selected, we know *we* created this file, so the mode + bits were set by our open. If the file has abnormal mode bits, we must + chmod since writing or chown has probably reset them. If the file is + normal, we merely skip the chmod. This works because we did umask (0) + when -p, so umask will have left the specified mode alone. */ + + if (!keep_old_files_option + || (stat_info->st_mode & (S_ISUID | S_ISGID | S_ISVTX))) + if (chmod (file_name, ~current_umask & stat_info->st_mode) < 0) + ERROR ((0, errno, _("%s: Cannot change mode to %0.4lo"), + file_name, + (unsigned long) (~current_umask & stat_info->st_mode))); +} + +/*----------------------------------------------------------------------. +| Restore stat attributes (owner, group, mode and times) for FILE_NAME, | +| using information given in STAT_INFO. SYMLINK_FLAG is non-zero for a | +| freshly restored symbolic link. | +`----------------------------------------------------------------------*/ + +/* FIXME: About proper restoration of symbolic link attributes, we still do + not have it right. Pretesters' reports tell us we need further study and + probably more configuration. For now, just use lchown if it exists, and + punt for the rest. Sigh! */ + +static void +set_stat (char *file_name, struct stat *stat_info, int symlink_flag) { - int ourmask; - - now = time((time_t *)0); - if (geteuid() == 0) - we_are_root = 1; - - /* - * We need to know our umask. But if f_use_protection is set, - * leave our kernel umask at 0, and our "notumask" at ~0. - */ - ourmask = umask(0); /* Read it */ - if (!f_use_protection) { - (void) umask (ourmask); /* Set it back how it was */ - notumask = ~ourmask; /* Make umask override permissions */ + struct utimbuf utimbuf; + + if (!symlink_flag) + { + /* We do the utime before the chmod because some versions of utime are + broken and trash the modes of the file. */ + + if (!touch_option) + { + /* We set the accessed time to `now', which is really the time we + started extracting files, unless incremental_option is used, in + which case .st_atime is used. */ + + /* FIXME: incremental_option should set ctime too, but how? */ + + if (incremental_option) + utimbuf.actime = stat_info->st_atime; + else + utimbuf.actime = now; + + utimbuf.modtime = stat_info->st_mtime; + + if (utime (file_name, &utimbuf) < 0) + ERROR ((0, errno, + _("%s: Could not change access and modification times"), + file_name)); + } + + /* Some systems allow non-root users to give files away. Once this + 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); + } + + /* If we are root, set the owner and group of the extracted file, so we + extract as the original owner. Or else, if we are running as a user, + leave the owner and group as they are, so we extract as that user. */ + + if (we_are_root || same_owner_option) + { +#if HAVE_LCHOWN + + /* When lchown exists, it should be used to change the attributes of + the symbolic link itself. In this case, a mere chown would change + the attributes of the file the symbolic link is pointing to, and + should be avoided. */ + + if (symlink_flag) + { + if (lchown (file_name, stat_info->st_uid, stat_info->st_gid) < 0) + ERROR ((0, errno, _("%s: Cannot lchown to uid %lu gid %lu"), + file_name, + (unsigned long) stat_info->st_uid, + (unsigned long) stat_info->st_gid)); + } + else + { + if (chown (file_name, stat_info->st_uid, stat_info->st_gid) < 0) + ERROR ((0, errno, _("%s: Cannot chown to uid %lu gid %lu"), + file_name, + (unsigned long) stat_info->st_uid, + (unsigned long) stat_info->st_gid)); } + +#else /* not HAVE_LCHOWN */ + + if (!symlink_flag) + + if (chown (file_name, stat_info->st_uid, stat_info->st_gid) < 0) + ERROR ((0, errno, _("%s: Cannot chown to uid %lu gid %lu"), + file_name, + (unsigned long) stat_info->st_uid, + (unsigned long) stat_info->st_gid)); + +#endif/* not HAVE_LCHOWN */ + + if (!symlink_flag) + + /* 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); + } } +/*-----------------------------------------------------------------------. +| 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 *cursor; /* points into path */ + int did_something = 0; /* did we do anything yet? */ + int saved_errno = errno; /* remember caller's errno */ + int status; + + for (cursor = strchr (file_name, '/'); + cursor != NULL; + cursor = strchr (cursor + 1, '/')) + { + /* Avoid mkdir of empty string, if leading or double '/'. */ + + if (cursor == file_name || cursor[-1] == '/') + continue; + + /* Avoid mkdir where last part of path is '.'. */ + + if (cursor[-1] == '.' && (cursor == file_name + 1 || cursor[-2] == '/')) + continue; + + *cursor = '\0'; /* truncate the path there */ + status = mkdir (file_name, ~newdir_umask & 0777); + + if (status == 0) + { + /* Fix ownership. */ + + if (we_are_root) + if (chown (file_name, current_stat.st_uid, current_stat.st_gid) < 0) + ERROR ((0, errno, + _("%s: Cannot change owner to uid %lu, gid %lu"), + file_name, + (unsigned long) current_stat.st_uid, + (unsigned long) current_stat.st_gid)); + + print_for_mkdir (file_name, cursor - file_name, + ~newdir_umask & 0777); + did_something = 1; + + *cursor = '/'; + continue; + } + + *cursor = '/'; + + if (errno == EEXIST +#if MSDOS + /* Turbo C mkdir gives a funny errno. */ + || errno == EACCES +#endif + ) + /* Directory already exists. */ + continue; + + /* Some other error in the mkdir. We return to the caller. */ + break; + } + + errno = saved_errno; /* FIXME: errno should be read-only */ + return did_something; /* tell them to retry if we made one */ +} + +/*--------------------------------------------------------------------. +| 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) +{ + switch (errno) + { + case EEXIST: + /* Attempt deleting an existing file. However, with -k, just stay + quiet. */ + + if (keep_old_files_option) + return 0; + + return remove_any_file (file_name, 0); + + case ENOENT: + /* Attempt creating missing intermediate directories. */ + + return make_directories (file_name); + + default: + /* Just say we can't do anything about it... */ + + return 0; + } +} + +/*---. +| ? | +`---*/ + +static void +extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name) +{ + union block *data_block; + int sparse_ind = 0; + size_t written; + ssize_t count; + + /* FIXME: `data_block' might be used uninitialized in this function. + Reported by Bruno Haible. */ + + /* assuming sizeleft is initially totalsize */ + + while (*sizeleft > 0) + { + data_block = find_next_block (); + if (data_block == NULL) + { + ERROR ((0, 0, _("Unexpected EOF on archive file"))); + return; + } + lseek (fd, sparsearray[sparse_ind].offset, 0); + written = sparsearray[sparse_ind++].numbytes; + while (written > BLOCKSIZE) + { + count = full_write (fd, data_block->buffer, BLOCKSIZE); + if (count < 0) + ERROR ((0, errno, _("%s: Could not write to file"), name)); + written -= count; + *sizeleft -= count; + set_next_block_after (data_block); + data_block = find_next_block (); + } + + count = full_write (fd, data_block->buffer, written); + + if (count < 0) + ERROR ((0, errno, _("%s: Could not write to file"), name)); + else if (count != written) + { + char buf1[UINTMAX_STRSIZE_BOUND]; + char buf2[UINTMAX_STRSIZE_BOUND]; + ERROR ((0, 0, _("%s: Could only write %s of %s bytes"), + name, + STRINGIFY_BIGINT (totalsize - *sizeleft, buf1), + STRINGIFY_BIGINT (totalsize, buf2))); + skip_file (*sizeleft); + } + + written -= count; + *sizeleft -= count; + set_next_block_after (data_block); + } + free (sparsearray); + set_next_block_after (data_block); +} + +/*----------------------------------. +| Extract a file from the archive. | +`----------------------------------*/ -/* - * Extract a file from the archive. - */ void -extract_archive() +extract_archive (void) { - register char *data; - int fd, check, namelen, written, openflag; - long size; - time_t acc_upd_times[2]; - register int skipcrud; - register int i; -/* int sparse_ind = 0;*/ - union record *exhdr; - struct saved_dir_info *tmp; -/* int end_nulls; */ - char **longp; - static char *longname; - static char *longlink; - - saverec(&head); /* Make sure it sticks around */ - userec(head); /* And go past it in the archive */ - decode_header(head, &hstat, &head_standard, 1); /* Snarf fields */ - - if(f_confirm && !confirm("extract",head->header.name)) { - if (head->header.isextended) - skip_extended_headers(); - skip_file((long)hstat.st_size); - saverec((union record **)0); - return; + union block *data_block; + int fd; + int status; + ssize_t sstatus; + size_t name_length; + size_t written; + int openflag; + off_t size; + int skipcrud; + int counter; + char typeflag; +#if 0 + int sparse_ind = 0; +#endif + union block *exhdr; + struct delayed_set_stat *data; + +#define CURRENT_FILE_NAME (skipcrud + current_file_name) + + set_next_block_after (current_header); + decode_header (current_header, ¤t_stat, ¤t_format, 1); + + if (interactive_option && !confirm ("extract", current_file_name)) + { + if (current_header->oldgnu_header.isextended) + skip_extended_headers (); + skip_file (current_stat.st_size); + return; + } + + /* Print the block from `current_header' and `current_stat'. */ + + if (verbose_option) + print_header (); + + /* Check for fully specified file names and other atrocities. */ + + skipcrud = 0; + while (!absolute_names_option && CURRENT_FILE_NAME[0] == '/') + { + static int warned_once = 0; + + skipcrud++; /* force relative path */ + if (!warned_once) + { + warned_once = 1; + WARN ((0, 0, _("\ +Removing leading `/' from absolute path names in the archive"))); } + } + + /* Take a safety backup of a previously existing file. */ + + if (backup_option && !to_stdout_option) + if (!maybe_backup_file (CURRENT_FILE_NAME, 0)) + { + ERROR ((0, errno, _("%s: Was unable to backup this file"), + CURRENT_FILE_NAME)); + if (current_header->oldgnu_header.isextended) + skip_extended_headers (); + skip_file (current_stat.st_size); + return; + } - /* Print the record from 'head' and 'hstat' */ - if (f_verbose) - print_header(); - - /* - * Check for fully specified pathnames and other atrocities. - * - * Note, we can't just make a pointer to the new file name, - * since saverec() might move the header and adjust "head". - * We have to start from "head" every time we want to touch - * the header record. - */ - skipcrud = 0; - while (!f_absolute_paths && '/' == head->header.name[skipcrud]) { - static int warned_once = 0; - - skipcrud++; /* Force relative path */ - if (!warned_once++) { - msg("Removing leading / from absolute path names in the archive."); - } - } - - switch (head->header.linkflag) { - - default: - msg("Unknown file type '%c' for %s, extracted as normal file", - head->header.linkflag, skipcrud+head->header.name); - /* FALL THRU */ - - /* - * JK - What we want to do if the file is sparse is loop through - * the array of sparse structures in the header and read in - * and translate the character strings representing 1) the offset - * at which to write and 2) how many bytes to write into numbers, - * which we store into the scratch array, "sparsearray". This - * array makes our life easier the same way it did in creating - * the tar file that had to deal with a sparse file. - * - * After we read in the first five (at most) sparse structures, - * we check to see if the file has an extended header, i.e., - * if more sparse structures are needed to describe the contents - * of the new file. If so, we read in the extended headers - * and continue to store their contents into the sparsearray. - */ - case LF_SPARSE: - sp_array_size = 10; - sparsearray = (struct sp_array *) malloc(sp_array_size * sizeof(struct sp_array)); - for (i = 0; i < SPARSE_IN_HDR; i++) { - sparsearray[i].offset = - from_oct(1+12, head->header.sp[i].offset); - sparsearray[i].numbytes = - from_oct(1+12, head->header.sp[i].numbytes); - if (!sparsearray[i].numbytes) - break; - } - - /* end_nulls = from_oct(1+12, head->header.ending_blanks);*/ - - if (head->header.isextended) { - /* read in the list of extended headers - and translate them into the sparsearray - as before */ - - /* static */ int ind = SPARSE_IN_HDR; - - for (;;) { - - exhdr = findrec(); - for (i = 0; i < SPARSE_EXT_HDR; i++) { - - if (i+ind > sp_array_size-1) { - /* - * realloc the scratch area - * since we've run out of room -- - */ - sparsearray = (struct sp_array *) - realloc(sparsearray, - 2 * sp_array_size * (sizeof(struct sp_array))); - sp_array_size *= 2; - } - if (!exhdr->ext_hdr.sp[i].numbytes) - break; - sparsearray[i+ind].offset = - from_oct(1+12, exhdr->ext_hdr.sp[i].offset); - sparsearray[i+ind].numbytes = - from_oct(1+12, exhdr->ext_hdr.sp[i].numbytes); - } - if (!exhdr->ext_hdr.isextended) - break; - else { - ind += SPARSE_EXT_HDR; - userec(exhdr); - } - } - userec(exhdr); - } - - /* FALL THRU */ - case LF_OLDNORMAL: - case LF_NORMAL: - case LF_CONTIG: - /* - * Appears to be a file. - * See if it's really a directory. - */ - namelen = strlen(skipcrud+head->header.name)-1; - if (head->header.name[skipcrud+namelen] == '/') - goto really_dir; - - /* FIXME, deal with protection issues */ - again_file: - openflag = (f_keep? - O_BINARY|O_NDELAY|O_WRONLY|O_CREAT|O_EXCL: - O_BINARY|O_NDELAY|O_WRONLY|O_CREAT|O_TRUNC) - | ((head->header.linkflag == LF_SPARSE) ? 0 : O_APPEND); - /* - * JK - The last | is a kludge to solve the problem - * the O_APPEND flag causes with files we are - * trying to make sparse: when a file is opened - * with O_APPEND, it writes to the last place - * that something was written, thereby ignoring - * any lseeks that we have done. We add this - * extra condition to make it able to lseek when - * a file is sparse, i.e., we don't open the new - * file with this flag. (Grump -- this bug caused - * me to waste a good deal of time, I might add) - */ - - if(f_exstdout) { - fd = 1; - goto extract_file; - } - #ifdef O_CTG - /* - * Contiguous files (on the Masscomp) have to specify - * the size in the open call that creates them. - */ - if (head->header.linkflag == LF_CONTIG) - fd = open(skipcrud+head->header.name, openflag | O_CTG, - hstat.st_mode, hstat.st_size); - else - #endif - { - #ifdef NO_OPEN3 - /* - * On raw V7 we won't let them specify -k (f_keep), but - * we just bull ahead and create the files. - */ - fd = creat(skipcrud+head->header.name, - hstat.st_mode); - #else - /* - * With 3-arg open(), we can do this up right. - */ - fd = open(skipcrud+head->header.name, openflag, - hstat.st_mode); - #endif - } - - if (fd < 0) { - if (make_dirs(skipcrud+head->header.name)) - goto again_file; - msg_perror("Could not create file %s",skipcrud+head->header.name); - if (head->header.isextended) - skip_extended_headers(); - skip_file((long)hstat.st_size); - goto quit; - } - - extract_file: - if (head->header.linkflag == LF_SPARSE) { - char *name; - int namelen; - - /* - * Kludge alert. NAME is assigned to header.name - * because during the extraction, the space that - * contains the header will get scribbled on, and - * the name will get munged, so any error messages - * that happen to contain the filename will look - * REAL interesting unless we do this. - */ - namelen = strlen(skipcrud+head->header.name); - name = (char *) malloc((sizeof(char)) * namelen); - bcopy(skipcrud+head->header.name, name, namelen); - size = hstat.st_size; - extract_sparse_file(fd, &size, hstat.st_size, - name); - } - else - for (size = hstat.st_size; - size > 0; - size -= written) { - - /* long offset, - numbytes;*/ - - if(f_multivol) { - save_name=head->header.name; - save_totsize=hstat.st_size; - save_sizeleft=size; - } - - /* - * Locate data, determine max length - * writeable, write it, record that - * we have used the data, then check - * if the write worked. - */ - data = findrec()->charptr; - if (data == NULL) { /* Check it... */ - msg("Unexpected EOF on archive file"); - break; - } - /* - * JK - If the file is sparse, use the sparsearray - * that we created before to lseek into the new - * file the proper amount, and to see how many - * bytes we want to write at that position. - */ - /* if (head->header.linkflag == LF_SPARSE) { - off_t pos; - - pos = lseek(fd, (off_t) sparsearray[sparse_ind].offset, 0); - printf("%d at %d\n", (int) pos, sparse_ind); - written = sparsearray[sparse_ind++].numbytes; - } else*/ - written = endofrecs()->charptr - data; - if (written > size) - written = size; - errno = 0; - check = write(fd, data, written); - /* - * The following is in violation of strict - * typing, since the arg to userec - * should be a struct rec *. FIXME. - */ - userec((union record *)(data + written - 1)); - if (check == written) continue; - /* - * Error in writing to file. - * Print it, skip to next file in archive. - */ - if(check<0) - msg_perror("couldn't write to file %s",skipcrud+head->header.name); - else - msg("could only write %d of %d bytes to file %s",written,check,skipcrud+head->header.name); - skip_file((long)(size - written)); - break; /* Still do the close, mod time, chmod, etc */ - } - - if(f_multivol) - save_name = 0; - - /* 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 */ - if(f_exstdout) - break; - - /* if (head->header.isextended) { - register union record *exhdr; - register int i; - - for (i = 0; i < 21; i++) { - long offset; - - if (!exhdr->ext_hdr.sp[i].numbytes) - break; - offset = from_oct(1+12, - exhdr->ext_hdr.sp[i].offset); - written = from_oct(1+12, - exhdr->ext_hdr.sp[i].numbytes); - lseek(fd, offset, 0); - check = write(fd, data, written); - if (check == written) continue; - - } - - - }*/ - check = close(fd); - if (check < 0) { - msg_perror("Error while closing %s",skipcrud+head->header.name); - } - - - set_filestat: - - /* - * If we are root, set the owner and group of the extracted - * file. This does what is wanted both on real Unix and on - * System V. If we are running as a user, we extract as that - * user; if running as root, we extract as the original owner. - */ - if (we_are_root || f_do_chown) { - if (chown(skipcrud+head->header.name, hstat.st_uid, - hstat.st_gid) < 0) { - msg_perror("cannot chown file %s to uid %d gid %d",skipcrud+head->header.name,hstat.st_uid,hstat.st_gid); - } - } - - /* - * Set the modified time of the file. - * - * Note that we set the accessed time to "now", which - * is really "the time we started extracting files". - * unless f_gnudump is used, in which case .st_atime is used - */ - if (!f_modified) { - /* fixme if f_gnudump should set ctime too, but how? */ - if(f_gnudump) - acc_upd_times[0]=hstat.st_atime; - else acc_upd_times[0] = now; /* Accessed now */ - acc_upd_times[1] = hstat.st_mtime; /* Mod'd */ - if (utime(skipcrud+head->header.name, - acc_upd_times) < 0) { - msg_perror("couldn't change access and modification times of %s",skipcrud+head->header.name); - } + /* Extract the archive entry according to its type. */ + + typeflag = current_header->header.typeflag; + switch (typeflag) + { + /* JK - What we want to do if the file is sparse is loop through + the array of sparse structures in the header and read in and + translate the character strings representing 1) the offset at + which to write and 2) how many bytes to write into numbers, + which we store into the scratch array, "sparsearray". This + array makes our life easier the same way it did in creating the + tar file that had to deal with a sparse file. + + After we read in the first five (at most) sparse structures, we + check to see if the file has an extended header, i.e., if more + sparse structures are needed to describe the contents of the new + file. If so, we read in the extended headers and continue to + store their contents into the sparsearray. */ + + case GNUTYPE_SPARSE: + sp_array_size = 10; + sparsearray = (struct sp_array *) + xmalloc (sp_array_size * sizeof (struct sp_array)); + + for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++) + { + sparsearray[counter].offset = + OFF_FROM_OCT (current_header->oldgnu_header.sp[counter].offset); + sparsearray[counter].numbytes = + SIZE_FROM_OCT (current_header->oldgnu_header.sp[counter].numbytes); + if (!sparsearray[counter].numbytes) + break; + } + + if (current_header->oldgnu_header.isextended) + { + /* Read in the list of extended headers and translate them + into the sparsearray as before. Note that this + invalidates current_header. */ + + /* static */ int ind = SPARSES_IN_OLDGNU_HEADER; + + while (1) + { + exhdr = find_next_block (); + for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++) + { + if (counter + ind > sp_array_size - 1) + { + /* Realloc the scratch area since we've run out of + room. */ + + sp_array_size *= 2; + sparsearray = (struct sp_array *) + xrealloc (sparsearray, + sp_array_size * (sizeof (struct sp_array))); + } + /* Compare to 0, or use !(int)..., for Pyramid's dumb + compiler. */ + if (exhdr->sparse_header.sp[counter].numbytes == 0) + break; + sparsearray[counter + ind].offset = + OFF_FROM_OCT (exhdr->sparse_header.sp[counter].offset); + sparsearray[counter + ind].numbytes = + SIZE_FROM_OCT (exhdr->sparse_header.sp[counter].numbytes); } - /* We do the utime before the chmod because some versions of - utime are broken and trash the modes of the file. Since - we then change the mode anyway, we don't care. . . */ - - /* - * If '-k' is not set, open() or creat() could have saved - * the permission bits from a previously created file, - * ignoring the ones we specified. - * Even if -k is set, if the file has abnormal - * mode bits, we must chmod since writing or chown() has - * probably reset them. - * - * If -k is set, we know *we* created this file, so the mode - * bits were set by our open(). If the file is "normal", we - * skip the chmod. This works because we did umask(0) if -p - * is set, so umask will have left the specified mode alone. - */ - if ((!f_keep) - || (hstat.st_mode & (S_ISUID|S_ISGID|S_ISVTX))) { - if (chmod(skipcrud+head->header.name, - notumask & (int)hstat.st_mode) < 0) { - msg_perror("cannot change mode of file %s to %ld",skipcrud+head->header.name,notumask & (int)hstat.st_mode); - } + if (!exhdr->sparse_header.isextended) + break; + else + { + ind += SPARSES_IN_SPARSE_HEADER; + set_next_block_after (exhdr); } + } + set_next_block_after (exhdr); + } + /* Fall through. */ - quit: - break; + case AREGTYPE: + case REGTYPE: + case CONTTYPE: + + /* Appears to be a file. But BSD tar uses the convention that a slash + suffix means a directory. */ + + name_length = strlen (CURRENT_FILE_NAME) - 1; + if (CURRENT_FILE_NAME[name_length] == '/') + goto really_dir; + + /* FIXME: deal with protection issues. */ + + again_file: + openflag = (keep_old_files_option ? + O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_EXCL : + O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_TRUNC) + | ((typeflag == GNUTYPE_SPARSE) ? 0 : O_APPEND); + + /* JK - The last | is a kludge to solve the problem the O_APPEND + flag causes with files we are trying to make sparse: when a file + is opened with O_APPEND, it writes to the last place that + something was written, thereby ignoring any lseeks that we have + done. We add this extra condition to make it able to lseek when + a file is sparse, i.e., we don't open the new file with this + flag. (Grump -- this bug caused me to waste a good deal of + time, I might add) */ - case LF_LINK: - again_link: + if (to_stdout_option) { - struct stat st1,st2; - - check = link (head->header.linkname, - skipcrud+head->header.name); - if (check == 0) - break; - if (make_dirs(skipcrud+head->header.name)) - goto again_link; - if(f_gnudump && errno==EEXIST) - break; - if( stat(head->header.linkname, &st1) == 0 - && stat(skipcrud+head->header.name, &st2)==0 - && st1.st_dev==st2.st_dev - && st1.st_ino==st2.st_ino) - break; - msg_perror("Could not link %s to %s", - skipcrud+head->header.name,head->header.linkname); + fd = 1; + goto extract_file; } - break; -#ifdef S_ISLNK - case LF_SYMLINK: - again_symlink: - check = symlink(head->header.linkname, - skipcrud+head->header.name); - /* FIXME, don't worry uid, gid, etc... */ - if (check == 0) - break; - if (make_dirs(skipcrud+head->header.name)) - goto again_symlink; - msg_perror("Could not create symlink to %s",head->header.linkname); + if (unlink_first_option) + remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option); + +#if O_CTG + /* Contiguous files (on the Masscomp) have to specify the size in + the open call that creates them. */ + + if (typeflag == CONTTYPE) + fd = open (CURRENT_FILE_NAME, openflag | O_CTG, + current_stat.st_mode, current_stat.st_size); + else + fd = open (CURRENT_FILE_NAME, openflag, current_stat.st_mode); + +#else /* not O_CTG */ + if (typeflag == CONTTYPE) + { + static int conttype_diagnosed = 0; + + if (!conttype_diagnosed) + { + conttype_diagnosed = 1; + WARN ((0, 0, _("Extracting contiguous files as regular files"))); + } + } + fd = open (CURRENT_FILE_NAME, openflag, current_stat.st_mode); + +#endif /* not O_CTG */ + + if (fd < 0) + { + if (maybe_recoverable (CURRENT_FILE_NAME)) + goto again_file; + + ERROR ((0, errno, _("%s: Could not create file"), + CURRENT_FILE_NAME)); + if (current_header->oldgnu_header.isextended) + skip_extended_headers (); + skip_file (current_stat.st_size); + if (backup_option) + undo_last_backup (); + break; + } + + extract_file: + if (typeflag == GNUTYPE_SPARSE) + { + char *name; + size_t name_length_bis; + + /* Kludge alert. NAME is assigned to header.name because + during the extraction, the space that contains the header + will get scribbled on, and the name will get munged, so any + error messages that happen to contain the filename will look + REAL interesting unless we do this. */ + + name_length_bis = strlen (CURRENT_FILE_NAME) + 1; + name = (char *) xmalloc (name_length_bis); + memcpy (name, CURRENT_FILE_NAME, name_length_bis); + size = current_stat.st_size; + extract_sparse_file (fd, &size, current_stat.st_size, name); + } + else + for (size = current_stat.st_size; + size > 0; + size -= written) + { + if (multi_volume_option) + { + assign_string (&save_name, current_file_name); + save_totsize = current_stat.st_size; + save_sizeleft = 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 == NULL) + { + ERROR ((0, 0, _("Unexpected EOF on archive file"))); + break; /* FIXME: What happens, then? */ + } + + /* If the file is sparse, use the sparsearray that we created + before to lseek into the new file the proper amount, and to + see how many bytes we want to write at that position. */ + +#if 0 + if (typeflag == GNUTYPE_SPARSE) + { + lseek (fd, sparsearray[sparse_ind].offset, 0); + written = sparsearray[sparse_ind++].numbytes; + } + else +#endif + written = available_space_after (data_block); + + if (written > size) + written = size; + errno = 0; /* FIXME: errno should be read-only */ + sstatus = full_write (fd, data_block->buffer, written); + + 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) + ERROR ((0, errno, _("%s: Could not write to file"), + CURRENT_FILE_NAME)); + else + ERROR ((0, 0, _("%s: Could only write %lu of %lu bytes"), + CURRENT_FILE_NAME, + (unsigned long) sstatus, + (unsigned long) written)); + skip_file (size - written); + break; /* still do the close, mod time, chmod, etc */ + } + + if (multi_volume_option) + assign_string (&save_name, NULL); + + /* 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. */ + + if (to_stdout_option) + break; + +#if 0 + if (current_header->header.isextended) + { + union block *exhdr; + int counter; + + for (counter = 0; counter < 21; counter++) + { + off_t offset; + + if (!exhdr->sparse_header.sp[counter].numbytes) break; + offset = OFF_FROM_OCT (exhdr->sparse_header.sp[counter].offset); + written + = SIZE_FROM_OCT (exhdr->sparse_header.sp[counter].numbytes); + lseek (fd, offset, 0); + sstatus = full_write (fd, data_block->buffer, written); + if (sstatus == written) + continue; + } + } #endif + status = close (fd); + if (status < 0) + { + ERROR ((0, errno, _("%s: Error while closing"), CURRENT_FILE_NAME)); + if (backup_option) + undo_last_backup (); + } + + set_stat (CURRENT_FILE_NAME, ¤t_stat, 0); + break; + + case SYMTYPE: + if (to_stdout_option) + break; + +#ifdef S_ISLNK + if (unlink_first_option) + remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option); + + while (status = symlink (current_link_name, CURRENT_FILE_NAME), + status != 0) + if (!maybe_recoverable (CURRENT_FILE_NAME)) + 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, 1); + + else + { + ERROR ((0, errno, _("%s: Could not create symlink to `%s'"), + CURRENT_FILE_NAME, current_link_name)); + if (backup_option) + undo_last_backup (); + } + break; + +#else /* not S_ISLNK */ + { + static int warned_once = 0; + + if (!warned_once) + { + warned_once = 1; + WARN ((0, 0, _("\ +Attempting extraction of symbolic links as hard links"))); + } + } + /* Fall through. */ + +#endif /* not S_ISLNK */ + + case LNKTYPE: + if (to_stdout_option) + break; + + if (unlink_first_option) + remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option); + + again_link: + { + struct stat st1, st2; + + /* MSDOS does not implement links. However, djgpp's link() actually + copies the file. */ + status = link (current_link_name, CURRENT_FILE_NAME); + + if (status == 0) + break; + if (maybe_recoverable (CURRENT_FILE_NAME)) + goto again_link; + + if (incremental_option && errno == EEXIST) + break; + if (stat (current_link_name, &st1) == 0 + && stat (CURRENT_FILE_NAME, &st2) == 0 + && st1.st_dev == st2.st_dev + && st1.st_ino == st2.st_ino) + break; + + ERROR ((0, errno, _("%s: Could not link to `%s'"), + CURRENT_FILE_NAME, current_link_name)); + if (backup_option) + undo_last_backup (); + } + break; -#ifdef S_IFCHR - case LF_CHR: - hstat.st_mode |= S_IFCHR; - goto make_node; +#if S_IFCHR + case CHRTYPE: + current_stat.st_mode |= S_IFCHR; + goto make_node; #endif -#ifdef S_IFBLK - case LF_BLK: - hstat.st_mode |= S_IFBLK; +#if S_IFBLK + case BLKTYPE: + current_stat.st_mode |= S_IFBLK; #endif + #if defined(S_IFCHR) || defined(S_IFBLK) - make_node: - check = mknod(skipcrud+head->header.name, - (int) hstat.st_mode, (int) hstat.st_rdev); - if (check != 0) { - if (make_dirs(skipcrud+head->header.name)) - goto make_node; - msg_perror("Could not make %s",skipcrud+head->header.name); - break; - }; - goto set_filestat; + make_node: + if (to_stdout_option) + break; + + if (unlink_first_option) + remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option); + + status = mknod (CURRENT_FILE_NAME, current_stat.st_mode, + current_stat.st_rdev); + if (status != 0) + { + if (maybe_recoverable (CURRENT_FILE_NAME)) + goto make_node; + + ERROR ((0, errno, _("%s: Could not make node"), CURRENT_FILE_NAME)); + if (backup_option) + undo_last_backup (); + break; + }; + set_stat (CURRENT_FILE_NAME, ¤t_stat, 0); + break; #endif #ifdef S_ISFIFO - /* If local system doesn't support FIFOs, use default case */ - case LF_FIFO: - make_fifo: - check = mkfifo(skipcrud+head->header.name, - (int) hstat.st_mode); - if (check != 0) { - if (make_dirs(skipcrud+head->header.name)) - goto make_fifo; - msg_perror("Could not make %s",skipcrud+head->header.name); - break; - }; - goto set_filestat; -#endif + case FIFOTYPE: + if (to_stdout_option) + break; - case LF_DIR: - case LF_DUMPDIR: - namelen = strlen(skipcrud+head->header.name)-1; - really_dir: - /* Check for trailing /, and zap as many as we find. */ - while (namelen && head->header.name[skipcrud+namelen] == '/') - head->header.name[skipcrud+namelen--] = '\0'; - if(f_gnudump) { /* Read the entry and delete files - that aren't listed in the archive */ - gnu_restore(skipcrud); - - } else if(head->header.linkflag==LF_DUMPDIR) - skip_file((long)(hstat.st_size)); - - - again_dir: - check = mkdir(skipcrud+head->header.name, - (we_are_root ? 0 : 0300) | (int)hstat.st_mode); - if (check != 0) { - struct stat st1; - - if (make_dirs(skipcrud+head->header.name)) - goto again_dir; - /* If we're trying to create '.', let it be. */ - if (head->header.name[skipcrud+namelen] == '.' && - (namelen==0 || - head->header.name[skipcrud+namelen-1]=='/')) - goto check_perms; - if( errno==EEXIST - && stat(skipcrud+head->header.name,&st1)==0 - && (S_ISDIR(st1.st_mode))) - break; - msg_perror("Could not create directory %s",skipcrud+head->header.name); - break; - } - - check_perms: - if (!we_are_root && 0300 != (0300 & (int) hstat.st_mode)) { - hstat.st_mode |= 0300; - msg("Added write and execute permission to directory %s", - skipcrud+head->header.name); - } + if (unlink_first_option) + remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option); - if (f_modified) - goto set_filestat; - tmp = malloc (sizeof (struct saved_dir_info)); - tmp->path = malloc (strlen (skipcrud + head->header.name) + 1); - strcpy (tmp->path, skipcrud + head->header.name); - tmp->mode = hstat.st_mode; - tmp->atime = hstat.st_atime; - tmp->mtime = hstat.st_mtime; - tmp->next = saved_dir_info_head; - saved_dir_info_head = tmp; - case LF_VOLHDR: - if(f_verbose) { - printf("Reading %s\n",head->header.name); - } - break; + while (status = mkfifo (CURRENT_FILE_NAME, current_stat.st_mode), + status != 0) + if (!maybe_recoverable (CURRENT_FILE_NAME)) + break; - case LF_NAMES: - extract_mangle(head); - break; + if (status == 0) + set_stat (CURRENT_FILE_NAME, ¤t_stat, 0); + else + { + ERROR ((0, errno, _("%s: Could not make fifo"), CURRENT_FILE_NAME)); + if (backup_option) + undo_last_backup (); + } + break; +#endif - case LF_MULTIVOL: - msg("Can't extract '%s'--file is continued from another volume\n",head->header.name); - skip_file((long)hstat.st_size); - break; + case DIRTYPE: + case GNUTYPE_DUMPDIR: + name_length = strlen (CURRENT_FILE_NAME) - 1; + + really_dir: + /* Check for trailing /, and zap as many as we find. */ + while (name_length && CURRENT_FILE_NAME[name_length] == '/') + CURRENT_FILE_NAME[name_length--] = '\0'; - case LF_LONGNAME: - longp = &longname; - goto extract_long; - - case LF_LONGLINK - longp = &longlink; - extract_long: - - if (*longp) - free (*longp); - *longp = ck_malloc (hstat.st_size); - - /* This loop is copied from the extract_file label above; - as a result, some of the variable names (like `written') - might be confusing. */ - for (size = hstat.st_size; - size > 0; - size -= written) - { - - - + if (incremental_option) + { + /* Read the entry and delete files that aren't listed in the + archive. */ + + gnu_restore (skipcrud); } + else if (typeflag == GNUTYPE_DUMPDIR) + skip_file (current_stat.st_size); - /* We don't need to save it any longer. */ - saverec((union record **) 0); /* Unsave it */ -} + if (to_stdout_option) + break; -/* - * After a file/link/symlink/dir creation has failed, see if - * it's because some required directory was not present, and if - * so, create all required dirs. - */ -int -make_dirs(pathname) - char *pathname; -{ - char *p; /* Points into path */ - int madeone = 0; /* Did we do anything yet? */ - int save_errno = errno; /* Remember caller's errno */ - int check; - - if (errno != ENOENT) - return 0; /* Not our problem */ - - for (p = index(pathname, '/'); p != NULL; p = index(p+1, '/')) { - /* Avoid mkdir of empty string, if leading or double '/' */ - if (p == pathname || p[-1] == '/') - continue; - /* Avoid mkdir where last part of path is '.' */ - if (p[-1] == '.' && (p == pathname+1 || p[-2] == '/')) - continue; - *p = 0; /* Truncate the path there */ - check = mkdir (pathname, 0777); /* Try to create it as a dir */ - if (check == 0) { - /* Fix ownership */ - if (we_are_root) { - if (chown(pathname, hstat.st_uid, - hstat.st_gid) < 0) { - msg_perror("cannot change owner of %s to uid %d gid %d",pathname,hstat.st_uid,hstat.st_gid); - } - } - pr_mkdir(pathname, p-pathname, notumask&0777); - madeone++; /* Remember if we made one */ - *p = '/'; - continue; - } - *p = '/'; - if (errno == EEXIST) /* Directory already exists */ - continue; - /* - * Some other error in the mkdir. We return to the caller. - */ - break; + again_dir: + status = mkdir (CURRENT_FILE_NAME, + (we_are_root ? 0 : 0300) | current_stat.st_mode); + if (status != 0) + { + /* If the directory creation fails, let's consider immediately the + case where the directory already exists. We have three good + reasons for clearing out this case before attempting recovery. + + 1) It would not be efficient recovering the error by deleting + the directory in maybe_recoverable, then recreating it right + away. We only hope we will be able to adjust its permissions + adequately, later. + + 2) Removing the directory might fail if it is not empty. By + exception, this real error is traditionally not reported. + + 3) Let's suppose `DIR' already exists and we are about to + extract `DIR/../DIR'. This would fail because the directory + already exists, and maybe_recoverable would react by removing + `DIR'. This then would fail again because there are missing + intermediate directories, and maybe_recoverable would react by + creating `DIR'. We would then have an extraction loop. */ + + if (errno == EEXIST) + { + struct stat st1; + int saved_errno = errno; + + if (stat (CURRENT_FILE_NAME, &st1) == 0 && S_ISDIR (st1.st_mode)) + goto check_perms; + + errno = saved_errno; /* FIXME: errno should be read-only */ + } + + if (maybe_recoverable (CURRENT_FILE_NAME)) + goto again_dir; + + /* If we're trying to create '.', let it be. */ + + /* FIXME: Strange style... */ + + if (CURRENT_FILE_NAME[name_length] == '.' + && (name_length == 0 + || CURRENT_FILE_NAME[name_length - 1] == '/')) + goto check_perms; + + ERROR ((0, errno, _("%s: Could not create directory"), + CURRENT_FILE_NAME)); + if (backup_option) + undo_last_backup (); + break; } - errno = save_errno; /* Restore caller's errno */ - return madeone; /* Tell them to retry if we made one */ -} + check_perms: + if (!we_are_root && 0300 != (0300 & current_stat.st_mode)) + { + current_stat.st_mode |= 0300; + WARN ((0, 0, _("Added write and execute permission to directory %s"), + CURRENT_FILE_NAME)); + } -void -extract_sparse_file(fd, sizeleft, totalsize, name) - int fd; - long *sizeleft, - totalsize; - char *name; -{ -/* register char *data;*/ - union record *datarec; - int sparse_ind = 0; - int written, - count; - - /* assuming sizeleft is initially totalsize */ - - - while (*sizeleft > 0) { - datarec = findrec(); - if (datarec == NULL) { - msg("Unexpected EOF on archive file"); - return; - } - lseek(fd, sparsearray[sparse_ind].offset, 0); - written = sparsearray[sparse_ind++].numbytes; - while (written > RECORDSIZE) { - count = write(fd, datarec->charptr, RECORDSIZE); - if (count < 0) - msg_perror("couldn't write to file %s", name); - written -= count; - *sizeleft -= count; - userec(datarec); - datarec = findrec(); - } +#if !MSDOS + /* MSDOS does not associate timestamps with directories. In this + case, no need to try delaying their restoration. */ - count = write(fd, datarec->charptr, written); - - if (count < 0) { - msg_perror("couldn't write to file %s", name); - } else if (count != written) { - msg("could only write %d of %d bytes to file %s", totalsize - *sizeleft, totalsize, name); - skip_file((long) (*sizeleft)); - } + if (touch_option) + + /* FIXME: I do not believe in this. Ignoring time stamps does not + alleviate the need of delaying the restoration of directories' + mode. Let's ponder this for a little while. */ + + set_mode (CURRENT_FILE_NAME, ¤t_stat); - written -= count; - *sizeleft -= count; - userec(datarec); + else + { + data = ((struct delayed_set_stat *) + xmalloc (sizeof (struct delayed_set_stat))); + data->file_name = xstrdup (CURRENT_FILE_NAME); + data->stat_info = current_stat; + data->next = delayed_set_stat_head; + delayed_set_stat_head = data; } - free(sparsearray); -/* if (end_nulls) { - register int i; - - printf("%d\n", (int) end_nulls); - for (i = 0; i < end_nulls; i++) - write(fd, "\000", 1); - }*/ - userec(datarec); +#endif /* !MSDOS */ + break; + + case GNUTYPE_VOLHDR: + if (verbose_option) + fprintf (stdlis, _("Reading %s\n"), current_file_name); + break; + + case GNUTYPE_NAMES: + extract_mangle (); + break; + + case GNUTYPE_MULTIVOL: + ERROR ((0, 0, _("\ +Cannot extract `%s' -- file is continued from another volume"), + current_file_name)); + skip_file (current_stat.st_size); + if (backup_option) + undo_last_backup (); + break; + + case GNUTYPE_LONGNAME: + case GNUTYPE_LONGLINK: + ERROR ((0, 0, _("Visible long name error"))); + skip_file (current_stat.st_size); + if (backup_option) + undo_last_backup (); + break; + + default: + WARN ((0, 0, + _("Unknown file type '%c' for %s, extracted as normal file"), + typeflag, CURRENT_FILE_NAME)); + goto again_file; + } + +#undef CURRENT_FILE_NAME } -/* Set back the utime and mode for all the extracted directories. */ -void restore_saved_dir_info () +/*----------------------------------------------------------------. +| Set back the utime and mode for all the extracted directories. | +`----------------------------------------------------------------*/ + +void +apply_delayed_set_stat (void) { - time_t acc_upd_times[2]; - saved_dir_info *tmp; + struct delayed_set_stat *data; - while (saved_info_head != NULL) + while (delayed_set_stat_head != NULL) { - /* fixme if f_gnudump should set ctime too, but how? */ - if(f_gnudump) - acc_upd_times[0]=saved_info_head -> atime; - else acc_upd_times[0] = now; /* Accessed now */ - acc_upd_times[1] = saved_info_head -> mtime; /* Mod'd */ - if (utime(saved_info_head -> path, acc_upd_times) < 0) { - msg_perror("couldn't change access and modification times of %s", - saved_info_head -> path); - } - if ((!f_keep) || (saved_info_head -> mode & (S_ISUID|S_ISGID|S_ISVTX))) - { - if (chmod(saved_info_head -> path, - notumask & saved_info_head -> mode) < 0) { - msg_perror("cannot change mode of file %s to %ld", - saved_info_head -> path, - notumask & saved_info_head -> mode); - } - } - saved_info_head = saved_info_head -> next; + data = delayed_set_stat_head; + delayed_set_stat_head = delayed_set_stat_head->next; + set_stat (data->file_name, &data->stat_info, 0); + free (data->file_name); + free (data); } }