|
@@ -1,907 +1,1016 @@
|
|
|
/* Extract files from a tar archive.
|
|
|
- Copyright (C) 1988, 1992, 1993 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 <stdio.h>
|
|
|
-#include <errno.h>
|
|
|
-#ifndef STDC_HEADERS
|
|
|
-extern int errno;
|
|
|
-#endif
|
|
|
-#include <sys/types.h>
|
|
|
+ Copyright (C) 1988, 92, 93, 94, 96, 97 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.h>
|
|
|
time_t time ();
|
|
|
|
|
|
-#ifdef BSD42
|
|
|
-#include <sys/file.h>
|
|
|
+#if HAVE_UTIME_H
|
|
|
+# include <utime.h>
|
|
|
#else
|
|
|
-#ifndef V7
|
|
|
-#include <fcntl.h>
|
|
|
-#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"
|
|
|
+
|
|
|
+static time_t now; /* current time */
|
|
|
+static int we_are_root; /* true if our effective uid == 0 */
|
|
|
+static int newdir_umask; /* umask when creating new directories */
|
|
|
+static int current_umask; /* current umask (which is set to 0 if -p) */
|
|
|
+
|
|
|
+#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;
|
|
|
|
|
|
-#ifdef EMUL_OPEN3
|
|
|
-/* Simulated 3-argument open for systems that don't have it */
|
|
|
-#include "open3.h"
|
|
|
+/* Number of elts storable in the sparsearray. */
|
|
|
+int sp_array_size = 10;
|
|
|
#endif
|
|
|
|
|
|
-#include "tar.h"
|
|
|
-#include "port.h"
|
|
|
+struct delayed_set_stat
|
|
|
+ {
|
|
|
+ struct delayed_set_stat *next;
|
|
|
+ char *file_name;
|
|
|
+ struct stat stat_info;
|
|
|
+ };
|
|
|
|
|
|
-#if defined(_POSIX_VERSION)
|
|
|
-#include <utime.h>
|
|
|
-#else
|
|
|
-struct utimbuf
|
|
|
+static struct delayed_set_stat *delayed_set_stat_head;
|
|
|
+
|
|
|
+/*--------------------------.
|
|
|
+| Set up to extract files. |
|
|
|
+`--------------------------*/
|
|
|
+
|
|
|
+void
|
|
|
+extr_init (void)
|
|
|
{
|
|
|
- long actime;
|
|
|
- long modtime;
|
|
|
-};
|
|
|
+ now = time ((time_t *) 0);
|
|
|
+ we_are_root = geteuid () == 0;
|
|
|
|
|
|
-#endif
|
|
|
+ /* 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. */
|
|
|
+
|
|
|
+ newdir_umask = umask (0);
|
|
|
+ if (same_permissions_option)
|
|
|
+ current_umask = 0;
|
|
|
+ else
|
|
|
+ {
|
|
|
+ umask (newdir_umask); /* restore the kernel umask */
|
|
|
+ current_umask = newdir_umask;
|
|
|
+ }
|
|
|
|
|
|
-extern FILE *msg_file;
|
|
|
+ /* 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;
|
|
|
+}
|
|
|
|
|
|
-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 */
|
|
|
+/*------------------------------------------------------------------.
|
|
|
+| Restore mode for FILE_NAME, from information given in STAT_INFO. |
|
|
|
+`------------------------------------------------------------------*/
|
|
|
|
|
|
-extern char *save_name;
|
|
|
-extern long save_totsize;
|
|
|
-extern long save_sizeleft;
|
|
|
+static void
|
|
|
+set_mode (char *file_name, struct stat *stat_info)
|
|
|
+{
|
|
|
+ /* 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 & (int) stat_info->st_mode) < 0)
|
|
|
+ ERROR ((0, errno, _("%s: Cannot change mode to %0.4o"),
|
|
|
+ file_name, ~current_umask & (int) stat_info->st_mode));
|
|
|
+}
|
|
|
|
|
|
-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 ();
|
|
|
+/*----------------------------------------------------------------------.
|
|
|
+| 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. |
|
|
|
+`----------------------------------------------------------------------*/
|
|
|
|
|
|
-int make_dirs (); /* Makes required directories */
|
|
|
+/* 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 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 */
|
|
|
+static void
|
|
|
+set_stat (char *file_name, struct stat *stat_info, int symlink_flag)
|
|
|
+{
|
|
|
+ struct utimbuf utimbuf;
|
|
|
|
|
|
-/*
|
|
|
- * "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 (!symlink_flag)
|
|
|
+ {
|
|
|
+ /* We do the utime before the chmod because some versions of utime are
|
|
|
+ broken and trash the modes of the file. */
|
|
|
|
|
|
-/* number of elts storable in the sparsearray */
|
|
|
-/*int sp_array_size = 10;*/
|
|
|
+ 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. */
|
|
|
|
|
|
-struct saved_dir_info
|
|
|
+ /* 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 %d gid %d"),
|
|
|
+ file_name, stat_info->st_uid, 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 %d gid %d"),
|
|
|
+ file_name, stat_info->st_uid, 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 %d gid %d"),
|
|
|
+ file_name, stat_info->st_uid, 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, when root, let's attempt setting these bits once more. */
|
|
|
+
|
|
|
+ if (we_are_root && (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 *path;
|
|
|
- int mode;
|
|
|
- int atime;
|
|
|
- int mtime;
|
|
|
- struct saved_dir_info *next;
|
|
|
-};
|
|
|
-
|
|
|
-struct saved_dir_info *saved_dir_info_head;
|
|
|
-
|
|
|
-/*
|
|
|
- * Set up to extract files.
|
|
|
- */
|
|
|
-void
|
|
|
-extr_init ()
|
|
|
+ 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 %d, gid %d"),
|
|
|
+ file_name, current_stat.st_uid, 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)
|
|
|
{
|
|
|
- int ourmask;
|
|
|
+ switch (errno)
|
|
|
+ {
|
|
|
+ case EEXIST:
|
|
|
+ /* Attempt deleting an existing file. However, with -k, just stay
|
|
|
+ quiet. */
|
|
|
|
|
|
- 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)
|
|
|
+ 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, long *sizeleft, long totalsize, char *name)
|
|
|
+{
|
|
|
+ union block *data_block;
|
|
|
+ int sparse_ind = 0;
|
|
|
+ int written, count;
|
|
|
+
|
|
|
+ /* FIXME: `data_block' might be used uninitialized in this function.
|
|
|
+ Reported by Bruno Haible. */
|
|
|
+
|
|
|
+ /* assuming sizeleft is initially totalsize */
|
|
|
+
|
|
|
+ while (*sizeleft > 0)
|
|
|
{
|
|
|
- (void) umask (ourmask); /* Set it back how it was */
|
|
|
- notumask = ~ourmask; /* Make umask override permissions */
|
|
|
+ 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 = 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 = write (fd, data_block->buffer, (size_t) written);
|
|
|
+
|
|
|
+ if (count < 0)
|
|
|
+ ERROR ((0, errno, _("%s: Could not write to file"), name));
|
|
|
+ else if (count != written)
|
|
|
+ {
|
|
|
+ ERROR ((0, 0, _("%s: Could only write %d of %d bytes"),
|
|
|
+ name, count, totalsize));
|
|
|
+ skip_file ((long) (*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;
|
|
|
+ union block *data_block;
|
|
|
+ int fd;
|
|
|
+ int status;
|
|
|
+ int name_length;
|
|
|
+ int written;
|
|
|
+ int openflag;
|
|
|
long size;
|
|
|
- struct utimbuf acc_upd_times;
|
|
|
- register int skipcrud;
|
|
|
- register int i;
|
|
|
- /* int sparse_ind = 0;*/
|
|
|
- union record *exhdr;
|
|
|
- struct saved_dir_info *tmp;
|
|
|
- /* int end_nulls; */
|
|
|
-
|
|
|
- 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", current_file_name))
|
|
|
+ int skipcrud;
|
|
|
+ int counter;
|
|
|
+#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 (head->header.isextended)
|
|
|
+ if (current_header->oldgnu_header.isextended)
|
|
|
skip_extended_headers ();
|
|
|
- skip_file ((long) hstat.st_size);
|
|
|
- saverec ((union record **) 0);
|
|
|
+ skip_file ((long) current_stat.st_size);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- /* Print the record from 'head' and 'hstat' */
|
|
|
- if (f_verbose)
|
|
|
+ /* Print the block from `current_header' and `current_stat'. */
|
|
|
+
|
|
|
+ if (verbose_option)
|
|
|
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.
|
|
|
- */
|
|
|
+ /* Check for fully specified file names and other atrocities. */
|
|
|
+
|
|
|
skipcrud = 0;
|
|
|
- while (!f_absolute_paths
|
|
|
- && '/' == current_file_name[skipcrud])
|
|
|
+ while (!absolute_names_option && CURRENT_FILE_NAME[0] == '/')
|
|
|
{
|
|
|
static int warned_once = 0;
|
|
|
|
|
|
- skipcrud++; /* Force relative path */
|
|
|
- if (!warned_once++)
|
|
|
+ skipcrud++; /* force relative path */
|
|
|
+ if (!warned_once)
|
|
|
{
|
|
|
- msg ("Removing leading / from absolute path names in the archive.");
|
|
|
+ warned_once = 1;
|
|
|
+ WARN ((0, 0, _("\
|
|
|
+Removing leading `/' from absolute path names in the archive")));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- switch (head->header.linkflag)
|
|
|
- {
|
|
|
+ /* Take a safety backup of a previously existing file. */
|
|
|
|
|
|
- default:
|
|
|
- msg ("Unknown file type '%c' for %s, extracted as normal file",
|
|
|
- head->header.linkflag, skipcrud + current_file_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:
|
|
|
+ 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 ((long) current_stat.st_size);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Extract the archive entry according to its type. */
|
|
|
+
|
|
|
+ switch (current_header->header.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 *) ck_malloc (sp_array_size * sizeof (struct sp_array));
|
|
|
- for (i = 0; i < SPARSE_IN_HDR; i++)
|
|
|
+ sparsearray = (struct sp_array *)
|
|
|
+ xmalloc (sp_array_size * sizeof (struct sp_array));
|
|
|
+
|
|
|
+ for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++)
|
|
|
{
|
|
|
- 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)
|
|
|
+ sparsearray[counter].offset =
|
|
|
+ from_oct (1 + 12,
|
|
|
+ current_header->oldgnu_header.sp[counter].offset);
|
|
|
+ sparsearray[counter].numbytes =
|
|
|
+ from_oct (1 + 12,
|
|
|
+ current_header->oldgnu_header.sp[counter].numbytes);
|
|
|
+ if (!sparsearray[counter].numbytes)
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- /* end_nulls = from_oct(1+12, head->header.ending_blanks);*/
|
|
|
-
|
|
|
- if (head->header.isextended)
|
|
|
+ if (current_header->oldgnu_header.isextended)
|
|
|
{
|
|
|
- /* read in the list of extended headers
|
|
|
- and translate them into the sparsearray
|
|
|
- as before */
|
|
|
+ /* Read in the list of extended headers and translate them into
|
|
|
+ the sparsearray as before. */
|
|
|
|
|
|
- /* static */ int ind = SPARSE_IN_HDR;
|
|
|
+ /* static */ int ind = SPARSES_IN_OLDGNU_HEADER;
|
|
|
|
|
|
- for (;;)
|
|
|
+ while (1)
|
|
|
{
|
|
|
-
|
|
|
- exhdr = findrec ();
|
|
|
- for (i = 0; i < SPARSE_EXT_HDR; i++)
|
|
|
+ exhdr = find_next_block ();
|
|
|
+ for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++)
|
|
|
{
|
|
|
-
|
|
|
- if (i + ind > sp_array_size - 1)
|
|
|
+ if (counter + ind > sp_array_size - 1)
|
|
|
{
|
|
|
- /*
|
|
|
- * realloc the scratch area
|
|
|
- * since we've run out of room --
|
|
|
- */
|
|
|
- sparsearray = (struct sp_array *)
|
|
|
- ck_realloc (sparsearray,
|
|
|
- 2 * sp_array_size * (sizeof (struct sp_array)));
|
|
|
+ /* 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)));
|
|
|
}
|
|
|
- if (!exhdr->ext_hdr.sp[i].numbytes)
|
|
|
+ /* Compare to 0, or use !(int)..., for Pyramid's dumb
|
|
|
+ compiler. */
|
|
|
+ if (exhdr->sparse_header.sp[counter].numbytes == 0)
|
|
|
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);
|
|
|
+ sparsearray[counter + ind].offset =
|
|
|
+ from_oct (1 + 12,
|
|
|
+ exhdr->sparse_header.sp[counter].offset);
|
|
|
+ sparsearray[counter + ind].numbytes =
|
|
|
+ from_oct (1 + 12,
|
|
|
+ exhdr->sparse_header.sp[counter].numbytes);
|
|
|
}
|
|
|
- if (!exhdr->ext_hdr.isextended)
|
|
|
+ if (!exhdr->sparse_header.isextended)
|
|
|
break;
|
|
|
else
|
|
|
{
|
|
|
- ind += SPARSE_EXT_HDR;
|
|
|
- userec (exhdr);
|
|
|
+ ind += SPARSES_IN_SPARSE_HEADER;
|
|
|
+ set_next_block_after (exhdr);
|
|
|
}
|
|
|
}
|
|
|
- userec (exhdr);
|
|
|
+ set_next_block_after (exhdr);
|
|
|
}
|
|
|
+ /* Fall through. */
|
|
|
+
|
|
|
+ case AREGTYPE:
|
|
|
+ case REGTYPE:
|
|
|
+ case CONTTYPE:
|
|
|
|
|
|
- /* 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 + current_file_name) - 1;
|
|
|
- if (current_file_name[skipcrud + namelen] == '/')
|
|
|
+ /* 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 */
|
|
|
+ /* FIXME: deal with protection issues. */
|
|
|
+
|
|
|
again_file:
|
|
|
- openflag = (f_keep ?
|
|
|
+ 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)
|
|
|
- | ((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)
|
|
|
+ | ((current_header->header.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) */
|
|
|
+
|
|
|
+ if (to_stdout_option)
|
|
|
{
|
|
|
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 ((longname ? longname : head->header.name)
|
|
|
- + skipcrud,
|
|
|
- openflag | O_CTG,
|
|
|
- hstat.st_mode, hstat.st_size);
|
|
|
+
|
|
|
+ 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 (current_header->header.typeflag == CONTTYPE)
|
|
|
+ fd = open (CURRENT_FILE_NAME, openflag | O_CTG,
|
|
|
+ current_stat.st_mode, current_stat.st_size);
|
|
|
else
|
|
|
-#endif
|
|
|
+ fd = open (CURRENT_FILE_NAME, openflag, current_stat.st_mode);
|
|
|
+
|
|
|
+#else /* not O_CTG */
|
|
|
+ if (current_header->header.typeflag == CONTTYPE)
|
|
|
{
|
|
|
-#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 ((longname
|
|
|
- ? longname
|
|
|
- : head->header.name) + skipcrud,
|
|
|
- hstat.st_mode);
|
|
|
-#else
|
|
|
- /*
|
|
|
- * With 3-arg open(), we can do this up right.
|
|
|
- */
|
|
|
- fd = open (skipcrud + current_file_name,
|
|
|
- openflag, hstat.st_mode);
|
|
|
-#endif
|
|
|
+ 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 (make_dirs (skipcrud + current_file_name))
|
|
|
+ if (maybe_recoverable (CURRENT_FILE_NAME))
|
|
|
goto again_file;
|
|
|
- msg_perror ("Could not create file %s",
|
|
|
- skipcrud + current_file_name);
|
|
|
- if (head->header.isextended)
|
|
|
+
|
|
|
+ ERROR ((0, errno, _("%s: Could not create file"),
|
|
|
+ CURRENT_FILE_NAME));
|
|
|
+ if (current_header->oldgnu_header.isextended)
|
|
|
skip_extended_headers ();
|
|
|
- skip_file ((long) hstat.st_size);
|
|
|
- goto quit;
|
|
|
+ skip_file ((long) current_stat.st_size);
|
|
|
+ if (backup_option)
|
|
|
+ undo_last_backup ();
|
|
|
+ break;
|
|
|
}
|
|
|
|
|
|
extract_file:
|
|
|
- if (head->header.linkflag == LF_SPARSE)
|
|
|
+ if (current_header->header.typeflag == GNUTYPE_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 + current_file_name) + 1;
|
|
|
- name = (char *) ck_malloc ((sizeof (char)) * namelen);
|
|
|
- bcopy (skipcrud + current_file_name, name, namelen);
|
|
|
- size = hstat.st_size;
|
|
|
- extract_sparse_file (fd, &size, hstat.st_size, name);
|
|
|
+ int 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 ((sizeof (char)) * name_length_bis);
|
|
|
+ memcpy (name, CURRENT_FILE_NAME, (size_t) name_length_bis);
|
|
|
+ size = current_stat.st_size;
|
|
|
+ extract_sparse_file (fd, &size, current_stat.st_size, name);
|
|
|
}
|
|
|
else
|
|
|
- for (size = hstat.st_size;
|
|
|
+ for (size = current_stat.st_size;
|
|
|
size > 0;
|
|
|
size -= written)
|
|
|
{
|
|
|
+#if 0
|
|
|
+ long offset, numbytes;
|
|
|
+#endif
|
|
|
+ if (multi_volume_option)
|
|
|
+ {
|
|
|
+ assign_string (&save_name, current_file_name);
|
|
|
+ save_totsize = current_stat.st_size;
|
|
|
+ save_sizeleft = size;
|
|
|
+ }
|
|
|
|
|
|
- /* long offset,
|
|
|
- numbytes;*/
|
|
|
+ /* Locate data, determine max length writeable, write it,
|
|
|
+ block that we have used the data, then check if the write
|
|
|
+ worked. */
|
|
|
|
|
|
- if (f_multivol)
|
|
|
+ data_block = find_next_block ();
|
|
|
+ if (data_block == NULL)
|
|
|
{
|
|
|
- save_name = current_file_name;
|
|
|
- save_totsize = hstat.st_size;
|
|
|
- save_sizeleft = size;
|
|
|
+ ERROR ((0, 0, _("Unexpected EOF on archive file")));
|
|
|
+ break; /* FIXME: What happens, then? */
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * 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;
|
|
|
+ /* 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 (current_header->header.typeflag == GNUTYPE_SPARSE)
|
|
|
+ {
|
|
|
+ off_t pos;
|
|
|
+
|
|
|
+ pos = lseek (fd, (off_t) sparsearray[sparse_ind].offset, 0);
|
|
|
+ fprintf (msg_file, _("%d at %d\n"), (int) pos, sparse_ind);
|
|
|
+ written = sparsearray[sparse_ind++].numbytes;
|
|
|
}
|
|
|
- /*
|
|
|
- * 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;
|
|
|
+ else
|
|
|
+#endif
|
|
|
+ written = available_space_after (data_block);
|
|
|
+
|
|
|
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)
|
|
|
+ errno = 0; /* FIXME: errno should be read-only */
|
|
|
+ status = write (fd, data_block->buffer, (size_t) written);
|
|
|
+
|
|
|
+ set_next_block_after ((union block *)
|
|
|
+ (data_block->buffer + written - 1));
|
|
|
+ if (status == 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 + current_file_name);
|
|
|
+
|
|
|
+ /* Error in writing to file. Print it, skip to next file in
|
|
|
+ archive. */
|
|
|
+
|
|
|
+ if (status < 0)
|
|
|
+ ERROR ((0, errno, _("%s: Could not write to file"),
|
|
|
+ CURRENT_FILE_NAME));
|
|
|
else
|
|
|
- msg ("could only write %d of %d bytes to file %s",
|
|
|
- check, written, skipcrud + current_file_name);
|
|
|
+ ERROR ((0, 0, _("%s: Could only write %d of %d bytes"),
|
|
|
+ CURRENT_FILE_NAME, status, written));
|
|
|
skip_file ((long) (size - written));
|
|
|
- break; /* Still do the close, mod time, chmod, etc */
|
|
|
+ break; /* still do the close, mod time, chmod, etc */
|
|
|
}
|
|
|
|
|
|
- if (f_multivol)
|
|
|
- save_name = 0;
|
|
|
+ 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 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)
|
|
|
+ if (to_stdout_option)
|
|
|
break;
|
|
|
|
|
|
- /* if (head->header.isextended) {
|
|
|
- register union record *exhdr;
|
|
|
- register int i;
|
|
|
+#if 0
|
|
|
+ if (current_header->header.isextended)
|
|
|
+ {
|
|
|
+ union block *exhdr;
|
|
|
+ int counter;
|
|
|
+
|
|
|
+ for (counter = 0; counter < 21; counter++)
|
|
|
+ {
|
|
|
+ off_t offset;
|
|
|
|
|
|
- for (i = 0; i < 21; i++) {
|
|
|
- long offset;
|
|
|
+ if (!exhdr->sparse_header.sp[counter].numbytes)
|
|
|
+ break;
|
|
|
+ offset = from_oct (1 + 12, exhdr->sparse_header.sp[counter].offset);
|
|
|
+ written = from_oct (1 + 12, exhdr->sparse_header.sp[counter].numbytes);
|
|
|
+ lseek (fd, offset, 0);
|
|
|
+ status = write (fd, data_block->buffer, written);
|
|
|
+ if (status == 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 ();
|
|
|
+ }
|
|
|
|
|
|
- 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;
|
|
|
+ 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);
|
|
|
|
|
|
- }*/
|
|
|
- check = close (fd);
|
|
|
- if (check < 0)
|
|
|
- {
|
|
|
- msg_perror ("Error while closing %s",
|
|
|
- skipcrud + current_file_name);
|
|
|
- }
|
|
|
+ while (status = symlink (current_link_name, CURRENT_FILE_NAME),
|
|
|
+ status != 0)
|
|
|
+ if (!maybe_recoverable (CURRENT_FILE_NAME))
|
|
|
+ break;
|
|
|
|
|
|
+ if (status == 0)
|
|
|
|
|
|
- set_filestat:
|
|
|
+ /* 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. */
|
|
|
|
|
|
- /*
|
|
|
- * 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 + current_file_name,
|
|
|
- hstat.st_uid, hstat.st_gid) < 0)
|
|
|
- {
|
|
|
- msg_perror ("cannot chown file %s to uid %d gid %d",
|
|
|
- skipcrud + current_file_name,
|
|
|
- hstat.st_uid, hstat.st_gid);
|
|
|
- }
|
|
|
- }
|
|
|
+ set_stat (CURRENT_FILE_NAME, ¤t_stat, 1);
|
|
|
|
|
|
- /*
|
|
|
- * 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.actime = hstat.st_atime;
|
|
|
- else
|
|
|
- acc_upd_times.actime = now; /* Accessed now */
|
|
|
- acc_upd_times.modtime = hstat.st_mtime; /* Mod'd */
|
|
|
- if (utime (skipcrud + current_file_name,
|
|
|
- &acc_upd_times) < 0)
|
|
|
- {
|
|
|
- msg_perror ("couldn't change access and modification times of %s", skipcrud + current_file_name);
|
|
|
- }
|
|
|
- }
|
|
|
- /* 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)))
|
|
|
+ else
|
|
|
{
|
|
|
- if (chmod (skipcrud + current_file_name,
|
|
|
- notumask & (int) hstat.st_mode) < 0)
|
|
|
- {
|
|
|
- msg_perror ("cannot change mode of file %s to %lo",
|
|
|
- skipcrud + current_file_name,
|
|
|
- notumask & (int) hstat.st_mode);
|
|
|
- }
|
|
|
+ ERROR ((0, errno, _("%s: Could not create symlink to `%s'"),
|
|
|
+ CURRENT_FILE_NAME, current_link_name));
|
|
|
+ if (backup_option)
|
|
|
+ undo_last_backup ();
|
|
|
}
|
|
|
-
|
|
|
- quit:
|
|
|
break;
|
|
|
|
|
|
- case LF_LINK:
|
|
|
+#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;
|
|
|
|
|
|
- check = link (current_link_name, skipcrud + current_file_name);
|
|
|
+ /* MSDOS does not implement links. However, djgpp's link() actually
|
|
|
+ copies the file. */
|
|
|
+ status = link (current_link_name, CURRENT_FILE_NAME);
|
|
|
|
|
|
- if (check == 0)
|
|
|
+ if (status == 0)
|
|
|
break;
|
|
|
- if (make_dirs (skipcrud + current_file_name))
|
|
|
+ if (maybe_recoverable (CURRENT_FILE_NAME))
|
|
|
goto again_link;
|
|
|
- if (f_gnudump && errno == EEXIST)
|
|
|
+
|
|
|
+ if (incremental_option && errno == EEXIST)
|
|
|
break;
|
|
|
if (stat (current_link_name, &st1) == 0
|
|
|
- && stat (current_file_name + skipcrud, &st2) == 0
|
|
|
+ && stat (CURRENT_FILE_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 + current_file_name,
|
|
|
- current_link_name);
|
|
|
- }
|
|
|
- break;
|
|
|
|
|
|
-#ifdef S_ISLNK
|
|
|
- case LF_SYMLINK:
|
|
|
- again_symlink:
|
|
|
- check = symlink (current_link_name,
|
|
|
- skipcrud + current_file_name);
|
|
|
- /* FIXME, don't worry uid, gid, etc... */
|
|
|
- if (check == 0)
|
|
|
- break;
|
|
|
- if (make_dirs (current_file_name + skipcrud))
|
|
|
- goto again_symlink;
|
|
|
- msg_perror ("Could not create symlink to %s",
|
|
|
- current_link_name);
|
|
|
+ ERROR ((0, errno, _("%s: Could not link to `%s'"),
|
|
|
+ CURRENT_FILE_NAME, current_link_name));
|
|
|
+ if (backup_option)
|
|
|
+ undo_last_backup ();
|
|
|
+ }
|
|
|
break;
|
|
|
-#endif
|
|
|
|
|
|
-#ifdef S_IFCHR
|
|
|
- case LF_CHR:
|
|
|
- hstat.st_mode |= S_IFCHR;
|
|
|
+#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 (current_file_name + skipcrud,
|
|
|
- (int) hstat.st_mode, (int) hstat.st_rdev);
|
|
|
- if (check != 0)
|
|
|
+ if (to_stdout_option)
|
|
|
+ break;
|
|
|
+
|
|
|
+ if (unlink_first_option)
|
|
|
+ remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option);
|
|
|
+
|
|
|
+ status = mknod (CURRENT_FILE_NAME, (int) current_stat.st_mode,
|
|
|
+ (int) current_stat.st_rdev);
|
|
|
+ if (status != 0)
|
|
|
{
|
|
|
- if (make_dirs (skipcrud + current_file_name))
|
|
|
+ if (maybe_recoverable (CURRENT_FILE_NAME))
|
|
|
goto make_node;
|
|
|
- msg_perror ("Could not make %s",
|
|
|
- current_file_name + skipcrud);
|
|
|
+
|
|
|
+ ERROR ((0, errno, _("%s: Could not make node"), CURRENT_FILE_NAME));
|
|
|
+ if (backup_option)
|
|
|
+ undo_last_backup ();
|
|
|
break;
|
|
|
};
|
|
|
- goto set_filestat;
|
|
|
+ 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 (current_file_name + skipcrud,
|
|
|
- (int) hstat.st_mode);
|
|
|
- if (check != 0)
|
|
|
- {
|
|
|
- if (make_dirs (current_file_name + skipcrud))
|
|
|
- goto make_fifo;
|
|
|
- msg_perror ("Could not make %s",
|
|
|
- skipcrud + current_file_name);
|
|
|
+ case FIFOTYPE:
|
|
|
+ if (to_stdout_option)
|
|
|
+ break;
|
|
|
+
|
|
|
+ if (unlink_first_option)
|
|
|
+ remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option);
|
|
|
+
|
|
|
+ while (status = mkfifo (CURRENT_FILE_NAME, (int) current_stat.st_mode),
|
|
|
+ status != 0)
|
|
|
+ if (!maybe_recoverable (CURRENT_FILE_NAME))
|
|
|
break;
|
|
|
- };
|
|
|
- goto set_filestat;
|
|
|
+
|
|
|
+ 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_DIR:
|
|
|
- case LF_DUMPDIR:
|
|
|
- namelen = strlen (current_file_name + skipcrud) - 1;
|
|
|
+ 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 (namelen
|
|
|
- && current_file_name[skipcrud + namelen] == '/')
|
|
|
- current_file_name[skipcrud + namelen--] = '\0';
|
|
|
- if (f_gnudump)
|
|
|
- { /* Read the entry and delete files
|
|
|
- that aren't listed in the archive */
|
|
|
- gnu_restore (skipcrud);
|
|
|
+ /* Check for trailing /, and zap as many as we find. */
|
|
|
+ while (name_length && CURRENT_FILE_NAME[name_length] == '/')
|
|
|
+ CURRENT_FILE_NAME[name_length--] = '\0';
|
|
|
+
|
|
|
+ if (incremental_option)
|
|
|
+ {
|
|
|
+ /* 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));
|
|
|
+ else if (current_header->header.typeflag == GNUTYPE_DUMPDIR)
|
|
|
+ skip_file ((long) (current_stat.st_size));
|
|
|
|
|
|
+ if (to_stdout_option)
|
|
|
+ break;
|
|
|
|
|
|
again_dir:
|
|
|
- check = mkdir (skipcrud + current_file_name,
|
|
|
- (we_are_root ? 0 : 0300) | (int) hstat.st_mode);
|
|
|
- if (check != 0)
|
|
|
+ status = mkdir (CURRENT_FILE_NAME,
|
|
|
+ (we_are_root ? 0 : 0300) | (int) current_stat.st_mode);
|
|
|
+ if (status != 0)
|
|
|
{
|
|
|
- struct stat st1;
|
|
|
+ /* 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;
|
|
|
|
|
|
- if (make_dirs (skipcrud + current_file_name))
|
|
|
+ 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. */
|
|
|
- if (current_file_name[skipcrud + namelen] == '.' &&
|
|
|
- (namelen == 0 ||
|
|
|
- current_file_name[skipcrud + namelen - 1] == '/'))
|
|
|
+
|
|
|
+ /* 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;
|
|
|
- if (errno == EEXIST
|
|
|
- && stat (skipcrud + current_file_name, &st1) == 0
|
|
|
- && (S_ISDIR (st1.st_mode)))
|
|
|
- break;
|
|
|
- msg_perror ("Could not create directory %s", skipcrud + current_file_name);
|
|
|
+
|
|
|
+ ERROR ((0, errno, _("%s: Could not create directory"),
|
|
|
+ CURRENT_FILE_NAME));
|
|
|
+ if (backup_option)
|
|
|
+ undo_last_backup ();
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
check_perms:
|
|
|
- if (!we_are_root && 0300 != (0300 & (int) hstat.st_mode))
|
|
|
+ if (!we_are_root && 0300 != (0300 & (int) current_stat.st_mode))
|
|
|
{
|
|
|
- hstat.st_mode |= 0300;
|
|
|
- msg ("Added write and execute permission to directory %s",
|
|
|
- skipcrud + current_file_name);
|
|
|
+ current_stat.st_mode |= 0300;
|
|
|
+ WARN ((0, 0, _("Added write and execute permission to directory %s"),
|
|
|
+ CURRENT_FILE_NAME));
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * 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 + current_file_name,
|
|
|
- hstat.st_uid, hstat.st_gid) < 0)
|
|
|
- {
|
|
|
- msg_perror ("cannot chown file %s to uid %d gid %d",
|
|
|
- skipcrud + current_file_name,
|
|
|
- hstat.st_uid, hstat.st_gid);
|
|
|
- }
|
|
|
- }
|
|
|
+#if !MSDOS
|
|
|
+ /* MSDOS does not associate timestamps with directories. In this
|
|
|
+ case, no need to try delaying their restoration. */
|
|
|
+
|
|
|
+ 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);
|
|
|
|
|
|
- if (!f_modified)
|
|
|
- {
|
|
|
- tmp = ((struct saved_dir_info *)
|
|
|
- ck_malloc (sizeof (struct saved_dir_info)));
|
|
|
- tmp->path = (char *) ck_malloc (strlen (skipcrud
|
|
|
- + current_file_name) + 1);
|
|
|
- strcpy (tmp->path, skipcrud + current_file_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;
|
|
|
- }
|
|
|
else
|
|
|
{
|
|
|
- /* This functions exactly as the code for set_filestat above. */
|
|
|
- if ((!f_keep)
|
|
|
- || (hstat.st_mode & (S_ISUID | S_ISGID | S_ISVTX)))
|
|
|
- {
|
|
|
- if (chmod (skipcrud + current_file_name,
|
|
|
- notumask & (int) hstat.st_mode) < 0)
|
|
|
- {
|
|
|
- msg_perror ("cannot change mode of file %s to %lo",
|
|
|
- skipcrud + current_file_name,
|
|
|
- notumask & (int) hstat.st_mode);
|
|
|
- }
|
|
|
- }
|
|
|
+ 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;
|
|
|
}
|
|
|
+#endif /* !MSDOS */
|
|
|
break;
|
|
|
|
|
|
- case LF_VOLHDR:
|
|
|
- if (f_verbose)
|
|
|
- {
|
|
|
- printf ("Reading %s\n", current_file_name);
|
|
|
- }
|
|
|
+ case GNUTYPE_VOLHDR:
|
|
|
+ if (verbose_option)
|
|
|
+ fprintf (stdlis, _("Reading %s\n"), current_file_name);
|
|
|
break;
|
|
|
|
|
|
- case LF_NAMES:
|
|
|
- extract_mangle (head);
|
|
|
+ case GNUTYPE_NAMES:
|
|
|
+ extract_mangle ();
|
|
|
break;
|
|
|
|
|
|
- case LF_MULTIVOL:
|
|
|
- msg ("Can't extract '%s'--file is continued from another volume\n", current_file_name);
|
|
|
- skip_file ((long) hstat.st_size);
|
|
|
+ case GNUTYPE_MULTIVOL:
|
|
|
+ ERROR ((0, 0, _("\
|
|
|
+Cannot extract `%s' -- file is continued from another volume"),
|
|
|
+ current_file_name));
|
|
|
+ skip_file ((long) current_stat.st_size);
|
|
|
+ if (backup_option)
|
|
|
+ undo_last_backup ();
|
|
|
break;
|
|
|
|
|
|
- case LF_LONGNAME:
|
|
|
- case LF_LONGLINK:
|
|
|
- msg ("Visible long name error\n");
|
|
|
- skip_file ((long) hstat.st_size);
|
|
|
+ case GNUTYPE_LONGNAME:
|
|
|
+ case GNUTYPE_LONGLINK:
|
|
|
+ ERROR ((0, 0, _("Visible long name error")));
|
|
|
+ skip_file ((long) current_stat.st_size);
|
|
|
+ if (backup_option)
|
|
|
+ undo_last_backup ();
|
|
|
break;
|
|
|
- }
|
|
|
|
|
|
- /* We don't need to save it any longer. */
|
|
|
- saverec ((union record **) 0);/* Unsave it */
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- * 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;
|
|
|
+ default:
|
|
|
+ WARN ((0, 0,
|
|
|
+ _("Unknown file type '%c' for %s, extracted as normal file"),
|
|
|
+ current_header->header.typeflag, CURRENT_FILE_NAME));
|
|
|
+ goto again_file;
|
|
|
}
|
|
|
|
|
|
- errno = save_errno; /* Restore caller's errno */
|
|
|
- return madeone; /* Tell them to retry if we made one */
|
|
|
+#undef 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 */
|
|
|
-
|
|
|
+/*----------------------------------------------------------------.
|
|
|
+| Set back the utime and mode for all the extracted directories. |
|
|
|
+`----------------------------------------------------------------*/
|
|
|
|
|
|
- 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 ();
|
|
|
- }
|
|
|
-
|
|
|
- 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", count,
|
|
|
- totalsize, name);
|
|
|
- skip_file ((long) (*sizeleft));
|
|
|
- }
|
|
|
-
|
|
|
- written -= count;
|
|
|
- *sizeleft -= count;
|
|
|
- userec (datarec);
|
|
|
- }
|
|
|
- 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);
|
|
|
-}
|
|
|
-
|
|
|
-/* Set back the utime and mode for all the extracted directories. */
|
|
|
-void
|
|
|
-restore_saved_dir_info ()
|
|
|
+void
|
|
|
+apply_delayed_set_stat (void)
|
|
|
{
|
|
|
- struct utimbuf acc_upd_times;
|
|
|
+ struct delayed_set_stat *data;
|
|
|
|
|
|
- while (saved_dir_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.actime = saved_dir_info_head->atime;
|
|
|
- else
|
|
|
- acc_upd_times.actime = now; /* Accessed now */
|
|
|
- acc_upd_times.modtime = saved_dir_info_head->mtime; /* Mod'd */
|
|
|
- if (utime (saved_dir_info_head->path, &acc_upd_times) < 0)
|
|
|
- {
|
|
|
- msg_perror ("couldn't change access and modification times of %s",
|
|
|
- saved_dir_info_head->path);
|
|
|
- }
|
|
|
- if ((!f_keep) || (saved_dir_info_head->mode & (S_ISUID | S_ISGID | S_ISVTX)))
|
|
|
- {
|
|
|
- if (chmod (saved_dir_info_head->path,
|
|
|
- notumask & saved_dir_info_head->mode) < 0)
|
|
|
- {
|
|
|
- msg_perror ("cannot change mode of file %s to %lo",
|
|
|
- saved_dir_info_head->path,
|
|
|
- notumask & saved_dir_info_head->mode);
|
|
|
- }
|
|
|
- }
|
|
|
- saved_dir_info_head = saved_dir_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);
|
|
|
}
|
|
|
}
|