/* Extract files from a tar archive.
- Copyright (C) 1988, 92, 93, 94, 96, 97 Free Software Foundation, Inc.
+ 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
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. */
+ 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "system.h"
/* 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;
+ newdir_umask &= ~ MODE_WXUSR;
}
/*------------------------------------------------------------------.
static void
set_mode (char *file_name, struct stat *stat_info)
{
- /* We ought to force permission when -k is not selected, because if the
+ /* Do nothing unless we are restoring the original permissions.
+
+ We must force permission when -k and -U are 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
+ But with -k or -U 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 ((we_are_root || same_permissions_option)
+ && ((!keep_old_files_option && !unlink_first_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"),
+ ERROR ((0, errno, _("%s: Cannot change mode to %04lo"),
file_name,
(unsigned long) (~current_umask & stat_info->st_mode)));
}
continue;
*cursor = '\0'; /* truncate the path there */
- status = mkdir (file_name, ~newdir_umask & 0777);
+ status = mkdir (file_name, ~newdir_umask & MODE_RWX);
if (status == 0)
{
(unsigned long) current_stat.st_gid));
print_for_mkdir (file_name, cursor - file_name,
- ~newdir_umask & 0777);
+ ~newdir_umask & MODE_RWX);
did_something = 1;
*cursor = '/';
return did_something; /* tell them to retry if we made one */
}
+/*--------------------------------------------------------------------.
+| Unlink the destination, if we are supposed to do so. |
+| Return zero if extraction should not proceed. |
+`--------------------------------------------------------------------*/
+
+static int
+unlink_destination (char const *file_name)
+{
+ if (unlink_first_option
+ && !remove_any_file (file_name, recursive_unlink_option)
+ && errno != ENOENT)
+ {
+ ERROR ((0, errno, _("Cannot remove %s"), file_name));
+ return 0;
+ }
+
+ return 1;
+}
+
/*--------------------------------------------------------------------.
| Attempt repairing what went wrong with the extraction. Delete an |
| already existing file or create missing intermediate directories. |
switch (errno)
{
case EEXIST:
- /* Attempt deleting an existing file. However, with -k, just stay
+ /* Attempt deleting an existing file. However, with -k or -U, just stay
quiet. */
- if (keep_old_files_option)
+ if (keep_old_files_option || unlink_first_option)
return 0;
return remove_any_file (file_name, 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 ();
+ union block *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);
+ if (lseek (fd, sparsearray[sparse_ind].offset, SEEK_SET) < 0)
+ {
+ char buf[UINTMAX_STRSIZE_BOUND];
+ ERROR ((0, errno, _("%s: lseek error at byte %s"),
+ STRINGIFY_BIGINT (sparsearray[sparse_ind].offset, buf),
+ name));
+ return;
+ }
written = sparsearray[sparse_ind++].numbytes;
while (written > BLOCKSIZE)
{
- count = write (fd, data_block->buffer, BLOCKSIZE);
+ count = full_write (fd, data_block->buffer, BLOCKSIZE);
if (count < 0)
ERROR ((0, errno, _("%s: Could not write to file"), name));
written -= count;
data_block = find_next_block ();
}
- count = write (fd, data_block->buffer, written);
+ count = full_write (fd, data_block->buffer, written);
if (count < 0)
ERROR ((0, errno, _("%s: Could not write to file"), name));
*sizeleft -= count;
set_next_block_after (data_block);
}
+
free (sparsearray);
- set_next_block_after (data_block);
}
/*----------------------------------.
off_t size;
int skipcrud;
int counter;
+ char typeflag;
#if 0
int sparse_ind = 0;
#endif
/* Extract the archive entry according to its type. */
- switch (current_header->header.typeflag)
+ 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
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. Note that this
+ invalidates current_header. */
/* static */ int ind = SPARSES_IN_OLDGNU_HEADER;
/* FIXME: deal with protection issues. */
again_file:
- openflag = (keep_old_files_option ?
+ openflag = (keep_old_files_option || unlink_first_option ?
O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_EXCL :
O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_TRUNC)
- | ((current_header->header.typeflag == GNUTYPE_SPARSE) ? 0 : O_APPEND);
+ | ((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
goto extract_file;
}
- if (unlink_first_option)
- remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option);
+ if (!unlink_destination (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;
+ }
#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)
+ 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 (current_header->header.typeflag == CONTTYPE)
+ if (typeflag == CONTTYPE)
{
static int conttype_diagnosed = 0;
}
extract_file:
- if (current_header->header.typeflag == GNUTYPE_SPARSE)
+ if (typeflag == GNUTYPE_SPARSE)
{
char *name;
size_t name_length_bis;
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 (current_header->header.typeflag == GNUTYPE_SPARSE)
- {
- lseek (fd, sparsearray[sparse_ind].offset, 0);
- written = sparsearray[sparse_ind++].numbytes;
- }
- else
-#endif
- written = available_space_after (data_block);
+ written = available_space_after (data_block);
if (written > size)
written = size;
errno = 0; /* FIXME: errno should be read-only */
- sstatus = write (fd, data_block->buffer, written);
+ sstatus = full_write (fd, data_block->buffer, written);
set_next_block_after ((union block *)
(data_block->buffer + written - 1));
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 = write (fd, data_block->buffer, written);
- if (sstatus == written)
- continue;
- }
- }
-#endif
status = close (fd);
if (status < 0)
{
if (to_stdout_option)
break;
-#ifdef S_ISLNK
- if (unlink_first_option)
- remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option);
+#ifdef HAVE_SYMLINK
+ if (!unlink_destination (CURRENT_FILE_NAME))
+ break;
while (status = symlink (current_link_name, CURRENT_FILE_NAME),
status != 0)
}
break;
-#else /* not S_ISLNK */
+#else
{
static int warned_once = 0;
}
/* Fall through. */
-#endif /* not S_ISLNK */
+#endif
case LNKTYPE:
if (to_stdout_option)
break;
- if (unlink_first_option)
- remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option);
+ if (!unlink_destination (CURRENT_FILE_NAME))
+ break;
again_link:
{
current_stat.st_mode |= S_IFBLK;
#endif
-#if defined(S_IFCHR) || defined(S_IFBLK)
+#if S_IFCHR || S_IFBLK
make_node:
if (to_stdout_option)
break;
- if (unlink_first_option)
- remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option);
+ if (!unlink_destination (CURRENT_FILE_NAME))
+ break;
status = mknod (CURRENT_FILE_NAME, current_stat.st_mode,
current_stat.st_rdev);
break;
#endif
-#ifdef S_ISFIFO
+#if HAVE_MKFIFO || defined mkfifo
case FIFOTYPE:
if (to_stdout_option)
break;
- if (unlink_first_option)
- remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option);
+ if (!unlink_destination (CURRENT_FILE_NAME))
+ break;
while (status = mkfifo (CURRENT_FILE_NAME, current_stat.st_mode),
status != 0)
gnu_restore (skipcrud);
}
- else if (current_header->header.typeflag == GNUTYPE_DUMPDIR)
+ else if (typeflag == GNUTYPE_DUMPDIR)
skip_file (current_stat.st_size);
if (to_stdout_option)
again_dir:
status = mkdir (CURRENT_FILE_NAME,
- (we_are_root ? 0 : 0300) | current_stat.st_mode);
+ ((we_are_root ? 0 : MODE_WXUSR)
+ | current_stat.st_mode));
if (status != 0)
{
/* If the directory creation fails, let's consider immediately the
}
check_perms:
- if (!we_are_root && 0300 != (0300 & current_stat.st_mode))
+ if (!we_are_root && MODE_WXUSR != (MODE_WXUSR & current_stat.st_mode))
{
- current_stat.st_mode |= 0300;
+ current_stat.st_mode |= MODE_WXUSR;
WARN ((0, 0, _("Added write and execute permission to directory %s"),
CURRENT_FILE_NAME));
}
default:
WARN ((0, 0,
_("Unknown file type '%c' for %s, extracted as normal file"),
- current_header->header.typeflag, CURRENT_FILE_NAME));
+ typeflag, CURRENT_FILE_NAME));
goto again_file;
}