if (archive_names == 0)
FATAL_ERROR ((0, 0, _("No archive name given")));
- current_file_name = 0;
- current_link_name = 0;
+ destroy_stat (¤t_stat_info);
save_name = 0;
real_s_name = 0;
else
strcpy (record_start->header.name, volume_label_option);
- assign_string (¤t_file_name, record_start->header.name);
- current_trailing_slash = strip_trailing_slashes (current_file_name);
+ assign_string (¤t_stat_info.file_name, record_start->header.name);
+ current_stat_info.had_trailing_slash = strip_trailing_slashes (current_stat_info.file_name);
record_start->header.typeflag = GNUTYPE_VOLHDR;
TIME_TO_CHARS (start_time, record_start->header.mtime);
}
#endif /* !MSDOS */
- if (current_file_name)
- free (current_file_name);
- if (current_link_name)
- free (current_link_name);
+ destroy_stat (¤t_stat_info);
if (save_name)
free (save_name);
if (real_s_name)
/* Diff files from a tar archive.
- Copyright (C) 1988, 92, 93, 94, 96, 97 Free Software Foundation, Inc.
+
+ Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
+ 2003 Free Software Foundation, Inc.
+
Written by John Gilmore, on 1987-04-30.
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"
+#if HAVE_UTIME_H
+# include <utime.h>
+#else
+struct utimbuf
+ {
+ long actime;
+ long modtime;
+ };
+#endif
+
#if HAVE_LINUX_FD_H
# include <linux/fd.h>
#endif
+#include <quotearg.h>
+
#include "common.h"
#include "rmt.h"
#define MESSAGE_BUFFER_SIZE 100
/* Nonzero if we are verifying at the moment. */
-int now_verifying = 0;
+bool now_verifying;
/* File descriptor for the file we are diffing. */
static int diff_handle;
/* Area for reading file contents into. */
-static char *diff_buffer = NULL;
-
-/*--------------------------------.
-| Initialize for a diff operation |
-`--------------------------------*/
+static char *diff_buffer;
+/* Initialize for a diff operation. */
void
diff_init (void)
{
- diff_buffer = (char *) valloc ((unsigned) record_size);
+ diff_buffer = valloc (record_size);
if (!diff_buffer)
- FATAL_ERROR ((0, 0,
- _("Could not allocate memory for diff buffer of %d bytes"),
- record_size));
+ xalloc_die ();
}
-/*------------------------------------------------------------------------.
-| Sigh about something that differs by writing a MESSAGE to stdlis, given |
-| MESSAGE is not NULL. Also set the exit status if not already. |
-`------------------------------------------------------------------------*/
-
+/* Sigh about something that differs by writing a MESSAGE to stdlis,
+ given MESSAGE is nonzero. Also set the exit status if not already. */
static void
report_difference (const char *message)
{
if (message)
- fprintf (stdlis, "%s: %s\n", current_file_name, message);
+ fprintf (stdlis, "%s: %s\n", quotearg_colon (current_stat_info.file_name), message);
if (exit_status == TAREXIT_SUCCESS)
exit_status = TAREXIT_DIFFERS;
}
-/*-----------------------------------------------------------------------.
-| Takes a buffer returned by read_and_process and does nothing with it. |
-`-----------------------------------------------------------------------*/
-
-/* Yes, I know. SIZE and DATA are unused in this function. Some compilers
- may even report it. That's OK, just relax! */
-
+/* Take a buffer returned by read_and_process and do nothing with it. */
static int
-process_noop (long size, char *data)
+process_noop (size_t size, char *data)
{
+ /* Yes, I know. SIZE and DATA are unused in this function. Some
+ compilers may even report it. That's OK, just relax! */
return 1;
}
-/*---.
-| ? |
-`---*/
-
static int
-process_rawdata (long bytes, char *buffer)
+process_rawdata (size_t bytes, char *buffer)
{
- int status = read (diff_handle, diff_buffer, (size_t) bytes);
+ ssize_t status = safe_read (diff_handle, diff_buffer, bytes);
char message[MESSAGE_BUFFER_SIZE];
if (status != bytes)
{
if (status < 0)
{
- WARN ((0, errno, _("Cannot read %s"), current_file_name));
- report_difference (NULL);
+ read_error (current_stat_info.file_name);
+ report_difference (0);
}
else
{
- sprintf (message, _("Could only read %d of %ld bytes"),
- status, bytes);
+ sprintf (message, _("Could only read %lu of %lu bytes"),
+ (unsigned long) status, (unsigned long) bytes);
report_difference (message);
}
return 0;
}
- if (memcmp (buffer, diff_buffer, (size_t) bytes))
+ if (memcmp (buffer, diff_buffer, bytes))
{
- report_difference (_("Data differs"));
+ report_difference (_("Contents differ"));
return 0;
}
return 1;
}
-/*---.
-| ? |
-`---*/
-
/* Directory contents, only for GNUTYPE_DUMPDIR. */
static char *dumpdir_cursor;
static int
-process_dumpdir (long bytes, char *buffer)
+process_dumpdir (size_t bytes, char *buffer)
{
- if (memcmp (buffer, dumpdir_cursor, (size_t) bytes))
+ if (memcmp (buffer, dumpdir_cursor, bytes))
{
- report_difference (_("Data differs"));
+ report_difference (_("Contents differ"));
return 0;
}
return 1;
}
-/*------------------------------------------------------------------------.
-| Some other routine wants SIZE bytes in the archive. For each chunk of |
-| the archive, call PROCESSOR with the size of the chunk, and the address |
-| of the chunk it can work with. The PROCESSOR should return nonzero for |
-| success. It it return error once, continue skipping without calling |
-| PROCESSOR anymore. |
-`------------------------------------------------------------------------*/
-
+/* Some other routine wants SIZE bytes in the archive. For each chunk
+ of the archive, call PROCESSOR with the size of the chunk, and the
+ address of the chunk it can work with. The PROCESSOR should return
+ nonzero for success. It it return error once, continue skipping
+ without calling PROCESSOR anymore. */
static void
-read_and_process (long size, int (*processor) (long, char *))
+read_and_process (off_t size, int (*processor) (size_t, char *))
{
union block *data_block;
- long data_size;
+ size_t data_size;
if (multi_volume_option)
save_sizeleft = size;
while (size)
{
data_block = find_next_block ();
- if (data_block == NULL)
+ if (! data_block)
{
- ERROR ((0, 0, _("Unexpected EOF on archive file")));
+ ERROR ((0, 0, _("Unexpected EOF in archive")));
return;
}
}
}
-/*---.
-| ? |
-`---*/
-
-/* JK This routine should be used more often than it is ... look into
- that. Anyhow, what it does is translate the sparse information on the
- header, and in any subsequent extended headers, into an array of
- structures with true numbers, as opposed to character strings. It
- simply makes our life much easier, doing so many comparisong and such.
- */
-
-static void
-fill_in_sparse_array (void)
-{
- int counter;
-
- /* Allocate space for our scratch space; it's initially 10 elements
- long, but can change in this routine if necessary. */
-
- sp_array_size = 10;
- sparsearray = (struct sp_array *) xmalloc (sp_array_size * sizeof (struct sp_array));
-
- /* There are at most five of these structures in the header itself;
- read these in first. */
-
- for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++)
- {
- /* Compare to 0, or use !(int)..., for Pyramid's dumb compiler. */
- if (current_header->oldgnu_header.sp[counter].numbytes == 0)
- break;
-
- 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 the header's extended, we gotta read in exhdr's till we're done. */
-
- if (current_header->oldgnu_header.isextended)
- {
- /* How far into the sparsearray we are `so far'. */
- static int so_far_ind = SPARSES_IN_OLDGNU_HEADER;
- union block *exhdr;
-
- while (1)
- {
- exhdr = find_next_block ();
- for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++)
- {
- if (counter + so_far_ind > sp_array_size - 1)
- {
- /* We just ran out of room in our scratch area -
- realloc it. */
-
- sp_array_size *= 2;
- sparsearray = (struct sp_array *)
- xrealloc (sparsearray,
- sp_array_size * sizeof (struct sp_array));
- }
-
- /* Convert the character strings into longs. */
-
- sparsearray[counter + so_far_ind].offset =
- from_oct (1 + 12, exhdr->sparse_header.sp[counter].offset);
- sparsearray[counter + so_far_ind].numbytes =
- from_oct (1 + 12, exhdr->sparse_header.sp[counter].numbytes);
- }
-
- /* If this is the last extended header for this file, we can
- stop. */
-
- if (!exhdr->sparse_header.isextended)
- break;
-
- so_far_ind += SPARSES_IN_SPARSE_HEADER;
- set_next_block_after (exhdr);
- }
-
- /* Be sure to skip past the last one. */
-
- set_next_block_after (exhdr);
- }
-}
-
-/*---.
-| ? |
-`---*/
-
/* JK Diff'ing a sparse file with its counterpart on the tar file is a
bit of a different story than a normal file. First, we must know what
- areas of the file to skip through, i.e., we need to contruct a
+ areas of the file to skip through, i.e., we need to construct a
sparsearray, which will hold all the information we need. We must
compare small amounts of data at a time as we find it. */
I'm not sure overall identical sparsity is verified. */
static void
-diff_sparse_files (int size_of_file)
+diff_sparse_files (void)
{
- int remaining_size = size_of_file;
- char *buffer = (char *) xmalloc (BLOCKSIZE * sizeof (char));
- int buffer_size = BLOCKSIZE;
- union block *data_block = NULL;
+ off_t remaining_size = current_stat_info.stat.st_size;
+ char *buffer = xmalloc (BLOCKSIZE * sizeof (char));
+ size_t buffer_size = BLOCKSIZE;
+ union block *data_block = 0;
int counter = 0;
int different = 0;
- fill_in_sparse_array ();
+ if (! fill_in_sparse_array ())
+ fatal_exit ();
while (remaining_size > 0)
{
- int status;
- long chunk_size;
+ ssize_t status;
+ size_t chunk_size;
+ off_t offset;
+
#if 0
- int amount_read = 0;
+ off_t amount_read = 0;
#endif
data_block = find_next_block ();
+ if (!data_block)
+ FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
chunk_size = sparsearray[counter].numbytes;
if (!chunk_size)
break;
- lseek (diff_handle, sparsearray[counter].offset, 0);
+ offset = sparsearray[counter].offset;
+ if (lseek (diff_handle, offset, SEEK_SET) < 0)
+ {
+ seek_error_details (current_stat_info.file_name, offset);
+ report_difference (0);
+ }
/* Take care to not run out of room in our buffer. */
while (buffer_size < chunk_size)
{
+ if (buffer_size * 2 < buffer_size)
+ xalloc_die ();
buffer_size *= 2;
- buffer = (char *) xrealloc (buffer, buffer_size * sizeof (char));
+ buffer = xrealloc (buffer, buffer_size * sizeof (char));
}
while (chunk_size > BLOCKSIZE)
{
- if (status = read (diff_handle, buffer, BLOCKSIZE),
+ if (status = safe_read (diff_handle, buffer, BLOCKSIZE),
status != BLOCKSIZE)
{
if (status < 0)
{
- WARN ((0, errno, _("Cannot read %s"), current_file_name));
- report_difference (NULL);
+ read_error (current_stat_info.file_name);
+ report_difference (0);
}
else
{
char message[MESSAGE_BUFFER_SIZE];
- sprintf (message, _("Could only read %d of %ld bytes"),
- status, chunk_size);
+ sprintf (message, _("Could only read %lu of %lu bytes"),
+ (unsigned long) status, (unsigned long) chunk_size);
report_difference (message);
}
break;
remaining_size -= status;
set_next_block_after (data_block);
data_block = find_next_block ();
+ if (!data_block)
+ FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
}
- if (status = read (diff_handle, buffer, (size_t) chunk_size),
+ if (status = safe_read (diff_handle, buffer, chunk_size),
status != chunk_size)
{
if (status < 0)
{
- WARN ((0, errno, _("Cannot read %s"), current_file_name));
- report_difference (NULL);
+ read_error (current_stat_info.file_name);
+ report_difference (0);
}
else
{
char message[MESSAGE_BUFFER_SIZE];
- sprintf (message, _("Could only read %d of %ld bytes"),
- status, chunk_size);
+ sprintf (message, _("Could only read %lu of %lu bytes"),
+ (unsigned long) status, (unsigned long) chunk_size);
report_difference (message);
}
break;
}
- if (memcmp (buffer, data_block->buffer, (size_t) chunk_size))
+ if (memcmp (buffer, data_block->buffer, chunk_size))
{
different = 1;
break;
amount_read = 0;
set_next_block_after (data_block);
data_block = find_next_block ();
+ if (!data_block)
+ FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
}
#endif
set_next_block_after (data_block);
/* If the number of bytes read isn't the number of bytes supposedly in
the file, they're different. */
- if (amount_read != size_of_file)
+ if (amount_read != current_stat_info.stat.st_size)
different = 1;
#endif
free (sparsearray);
if (different)
- report_difference (_("Data differs"));
+ report_difference (_("Contents differ"));
}
-/*---------------------------------------------------------------------.
-| Call either stat or lstat over STAT_DATA, depending on --dereference |
-| (-h), for a file which should exist. Diagnose any problem. Return |
-| nonzero for success, zero otherwise. |
-`---------------------------------------------------------------------*/
-
+/* Call either stat or lstat over STAT_DATA, depending on
+ --dereference (-h), for a file which should exist. Diagnose any
+ problem. Return nonzero for success, zero otherwise. */
static int
-get_stat_data (struct stat *stat_data)
+get_stat_data (char const *file_name, struct stat *stat_data)
{
- int status = (dereference_option
- ? stat (current_file_name, stat_data)
- : lstat (current_file_name, stat_data));
+ int status = deref_stat (dereference_option, file_name, stat_data);
- if (status < 0)
+ if (status != 0)
{
if (errno == ENOENT)
- report_difference (_("File does not exist"));
+ stat_warn (file_name);
else
- {
- ERROR ((0, errno, _("Cannot stat file %s"), current_file_name));
- report_difference (NULL);
- }
-#if 0
- skip_file ((long) current_stat.st_size);
-#endif
+ stat_error (file_name);
+ report_difference (0);
return 0;
}
return 1;
}
-/*----------------------------------.
-| Diff a file against the archive. |
-`----------------------------------*/
-
+/* Diff a file against the archive. */
void
diff_archive (void)
{
struct stat stat_data;
- int name_length;
int status;
-
- errno = EPIPE; /* FIXME: errno should be read-only */
- /* FIXME: remove perrors */
+ struct utimbuf restore_times;
set_next_block_after (current_header);
- decode_header (current_header, ¤t_stat, ¤t_format, 1);
+ decode_header (current_header, ¤t_stat_info, ¤t_format, 1);
- /* Print the block from `current_header' and `current_stat'. */
+ /* Print the block from current_header and current_stat_info. */
if (verbose_option)
{
if (now_verifying)
fprintf (stdlis, _("Verify "));
- print_header ();
+ print_header (-1);
}
switch (current_header->header.typeflag)
{
default:
- WARN ((0, 0, _("Unknown file type '%c' for %s, diffed as normal file"),
- current_header->header.typeflag, current_file_name));
+ ERROR ((0, 0, _("%s: Unknown file type '%c', diffed as normal file"),
+ quotearg_colon (current_stat_info.file_name),
+ current_header->header.typeflag));
/* Fall through. */
case AREGTYPE:
/* Appears to be a file. See if it's really a directory. */
- name_length = strlen (current_file_name) - 1;
- if (current_file_name[name_length] == '/')
+ if (current_stat_info.had_trailing_slash)
goto really_dir;
- if (!get_stat_data (&stat_data))
+ if (!get_stat_data (current_stat_info.file_name, &stat_data))
{
- if (current_header->oldgnu_header.isextended)
- skip_extended_headers ();
- skip_file ((long) current_stat.st_size);
+ skip_member ();
goto quit;
}
if (!S_ISREG (stat_data.st_mode))
{
- report_difference (_("Not a regular file"));
- skip_file ((long) current_stat.st_size);
+ report_difference (_("File type differs"));
+ skip_member ();
goto quit;
}
- stat_data.st_mode &= 07777;
- if (stat_data.st_mode != current_stat.st_mode)
+ if ((current_stat_info.stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
report_difference (_("Mode differs"));
#if !MSDOS
/* stat() in djgpp's C library gives a constant number of 42 as the
uid and gid of a file. So, comparing an FTP'ed archive just after
unpack would fail on MSDOS. */
- if (stat_data.st_uid != current_stat.st_uid)
+ if (stat_data.st_uid != current_stat_info.stat.st_uid)
report_difference (_("Uid differs"));
- if (stat_data.st_gid != current_stat.st_gid)
+ if (stat_data.st_gid != current_stat_info.stat.st_gid)
report_difference (_("Gid differs"));
#endif
- if (stat_data.st_mtime != current_stat.st_mtime)
+ if (stat_data.st_mtime != current_stat_info.stat.st_mtime)
report_difference (_("Mod time differs"));
if (current_header->header.typeflag != GNUTYPE_SPARSE &&
- stat_data.st_size != current_stat.st_size)
+ stat_data.st_size != current_stat_info.stat.st_size)
{
report_difference (_("Size differs"));
- skip_file ((long) current_stat.st_size);
+ skip_member ();
goto quit;
}
- diff_handle = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
+ diff_handle = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
- if (diff_handle < 0 && !absolute_names_option)
- {
- char *tmpbuf = xmalloc (strlen (current_file_name) + 2);
-
- *tmpbuf = '/';
- strcpy (tmpbuf + 1, current_file_name);
- diff_handle = open (tmpbuf, O_NDELAY | O_RDONLY);
- free (tmpbuf);
- }
if (diff_handle < 0)
{
- ERROR ((0, errno, _("Cannot open %s"), current_file_name));
- if (current_header->oldgnu_header.isextended)
- skip_extended_headers ();
- skip_file ((long) current_stat.st_size);
- report_difference (NULL);
+ open_error (current_stat_info.file_name);
+ skip_member ();
+ report_difference (0);
goto quit;
}
+ restore_times.actime = stat_data.st_atime;
+ restore_times.modtime = stat_data.st_mtime;
+
/* Need to treat sparse files completely differently here. */
if (current_header->header.typeflag == GNUTYPE_SPARSE)
- diff_sparse_files (current_stat.st_size);
+ diff_sparse_files ();
else
{
if (multi_volume_option)
{
- assign_string (&save_name, current_file_name);
- save_totsize = current_stat.st_size;
+ assign_string (&save_name, current_stat_info.file_name);
+ save_totsize = current_stat_info.stat.st_size;
/* save_sizeleft is set in read_and_process. */
}
- read_and_process ((long) (current_stat.st_size), process_rawdata);
+ read_and_process (current_stat_info.stat.st_size, process_rawdata);
if (multi_volume_option)
- assign_string (&save_name, NULL);
+ assign_string (&save_name, 0);
}
status = close (diff_handle);
- if (status < 0)
- ERROR ((0, errno, _("Error while closing %s"), current_file_name));
+ if (status != 0)
+ close_error (current_stat_info.file_name);
+
+ if (atime_preserve_option)
+ utime (current_stat_info.file_name, &restore_times);
quit:
break;
#if !MSDOS
case LNKTYPE:
{
- dev_t dev;
- ino_t ino;
+ struct stat link_data;
- if (!get_stat_data (&stat_data))
+ if (!get_stat_data (current_stat_info.file_name, &stat_data))
+ break;
+ if (!get_stat_data (current_stat_info.link_name, &link_data))
break;
- dev = stat_data.st_dev;
- ino = stat_data.st_ino;
- status = stat (current_link_name, &stat_data);
- if (status < 0)
- {
- if (errno == ENOENT)
- report_difference (_("Does not exist"));
- else
- {
- WARN ((0, errno, _("Cannot stat file %s"), current_file_name));
- report_difference (NULL);
- }
- break;
- }
-
- if (stat_data.st_dev != dev || stat_data.st_ino != ino)
+ if (stat_data.st_dev != link_data.st_dev
+ || stat_data.st_ino != link_data.st_ino)
{
- char *message = (char *)
- xmalloc (MESSAGE_BUFFER_SIZE + strlen (current_link_name));
+ char *message =
+ xmalloc (MESSAGE_BUFFER_SIZE + 4 * strlen (current_stat_info.link_name));
- sprintf (message, _("Not linked to %s"), current_link_name);
+ sprintf (message, _("Not linked to %s"),
+ quote (current_stat_info.link_name));
report_difference (message);
free (message);
break;
}
#endif /* not MSDOS */
-#ifdef S_ISLNK
+#ifdef HAVE_READLINK
case SYMTYPE:
{
- char linkbuf[NAME_FIELD_SIZE + 3]; /* FIXME: may be too short. */
+ size_t len = strlen (current_stat_info.link_name);
+ char *linkbuf = alloca (len + 1);
- status = readlink (current_file_name, linkbuf, (sizeof linkbuf) - 1);
+ status = readlink (current_stat_info.file_name, linkbuf, len + 1);
if (status < 0)
{
if (errno == ENOENT)
- report_difference (_("No such file or directory"));
+ readlink_warn (current_stat_info.file_name);
else
- {
- WARN ((0, errno, _("Cannot read link %s"), current_file_name));
- report_difference (NULL);
- }
- break;
+ readlink_error (current_stat_info.file_name);
+ report_difference (0);
}
-
- linkbuf[status] = '\0'; /* null-terminate it */
- if (strncmp (current_link_name, linkbuf, (size_t) status) != 0)
+ else if (status != len
+ || strncmp (current_stat_info.link_name, linkbuf, len) != 0)
report_difference (_("Symlink differs"));
break;
}
-#endif /* not S_ISLNK */
+#endif
-#ifdef S_IFCHR
case CHRTYPE:
- current_stat.st_mode |= S_IFCHR;
- goto check_node;
-#endif /* not S_IFCHR */
-
-#ifdef S_IFBLK
- /* If local system doesn't support block devices, use default case. */
-
case BLKTYPE:
- current_stat.st_mode |= S_IFBLK;
- goto check_node;
-#endif /* not S_IFBLK */
-
-#ifdef S_ISFIFO
- /* If local system doesn't support FIFOs, use default case. */
-
case FIFOTYPE:
-# ifdef S_IFIFO
- current_stat.st_mode |= S_IFIFO;
-# endif
- current_stat.st_rdev = 0; /* FIXME: do we need this? */
- goto check_node;
-#endif /* S_ISFIFO */
-
- check_node:
+
/* FIXME: deal with umask. */
- if (!get_stat_data (&stat_data))
+ if (!get_stat_data (current_stat_info.file_name, &stat_data))
break;
- if (current_stat.st_rdev != stat_data.st_rdev)
+ if (current_header->header.typeflag == CHRTYPE
+ ? !S_ISCHR (stat_data.st_mode)
+ : current_header->header.typeflag == BLKTYPE
+ ? !S_ISBLK (stat_data.st_mode)
+ : /* current_header->header.typeflag == FIFOTYPE */
+ !S_ISFIFO (stat_data.st_mode))
{
- report_difference (_("Device numbers changed"));
+ report_difference (_("File type differs"));
break;
}
- if (
-#ifdef S_IFMT
- current_stat.st_mode != stat_data.st_mode
-#else
- /* POSIX lossage. */
- (current_stat.st_mode & 07777) != (stat_data.st_mode & 07777)
-#endif
- )
+ if ((current_header->header.typeflag == CHRTYPE
+ || current_header->header.typeflag == BLKTYPE)
+ && current_stat_info.stat.st_rdev != stat_data.st_rdev)
+ {
+ report_difference (_("Device number differs"));
+ break;
+ }
+
+ if ((current_stat_info.stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
{
- report_difference (_("Mode or device-type changed"));
+ report_difference (_("Mode differs"));
break;
}
case GNUTYPE_DUMPDIR:
{
- char *dumpdir_buffer = get_directory_contents (current_file_name, 0);
+ char *dumpdir_buffer = get_directory_contents (current_stat_info.file_name, 0);
if (multi_volume_option)
{
- assign_string (&save_name, current_file_name);
- save_totsize = current_stat.st_size;
+ assign_string (&save_name, current_stat_info.file_name);
+ save_totsize = current_stat_info.stat.st_size;
/* save_sizeleft is set in read_and_process. */
}
if (dumpdir_buffer)
{
dumpdir_cursor = dumpdir_buffer;
- read_and_process ((long) (current_stat.st_size), process_dumpdir);
+ read_and_process (current_stat_info.stat.st_size, process_dumpdir);
free (dumpdir_buffer);
}
else
- read_and_process ((long) (current_stat.st_size), process_noop);
+ read_and_process (current_stat_info.stat.st_size, process_noop);
if (multi_volume_option)
- assign_string (&save_name, NULL);
+ assign_string (&save_name, 0);
/* Fall through. */
}
case DIRTYPE:
- /* Check for trailing /. */
-
- name_length = strlen (current_file_name) - 1;
-
really_dir:
- while (name_length && current_file_name[name_length] == '/')
- current_file_name[name_length--] = '\0'; /* zap / */
-
- if (!get_stat_data (&stat_data))
+ if (!get_stat_data (current_stat_info.file_name, &stat_data))
break;
if (!S_ISDIR (stat_data.st_mode))
{
- report_difference (_("No longer a directory"));
+ report_difference (_("File type differs"));
+ break;
+ }
+
+ if ((current_stat_info.stat.st_mode & MODE_ALL) != (stat_data.st_mode & MODE_ALL))
+ {
+ report_difference (_("Mode differs"));
break;
}
- if ((stat_data.st_mode & 07777) != (current_stat.st_mode & 07777))
- report_difference (_("Mode differs"));
break;
case GNUTYPE_VOLHDR:
{
off_t offset;
- name_length = strlen (current_file_name) - 1;
- if (current_file_name[name_length] == '/')
+ if (current_stat_info.had_trailing_slash)
goto really_dir;
- if (!get_stat_data (&stat_data))
+ if (!get_stat_data (current_stat_info.file_name, &stat_data))
break;
if (!S_ISREG (stat_data.st_mode))
{
- report_difference (_("Not a regular file"));
- skip_file ((long) current_stat.st_size);
+ report_difference (_("File type differs"));
+ skip_member ();
break;
}
- stat_data.st_mode &= 07777;
- offset = from_oct (1 + 12, current_header->oldgnu_header.offset);
- if (stat_data.st_size != current_stat.st_size + offset)
+ offset = OFF_FROM_HEADER (current_header->oldgnu_header.offset);
+ if (stat_data.st_size != current_stat_info.stat.st_size + offset)
{
report_difference (_("Size differs"));
- skip_file ((long) current_stat.st_size);
+ skip_member ();
break;
}
- diff_handle = open (current_file_name, O_NDELAY | O_RDONLY | O_BINARY);
+ diff_handle = open (current_stat_info.file_name, O_RDONLY | O_BINARY);
if (diff_handle < 0)
{
- WARN ((0, errno, _("Cannot open file %s"), current_file_name));
- report_difference (NULL);
- skip_file ((long) current_stat.st_size);
+ open_error (current_stat_info.file_name);
+ report_difference (0);
+ skip_member ();
break;
}
- status = lseek (diff_handle, offset, 0);
- if (status != offset)
+ if (lseek (diff_handle, offset, SEEK_SET) < 0)
{
- WARN ((0, errno, _("Cannot seek to %ld in file %s"),
- offset, current_file_name));
- report_difference (NULL);
+ seek_error_details (current_stat_info.file_name, offset);
+ report_difference (0);
break;
}
if (multi_volume_option)
{
- assign_string (&save_name, current_file_name);
+ assign_string (&save_name, current_stat_info.file_name);
save_totsize = stat_data.st_size;
/* save_sizeleft is set in read_and_process. */
}
- read_and_process ((long) (current_stat.st_size), process_rawdata);
+ read_and_process (current_stat_info.stat.st_size, process_rawdata);
if (multi_volume_option)
- assign_string (&save_name, NULL);
+ assign_string (&save_name, 0);
status = close (diff_handle);
- if (status < 0)
- ERROR ((0, errno, _("Error while closing %s"), current_file_name));
+ if (status != 0)
+ close_error (current_stat_info.file_name);
break;
}
}
}
-/*---.
-| ? |
-`---*/
-
void
verify_volume (void)
{
status < 0))
{
#endif
- if (rmtlseek (archive, 0L, 0) != 0)
+ if (rmtlseek (archive, (off_t) 0, SEEK_SET) != 0)
{
/* Lseek failed. Try a different method. */
-
- WARN ((0, errno,
- _("Could not rewind archive file for verify")));
+ seek_warn (archive_name_array[0]);
return;
}
#ifdef MTIOCTOP
flush_read ();
while (1)
{
- enum read_header status = read_header ();
+ enum read_header status = read_header (0);
if (status == HEADER_FAILURE)
{
int counter = 0;
- while (status == HEADER_FAILURE);
+ do
{
counter++;
- status = read_header ();
+ status = read_header (0);
}
+ while (status == HEADER_FAILURE);
+
ERROR ((0, 0,
_("VERIFY FAILURE: %d invalid header(s) detected"), counter));
}
/* FIXME: Cross recursion between start_header and write_long! */
-static union block *start_header (const char *, struct stat *);
+static union block *start_header (const char *, struct tar_stat_info *);
static void
write_long (const char *p, char type)
size_t size = strlen (p) + 1;
size_t bufsize;
union block *header;
- struct stat foo;
+ struct tar_stat_info foo;
memset (&foo, 0, sizeof foo);
- foo.st_size = size;
+ foo.stat.st_size = size;
header = start_header ("././@LongLink", &foo);
header->header.typeflag = type;
and return its address. */
static union block *
-start_header (const char *name, struct stat *st)
+start_header (const char *name, struct tar_stat_info *st)
{
union block *header;
header = find_next_block ();
memset (header->buffer, 0, sizeof (union block));
- assign_string (¤t_file_name, name);
+ assign_string (¤t_stat_info.file_name, name);
strncpy (header->header.name, name, NAME_FIELD_SIZE);
header->header.name[NAME_FIELD_SIZE - 1] = '\0';
/* Override some stat fields, if requested to do so. */
if (owner_option != (uid_t) -1)
- st->st_uid = owner_option;
+ st->stat.st_uid = owner_option;
if (group_option != (gid_t) -1)
- st->st_gid = group_option;
+ st->stat.st_gid = group_option;
if (mode_option)
- st->st_mode = ((st->st_mode & ~MODE_ALL)
- | mode_adjust (st->st_mode, mode_option));
+ st->stat.st_mode = ((st->stat.st_mode & ~MODE_ALL)
+ | mode_adjust (st->stat.st_mode, mode_option));
/* Paul Eggert tried the trivial test ($WRITER cf a b; $READER tvf a)
for a few tars and came up with the following interoperability
acceptor for Paul's test. */
if (archive_format == V7_FORMAT)
- MODE_TO_CHARS (st->st_mode & MODE_ALL, header->header.mode);
+ MODE_TO_CHARS (st->stat.st_mode & MODE_ALL, header->header.mode);
else
- MODE_TO_CHARS (st->st_mode, header->header.mode);
+ MODE_TO_CHARS (st->stat.st_mode, header->header.mode);
- UID_TO_CHARS (st->st_uid, header->header.uid);
- GID_TO_CHARS (st->st_gid, header->header.gid);
- OFF_TO_CHARS (st->st_size, header->header.size);
- TIME_TO_CHARS (st->st_mtime, header->header.mtime);
+ UID_TO_CHARS (st->stat.st_uid, header->header.uid);
+ GID_TO_CHARS (st->stat.st_gid, header->header.gid);
+ OFF_TO_CHARS (st->stat.st_size, header->header.size);
+ TIME_TO_CHARS (st->stat.st_mtime, header->header.mtime);
MAJOR_TO_CHARS (0, header->header.devmajor);
MINOR_TO_CHARS (0, header->header.devminor);
if (incremental_option)
if (archive_format == OLDGNU_FORMAT)
{
- TIME_TO_CHARS (st->st_atime, header->oldgnu_header.atime);
- TIME_TO_CHARS (st->st_ctime, header->oldgnu_header.ctime);
+ TIME_TO_CHARS (st->stat.st_atime, header->oldgnu_header.atime);
+ TIME_TO_CHARS (st->stat.st_ctime, header->oldgnu_header.ctime);
}
header->header.typeflag = archive_format == V7_FORMAT ? AREGTYPE : REGTYPE;
}
else
{
- uid_to_uname (st->st_uid, header->header.uname);
- gid_to_gname (st->st_gid, header->header.gname);
+ uid_to_uname (st->stat.st_uid, header->header.uname);
+ gid_to_gname (st->stat.st_gid, header->header.gname);
}
return header;
/* These globals are parameters to print_header, sigh. */
current_header = header;
- /* current_stat is already set up. */
+ /* current_stat_info is already set up. */
current_format = archive_format;
print_header (block_ordinal);
}
of an incremental dump. PARENT_DEVICE is the device of P's
parent directory; it is examined only if TOP_LEVEL is zero.
- Set global CURRENT_STAT to stat output for this file. */
+ Set global CURRENT_STAT_INFO to stat output for this file. */
/* FIXME: One should make sure that for *every* path leading to setting
exit_status to failure, a clear diagnostic has been issued. */
if (interactive_option && !confirm ("add", p))
return;
- if (deref_stat (dereference_option, p, ¤t_stat) != 0)
+ if (deref_stat (dereference_option, p, ¤t_stat_info.stat) != 0)
{
if (ignore_failed_read_option)
stat_warn (p);
return;
}
- original_ctime = current_stat.st_ctime;
- restore_times.actime = current_stat.st_atime;
- restore_times.modtime = current_stat.st_mtime;
+ original_ctime = current_stat_info.stat.st_ctime;
+ restore_times.actime = current_stat_info.stat.st_atime;
+ restore_times.modtime = current_stat_info.stat.st_mtime;
#ifdef S_ISHIDDEN
- if (S_ISHIDDEN (current_stat.st_mode))
+ if (S_ISHIDDEN (current_stat_info.stat.st_mode))
{
char *new = (char *) alloca (strlen (p) + 2);
if (new)
put in the archive. */
if ((0 < top_level || !incremental_option)
- && !S_ISDIR (current_stat.st_mode)
- && current_stat.st_mtime < newer_mtime_option
- && (!after_date_option || current_stat.st_ctime < newer_ctime_option))
+ && !S_ISDIR (current_stat_info.stat.st_mode)
+ && current_stat_info.stat.st_mtime < newer_mtime_option
+ && (!after_date_option || current_stat_info.stat.st_ctime < newer_ctime_option))
{
if (0 < top_level)
WARN ((0, 0, _("%s: file is unchanged; not dumped"),
#if !MSDOS
/* See if we are trying to dump the archive. */
- if (ar_dev && current_stat.st_dev == ar_dev && current_stat.st_ino == ar_ino)
+ if (ar_dev && current_stat_info.stat.st_dev == ar_dev && current_stat_info.stat.st_ino == ar_ino)
{
WARN ((0, 0, _("%s: file is the archive; not dumped"),
quotearg_colon (p)));
}
#endif
- if (S_ISDIR (current_stat.st_mode))
+ if (S_ISDIR (current_stat_info.stat.st_mode))
{
char *directory;
char const *entry;
char *namebuf;
size_t buflen;
size_t len;
- dev_t our_device = current_stat.st_dev;
+ dev_t our_device = current_stat_info.stat.st_dev;
errno = 0;
directory blocks to be written even with old archives. */
block_ordinal = current_block_ordinal ();
- current_stat.st_size = 0; /* force 0 size on dir */
+ current_stat_info.stat.st_size = 0; /* force 0 size on dir */
/* FIXME: If people could really read standard archives, this
should be:
header
- = start_header (standard_option ? p : namebuf, ¤t_stat);
+ = start_header (standard_option ? p : namebuf, ¤t_stat_info);
but since they'd interpret DIRTYPE blocks as regular
files, we'd better put the / on the name. */
- header = start_header (namebuf, ¤t_stat);
+ header = start_header (namebuf, ¤t_stat_info);
if (incremental_option)
header->header.typeflag = GNUTYPE_DUMPDIR;
avoid doing so if the user only wants to dump one file system. */
if (one_file_system_option && !top_level
- && parent_device != current_stat.st_dev)
+ && parent_device != current_stat_info.stat.st_dev)
{
if (verbose_option)
WARN ((0, 0,
{
/* Check for multiple links. */
- if (1 < current_stat.st_nlink && link_table)
+ if (1 < current_stat_info.stat.st_nlink && link_table)
{
struct link lp;
struct link *dup;
- lp.ino = current_stat.st_ino;
- lp.dev = current_stat.st_dev;
+ lp.ino = current_stat_info.stat.st_ino;
+ lp.dev = current_stat_info.stat.st_dev;
if ((dup = hash_lookup (link_table, &lp)))
{
block_ordinal = current_block_ordinal ();
if (NAME_FIELD_SIZE <= strlen (link_name))
write_long (link_name, GNUTYPE_LONGLINK);
- assign_string (¤t_link_name, link_name);
+ assign_string (¤t_stat_info.link_name, link_name);
- current_stat.st_size = 0;
- header = start_header (p, ¤t_stat);
+ current_stat_info.stat.st_size = 0;
+ header = start_header (p, ¤t_stat_info);
strncpy (header->header.linkname, link_name, NAME_FIELD_SIZE);
/* Force null termination. */
/* This is not a link to a previously dumped file, so dump it. */
- if (S_ISREG (current_stat.st_mode)
- || S_ISCTG (current_stat.st_mode))
+ if (S_ISREG (current_stat_info.stat.st_mode)
+ || S_ISCTG (current_stat_info.stat.st_mode))
{
int f; /* file descriptor */
size_t bufsize;
st_blocks, so `du' and `ls -s' give wrong results. So, the
--sparse option would not work on a minix filesystem. */
- if (ST_NBLOCKS (current_stat)
- < (current_stat.st_size / ST_NBLOCKSIZE
- + (current_stat.st_size % ST_NBLOCKSIZE != 0)))
+ if (ST_NBLOCKS (current_stat_info.stat)
+ < (current_stat_info.stat.st_size / ST_NBLOCKSIZE
+ + (current_stat_info.stat.st_size % ST_NBLOCKSIZE != 0)))
{
int counter;
block_ordinal = current_block_ordinal ();
- header = start_header (p, ¤t_stat);
+ header = start_header (p, ¤t_stat_info);
header->header.typeflag = GNUTYPE_SPARSE;
header_moved = 1;
<file>. It might be kind of disconcerting if the
shrunken file size was the one that showed up. */
- OFF_TO_CHARS (current_stat.st_size,
+ OFF_TO_CHARS (current_stat_info.stat.st_size,
header->oldgnu_header.realsize);
/* This will be the new "size" of the file, i.e., the size
of the file minus the blocks of holes that we're
skipping over. */
- current_stat.st_size = find_new_file_size (sparses);
- OFF_TO_CHARS (current_stat.st_size, header->header.size);
+ current_stat_info.stat.st_size = find_new_file_size (sparses);
+ OFF_TO_CHARS (current_stat_info.stat.st_size, header->header.size);
for (counter = 0;
counter < sparses && counter < SPARSES_IN_OLDGNU_HEADER;
}
}
- sizeleft = current_stat.st_size;
+ sizeleft = current_stat_info.stat.st_size;
/* Don't bother opening empty, world readable files. Also do not open
files when archive is meant for /dev/null. */
if (dev_null_output
|| (sizeleft == 0
- && MODE_R == (MODE_R & current_stat.st_mode)))
+ && MODE_R == (MODE_R & current_stat_info.stat.st_mode)))
f = -1;
else
{
if (!header_moved)
{
block_ordinal = current_block_ordinal ();
- header = start_header (p, ¤t_stat);
+ header = start_header (p, ¤t_stat_info);
}
/* Mark contiguous files, if we support them. */
- if (archive_format != V7_FORMAT && S_ISCTG (current_stat.st_mode))
+ if (archive_format != V7_FORMAT && S_ISCTG (current_stat_info.stat.st_mode))
header->header.typeflag = CONTTYPE;
isextended = header->oldgnu_header.isextended;
{
if (f < 0
|| finish_sparse_file (f, &sizeleft,
- current_stat.st_size, p))
+ current_stat_info.stat.st_size, p))
goto padit;
}
else
{
assign_string (&save_name, p);
save_sizeleft = sizeleft;
- save_totsize = current_stat.st_size;
+ save_totsize = current_stat_info.stat.st_size;
}
start = find_next_block ();
(ignore_failed_read_option
? read_warn_details
: read_error_details)
- (p, current_stat.st_size - sizeleft, bufsize);
+ (p, current_stat_info.stat.st_size - sizeleft, bufsize);
goto padit;
}
sizeleft -= count;
goto file_was_dumped;
}
#ifdef HAVE_READLINK
- else if (S_ISLNK (current_stat.st_mode))
+ else if (S_ISLNK (current_stat_info.stat.st_mode))
{
char *buffer;
int size;
- size_t linklen = current_stat.st_size;
- if (linklen != current_stat.st_size || linklen + 1 == 0)
+ size_t linklen = current_stat_info.stat.st_size;
+ if (linklen != current_stat_info.stat.st_size || linklen + 1 == 0)
xalloc_die ();
buffer = (char *) alloca (linklen + 1);
size = readlink (p, buffer, linklen + 1);
buffer[size] = '\0';
if (size >= NAME_FIELD_SIZE)
write_long (buffer, GNUTYPE_LONGLINK);
- assign_string (¤t_link_name, buffer);
+ assign_string (¤t_stat_info.link_name, buffer);
block_ordinal = current_block_ordinal ();
- current_stat.st_size = 0; /* force 0 size on symlink */
- header = start_header (p, ¤t_stat);
+ current_stat_info.stat.st_size = 0; /* force 0 size on symlink */
+ header = start_header (p, ¤t_stat_info);
strncpy (header->header.linkname, buffer, NAME_FIELD_SIZE);
header->header.linkname[NAME_FIELD_SIZE - 1] = '\0';
header->header.typeflag = SYMTYPE;
goto file_was_dumped;
}
#endif
- else if (S_ISCHR (current_stat.st_mode))
+ else if (S_ISCHR (current_stat_info.stat.st_mode))
type = CHRTYPE;
- else if (S_ISBLK (current_stat.st_mode))
+ else if (S_ISBLK (current_stat_info.stat.st_mode))
type = BLKTYPE;
- else if (S_ISFIFO (current_stat.st_mode))
+ else if (S_ISFIFO (current_stat_info.stat.st_mode))
type = FIFOTYPE;
- else if (S_ISSOCK (current_stat.st_mode))
+ else if (S_ISSOCK (current_stat_info.stat.st_mode))
{
WARN ((0, 0, _("%s: socket ignored"), quotearg_colon (p)));
return;
}
- else if (S_ISDOOR (current_stat.st_mode))
+ else if (S_ISDOOR (current_stat_info.stat.st_mode))
{
WARN ((0, 0, _("%s: door ignored"), quotearg_colon (p)));
return;
goto unknown;
block_ordinal = current_block_ordinal ();
- current_stat.st_size = 0; /* force 0 size */
- header = start_header (p, ¤t_stat);
+ current_stat_info.stat.st_size = 0; /* force 0 size */
+ header = start_header (p, ¤t_stat_info);
header->header.typeflag = type;
if (type != FIFOTYPE)
{
- MAJOR_TO_CHARS (major (current_stat.st_rdev), header->header.devmajor);
- MINOR_TO_CHARS (minor (current_stat.st_rdev), header->header.devminor);
+ MAJOR_TO_CHARS (major (current_stat_info.stat.st_rdev), header->header.devmajor);
+ MINOR_TO_CHARS (minor (current_stat_info.stat.st_rdev), header->header.devminor);
}
finish_header (header, block_ordinal);
return;
file_was_dumped:
- if (1 < current_stat.st_nlink)
+ if (1 < current_stat_info.stat.st_nlink)
{
struct link *dup;
struct link *lp = xmalloc (offsetof (struct link, name)
+ strlen (p) + 1);
- lp->ino = current_stat.st_ino;
- lp->dev = current_stat.st_dev;
- lp->nlink = current_stat.st_nlink;
+ lp->ino = current_stat_info.stat.st_ino;
+ lp->dev = current_stat_info.stat.st_dev;
+ lp->nlink = current_stat_info.stat.st_nlink;
strcpy (lp->name, p);
if (! ((link_table
/* Delete entries from a tar archive.
- Copyright (C) 1988, 1992, 1994, 1996, 1997 Free Software Foundation, Inc.
+
+ Copyright (C) 1988, 1992, 1994, 1996, 1997, 2000, 2001, 2003 Free
+ Software Foundation, Inc.
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
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"
-#define STDIN 0
-#define STDOUT 1
-
#include "common.h"
#include "rmt.h"
-static union block *new_record = NULL;
-static union block *save_record = NULL;
-static int records_read = 0;
-static int new_blocks = 0;
-static int blocks_needed = 0;
+static union block *new_record;
+static int new_blocks;
+static bool acting_as_filter;
-/* FIXME: This module should not directly handle the following three
- variables, instead, this should be done in buffer.c only. */
+/* FIXME: This module should not directly handle the following
+ variables, instead, the interface should be cleaned up. */
extern union block *record_start;
extern union block *record_end;
extern union block *current_block;
-
-/*-------------------------------------------------------------------------.
-| Move archive descriptor by COUNT records worth. If COUNT is positive we |
-| move forward, else we move negative. If its a tape, MTIOCTOP had better |
-| work. If its something else, we try to seek on it. If we can't seek, |
-| we loose! |
-`-------------------------------------------------------------------------*/
-
+extern union block *recent_long_name;
+extern union block *recent_long_link;
+extern off_t records_read;
+extern off_t records_written;
+
+/* The number of records skipped at the start of the archive, when
+ passing over members that are not deleted. */
+static off_t records_skipped;
+
+/* Move archive descriptor by COUNT records worth. If COUNT is
+ positive we move forward, else we move negative. If it's a tape,
+ MTIOCTOP had better work. If it's something else, we try to seek
+ on it. If we can't seek, we lose! */
static void
-move_archive (int count)
+move_archive (off_t count)
{
+ if (count == 0)
+ return;
+
#ifdef MTIOCTOP
{
struct mtop operation;
- int status;
- if (count > 0)
- {
- operation.mt_op = MTFSR;
- operation.mt_count = count;
- }
- else
+ if (count < 0
+ ? (operation.mt_op = MTBSR,
+ operation.mt_count = -count,
+ operation.mt_count == -count)
+ : (operation.mt_op = MTFSR,
+ operation.mt_count = count,
+ operation.mt_count == count))
{
- operation.mt_op = MTBSR;
- operation.mt_count = -count;
- }
-
- if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
- status >= 0)
- return;
+ if (0 <= rmtioctl (archive, MTIOCTOP, (char *) &operation))
+ return;
- if (errno == EIO)
- if (status = rmtioctl (archive, MTIOCTOP, (char *) &operation),
- status >= 0)
- return;
+ if (errno == EIO
+ && 0 <= rmtioctl (archive, MTIOCTOP, (char *) &operation))
+ return;
+ }
}
#endif /* MTIOCTOP */
{
- off_t position = rmtlseek (archive, 0L, 1);
-
- position += record_size * count;
+ off_t position0 = rmtlseek (archive, (off_t) 0, SEEK_CUR);
+ off_t increment = record_size * (off_t) count;
+ off_t position = position0 + increment;
- if (rmtlseek (archive, position, 0) != position)
- FATAL_ERROR ((0, 0, _("Could not re-position archive file")));
+ if (increment / count != record_size
+ || (position < position0) != (increment < 0)
+ || (position = position < 0 ? 0 : position,
+ rmtlseek (archive, position, SEEK_SET) != position))
+ seek_error_details (archive_name_array[0], position);
return;
}
}
-/*----------------------------------------------------------------.
-| Write out the record which has been filled. If MOVE_BACK_FLAG, |
-| backspace to where we started. |
-`----------------------------------------------------------------*/
-
+/* Write out the record which has been filled. If MOVE_BACK_FLAG,
+ backspace to where we started. */
static void
write_record (int move_back_flag)
{
- save_record = record_start;
+ union block *save_record = record_start;
record_start = new_record;
- if (archive == STDIN)
+ if (acting_as_filter)
{
- archive = STDOUT;
+ archive = STDOUT_FILENO;
flush_write ();
- archive = STDIN;
+ archive = STDIN_FILENO;
}
else
{
- move_archive (-(records_read + 1));
+ move_archive ((records_written + records_skipped) - records_read);
flush_write ();
}
{
/* Move the tape head back to where we were. */
- if (archive != STDIN)
- move_archive (records_read);
-
- records_read--;
+ if (! acting_as_filter)
+ move_archive (records_read - (records_written + records_skipped));
}
- blocks_needed = blocking_factor;
new_blocks = 0;
}
-/*---.
-| ? |
-`---*/
+static void
+write_recent_blocks (union block *h, size_t blocks)
+{
+ size_t i;
+ for (i = 0; i < blocks; i++)
+ {
+ new_record[new_blocks++] = h[i];
+ if (new_blocks == blocking_factor)
+ write_record (1);
+ }
+}
void
delete_archive_members (void)
/* FIXME: Should clean the routine before cleaning these variables :-( */
struct name *name;
- int blocks_to_skip = 0;
- int blocks_to_keep = 0;
+ off_t blocks_to_skip = 0;
+ off_t blocks_to_keep = 0;
int kept_blocks_in_record;
name_gather ();
open_archive (ACCESS_UPDATE);
+ acting_as_filter = strcmp (archive_name_array[0], "-") == 0;
- while (logical_status == HEADER_STILL_UNREAD)
+ do
{
- enum read_header status = read_header ();
+ enum read_header status = read_header (1);
switch (status)
{
abort ();
case HEADER_SUCCESS:
- if (name = name_scan (current_file_name), !name)
+ if (name = name_scan (current_stat_info.file_name), !name)
{
- set_next_block_after (current_header);
- if (current_header->oldgnu_header.isextended)
- skip_extended_headers ();
- skip_file ((long) (current_stat.st_size));
+ skip_member ();
break;
}
name->found = 1;
- logical_status = HEADER_SUCCESS;
+ /* Fall through. */
+ case HEADER_SUCCESS_EXTENDED:
+ logical_status = status;
break;
case HEADER_ZERO_BLOCK:
+ if (ignore_zeros_option)
+ {
+ set_next_block_after (current_header);
+ break;
+ }
+ /* Fall through. */
case HEADER_END_OF_FILE:
logical_status = HEADER_END_OF_FILE;
break;
previous_status = status;
}
+ while (logical_status == HEADER_STILL_UNREAD);
- if (logical_status != HEADER_SUCCESS)
- {
- write_eot ();
- close_archive ();
- names_notfound ();
- return;
- }
-
- write_archive_to_stdout = 0;
- new_record = (union block *) xmalloc ((size_t) record_size);
-
- /* Save away blocks before this one in this record. */
+ records_skipped = records_read - 1;
+ new_record = xmalloc (record_size);
- new_blocks = current_block - record_start;
- blocks_needed = blocking_factor - new_blocks;
- if (new_blocks)
- memcpy ((void *) new_record, (void *) record_start,
- (size_t) (new_blocks * BLOCKSIZE));
-
-#if 0
- /* FIXME: Old code, before the goto was inserted. To be redesigned. */
- set_next_block_after (current_header);
- if (current_header->oldgnu_header.isextended)
- skip_extended_headers ();
- skip_file ((long) (current_stat.st_size));
-#endif
- logical_status = HEADER_STILL_UNREAD;
- goto flush_file;
-
- /* FIXME: Solaris 2.4 Sun cc (the ANSI one, not the old K&R) says:
- "delete.c", line 223: warning: loop not entered at top
- Reported by Bruno Haible. */
- while (1)
+ if (logical_status == HEADER_SUCCESS
+ || logical_status == HEADER_SUCCESS_EXTENDED)
{
- enum read_header status;
+ write_archive_to_stdout = 0;
- /* Fill in a record. */
+ /* Save away blocks before this one in this record. */
- if (current_block == record_end)
- {
- flush_archive ();
- records_read++;
- }
- status = read_header ();
+ new_blocks = current_block - record_start;
+ if (new_blocks)
+ memcpy (new_record, record_start, new_blocks * BLOCKSIZE);
- if (status == HEADER_ZERO_BLOCK && ignore_zeros_option)
+ if (logical_status == HEADER_SUCCESS)
{
- set_next_block_after (current_header);
- continue;
- }
- if (status == HEADER_END_OF_FILE || status == HEADER_ZERO_BLOCK)
- {
- logical_status = HEADER_END_OF_FILE;
- memset (new_record[new_blocks].buffer, 0,
- (size_t) (BLOCKSIZE * blocks_needed));
- new_blocks += blocks_needed;
- blocks_needed = 0;
- write_record (0);
- break;
+ /* FIXME: Pheew! This is crufty code! */
+ logical_status = HEADER_STILL_UNREAD;
+ goto flush_file;
}
- if (status == HEADER_FAILURE)
+ /* FIXME: Solaris 2.4 Sun cc (the ANSI one, not the old K&R) says:
+ "delete.c", line 223: warning: loop not entered at top
+ Reported by Bruno Haible. */
+ while (1)
{
- ERROR ((0, 0, _("Deleting non-header from archive")));
- set_next_block_after (current_header);
- continue;
- }
+ enum read_header status;
- /* Found another header. */
+ /* Fill in a record. */
- if (name = name_scan (current_file_name), name)
- {
- name->found = 1;
- flush_file:
- set_next_block_after (current_header);
- blocks_to_skip = (current_stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
+ if (current_block == record_end)
+ flush_archive ();
+ status = read_header (0);
- while (record_end - current_block <= blocks_to_skip)
+ if (status == HEADER_ZERO_BLOCK && ignore_zeros_option)
{
- blocks_to_skip -= (record_end - current_block);
- flush_archive ();
- records_read++;
+ set_next_block_after (current_header);
+ continue;
+ }
+ if (status == HEADER_END_OF_FILE || status == HEADER_ZERO_BLOCK)
+ {
+ logical_status = HEADER_END_OF_FILE;
+ break;
}
- current_block += blocks_to_skip;
- blocks_to_skip = 0;
- continue;
- }
- /* Copy header. */
+ if (status == HEADER_FAILURE)
+ {
+ ERROR ((0, 0, _("Deleting non-header from archive")));
+ set_next_block_after (current_header);
+ continue;
+ }
- new_record[new_blocks] = *current_header;
- new_blocks++;
- blocks_needed--;
- blocks_to_keep
- = (current_stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
- set_next_block_after (current_header);
- if (blocks_needed == 0)
- write_record (1);
+ /* Found another header. */
+
+ if (name = name_scan (current_stat_info.file_name), name)
+ {
+ name->found = 1;
+ flush_file:
+ set_next_block_after (current_header);
+ blocks_to_skip = (current_stat_info.stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
+
+ while (record_end - current_block <= blocks_to_skip)
+ {
+ blocks_to_skip -= (record_end - current_block);
+ flush_archive ();
+ }
+ current_block += blocks_to_skip;
+ blocks_to_skip = 0;
+ continue;
+ }
- /* Copy data. */
+ /* Copy header. */
- kept_blocks_in_record = record_end - current_block;
- if (kept_blocks_in_record > blocks_to_keep)
- kept_blocks_in_record = blocks_to_keep;
+ write_recent_blocks (recent_long_name, recent_long_name_blocks);
+ write_recent_blocks (recent_long_link, recent_long_link_blocks);
+ new_record[new_blocks] = *current_header;
+ new_blocks++;
+ blocks_to_keep
+ = (current_stat_info.stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
+ set_next_block_after (current_header);
+ if (new_blocks == blocking_factor)
+ write_record (1);
- while (blocks_to_keep)
- {
- int count;
+ /* Copy data. */
- if (current_block == record_end)
+ kept_blocks_in_record = record_end - current_block;
+ if (kept_blocks_in_record > blocks_to_keep)
+ kept_blocks_in_record = blocks_to_keep;
+
+ while (blocks_to_keep)
{
- flush_read ();
- records_read++;
- current_block = record_start;
- kept_blocks_in_record = blocking_factor;
- if (kept_blocks_in_record > blocks_to_keep)
- kept_blocks_in_record = blocks_to_keep;
+ int count;
+
+ if (current_block == record_end)
+ {
+ flush_read ();
+ current_block = record_start;
+ kept_blocks_in_record = blocking_factor;
+ if (kept_blocks_in_record > blocks_to_keep)
+ kept_blocks_in_record = blocks_to_keep;
+ }
+ count = kept_blocks_in_record;
+ if (blocking_factor - new_blocks < count)
+ count = blocking_factor - new_blocks;
+
+ if (! count)
+ abort ();
+
+ memcpy (new_record + new_blocks, current_block, count * BLOCKSIZE);
+ new_blocks += count;
+ current_block += count;
+ blocks_to_keep -= count;
+ kept_blocks_in_record -= count;
+
+ if (new_blocks == blocking_factor)
+ write_record (1);
}
- count = kept_blocks_in_record;
- if (count > blocks_needed)
- count = blocks_needed;
-
- memcpy ((void *) (new_record + new_blocks),
- (void *) current_block,
- (size_t) (count * BLOCKSIZE));
- new_blocks += count;
- blocks_needed -= count;
- current_block += count;
- blocks_to_keep -= count;
- kept_blocks_in_record -= count;
-
- if (blocks_needed == 0)
- write_record (1);
}
}
- write_eot ();
+ if (logical_status == HEADER_END_OF_FILE)
+ {
+ /* Write the end of tape. FIXME: we can't use write_eot here,
+ as it gets confused when the input is at end of file. */
+
+ int total_zero_blocks = 0;
+
+ do
+ {
+ int zero_blocks = blocking_factor - new_blocks;
+ memset (new_record + new_blocks, 0, BLOCKSIZE * zero_blocks);
+ total_zero_blocks += zero_blocks;
+ write_record (total_zero_blocks < 2);
+ }
+ while (total_zero_blocks < 2);
+ }
+
+ free (new_record);
+
+ if (! acting_as_filter && ! _isrmt (archive))
+ {
+#if MSDOS
+ int status = write (archive, "", 0);
+#else
+ off_t pos = lseek (archive, (off_t) 0, SEEK_CUR);
+ int status = pos < 0 ? -1 : ftruncate (archive, pos);
+#endif
+ if (status != 0)
+ truncate_warn (archive_name_array[0]);
+ }
+
close_archive ();
names_notfound ();
}
}
/* If restoring permissions, restore the mode for FILE_NAME from
- information given in *STAT_INFO (where *CURRENT_STAT_INFO gives
- the current status if CURRENT_STAT_INFO is nonzero); otherwise invert the
+ information given in *STAT_INFO (where *CUR_INFO gives
+ the current status if CUR_INFO is nonzero); otherwise invert the
INVERT_PERMISSIONS bits from the file's current permissions.
PERMSTATUS specifies the status of the file's permissions.
TYPEFLAG specifies the type of the file. */
static void
-set_mode (char const *file_name, struct stat const *stat_info,
- struct stat const *current_stat_info,
+set_mode (char const *file_name,
+ struct stat const *stat_info,
+ struct stat const *cur_info,
mode_t invert_permissions, enum permstatus permstatus,
char typeflag)
{
that we created, so there's no point optimizing this code for
other cases. */
struct stat st;
- if (! current_stat_info)
+ if (! cur_info)
{
if (stat (file_name, &st) != 0)
{
stat_error (file_name);
return;
}
- current_stat_info = &st;
+ cur_info = &st;
}
- mode = current_stat_info->st_mode ^ invert_permissions;
+ mode = cur_info->st_mode ^ invert_permissions;
}
if (chmod (file_name, mode) != 0)
/* Restore stat attributes (owner, group, mode and times) for
FILE_NAME, using information given in *STAT_INFO.
- If CURRENT_STAT_INFO is nonzero, *CURRENT_STAT_INFO is the
+ If CUR_INFO is nonzero, *CUR_INFO is the
file's currernt status.
If not restoring permissions, invert the
INVERT_PERMISSIONS bits from the file's current permissions.
punt for the rest. Sigh! */
static void
-set_stat (char const *file_name, struct stat const *stat_info,
- struct stat const *current_stat_info,
+set_stat (char const *file_name,
+ struct stat const *stat_info,
+ struct stat const *cur_info,
mode_t invert_permissions, enum permstatus permstatus,
char typeflag)
{
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, current_stat_info,
+ set_mode (file_name, stat_info, cur_info,
invert_permissions, permstatus, typeflag);
}
if (st.st_dev == dir_stat_info->st_dev
&& st.st_ino == dir_stat_info->st_ino)
{
- data->stat_info = current_stat;
- data->invert_permissions = (MODE_RWX
- & (current_stat.st_mode ^ st.st_mode));
+ data->stat_info = current_stat_info.stat;
+ data->invert_permissions =
+ (MODE_RWX & (current_stat_info.stat.st_mode ^ st.st_mode));
data->permstatus = ARCHIVED_PERMSTATUS;
return;
}
invert_permissions is zero, because
repair_delayed_set_stat may need to update the struct. */
delay_set_stat (file_name,
- ¤t_stat /* ignored */,
+ ¤t_stat_info.stat /* ignored */,
invert_permissions, INTERDIR_PERMSTATUS);
print_for_mkdir (file_name, cursor - file_name, mode);
*cursor = '/';
- if (errno == EEXIST
+ if (errno == EEXIST)
+ continue; /* Directory already exists. */
+ else if ((errno == ENOSYS /* Automounted dirs on Solaris return
+ this. Reported by Warren Hyde
+ <Warren.Hyde@motorola.com> */
#if MSDOS
- /* Turbo C mkdir gives a funny errno. */
- || errno == EACCES
+ || errno == EACCES /* Turbo C mkdir gives a funny errno. */
#endif
- )
- /* Directory already exists. */
+ )
+ && access (file_name, W_OK) == 0)
continue;
/* Some other error in the mkdir. We return to the caller. */
bool
fill_in_sparse_array (void)
{
- off_t sparse_data_size = current_stat.st_size;
+ off_t sparse_data_size = current_stat_info.stat.st_size;
off_t file_size = OFF_FROM_HEADER (current_header->oldgnu_header.realsize);
int sparses;
int counter;
return 1;
invalid_member:
- ERROR ((0, 0, "%s: invalid sparse archive member", current_file_name));
+ ERROR ((0, 0, _("%s: invalid sparse archive member"),
+ current_stat_info.file_name));
return 0;
}
struct delayed_set_stat *data = delayed_set_stat_head;
bool skip_this_one = 0;
struct stat st;
- struct stat const *current_stat_info = 0;
+ struct stat const *cur_info = 0;
check_for_renamed_directories |= data->after_symlinks;
if (check_for_renamed_directories)
{
- current_stat_info = &st;
+ cur_info = &st;
if (stat (data->file_name, &st) != 0)
{
stat_error (data->file_name);
}
if (! skip_this_one)
- set_stat (data->file_name, &data->stat_info, current_stat_info,
+ set_stat (data->file_name, &data->stat_info, cur_info,
data->invert_permissions, data->permstatus, DIRTYPE);
delayed_set_stat_head = data->next;
off_t file_size;
int interdir_made = 0;
char typeflag;
- union block *exhdr;
char *file_name;
set_next_block_after (current_header);
- decode_header (current_header, ¤t_stat, ¤t_format, 1);
+ decode_header (current_header, ¤t_stat_info, ¤t_format, 1);
- if (interactive_option && !confirm ("extract", current_file_name))
+ if (interactive_option && !confirm ("extract", current_stat_info.file_name))
{
skip_member ();
return;
if (verbose_option)
print_header (-1);
- file_name = safer_name_suffix (current_file_name, 0);
+ file_name = safer_name_suffix (current_stat_info.file_name, 0);
apply_nonancestor_delayed_set_stat (file_name, 0);
/* Appears to be a file. But BSD tar uses the convention that a slash
suffix means a directory. */
- if (current_trailing_slash)
+ if (current_stat_info.had_trailing_slash)
goto really_dir;
/* FIXME: deal with protection issues. */
| (old_files_option == OVERWRITE_OLD_FILES
? O_TRUNC
: O_EXCL));
- mode = current_stat.st_mode & MODE_RWX & ~ current_umask;
+ mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask;
if (to_stdout_option)
{
the open call that creates them. */
if (typeflag == CONTTYPE)
- fd = open (file_name, openflag | O_CTG, mode, current_stat.st_size);
+ fd = open (file_name, openflag | O_CTG, mode, current_stat_info.stat.st_size);
else
fd = open (file_name, openflag, mode);
name = xmalloc (name_length_bis);
memcpy (name, file_name, name_length_bis);
size = extract_sparse_file (fd, name,
- current_stat.st_size, file_size);
+ current_stat_info.stat.st_size, file_size);
free (sparsearray);
}
else
- for (size = current_stat.st_size; size > 0; )
+ for (size = current_stat_info.stat.st_size; size > 0; )
{
if (multi_volume_option)
{
- assign_string (&save_name, current_file_name);
- save_totsize = current_stat.st_size;
+ assign_string (&save_name, current_stat_info.file_name);
+ save_totsize = current_stat_info.stat.st_size;
save_sizeleft = size;
}
undo_last_backup ();
}
- set_stat (file_name, ¤t_stat, 0, 0,
+ set_stat (file_name, ¤t_stat_info.stat, 0, 0,
(old_files_option == OVERWRITE_OLD_FILES
? UNKNOWN_PERMSTATUS
: ARCHIVED_PERMSTATUS),
break;
if (absolute_names_option
- || ! (ISSLASH (current_link_name
- [FILESYSTEM_PREFIX_LEN (current_link_name)])
- || contains_dot_dot (current_link_name)))
+ || ! (ISSLASH (current_stat_info.link_name
+ [FILESYSTEM_PREFIX_LEN (current_stat_info.link_name)])
+ || contains_dot_dot (current_stat_info.link_name)))
{
- while (status = symlink (current_link_name, file_name),
+ while (status = symlink (current_stat_info.link_name, file_name),
status != 0)
if (!maybe_recoverable (file_name, &interdir_made))
break;
if (status == 0)
- set_stat (file_name, ¤t_stat, 0, 0, 0, SYMTYPE);
+ set_stat (file_name, ¤t_stat_info.stat, 0, 0, 0, SYMTYPE);
else
- symlink_error (current_link_name, file_name);
+ symlink_error (current_stat_info.link_name, file_name);
}
else
{
struct delayed_set_stat *h;
struct delayed_symlink *p =
xmalloc (offsetof (struct delayed_symlink, target)
- + strlen (current_link_name) + 1);
+ + strlen (current_stat_info.link_name) + 1);
p->next = delayed_symlink_head;
delayed_symlink_head = p;
p->dev = st.st_dev;
p->ino = st.st_ino;
p->mtime = st.st_mtime;
- p->uid = current_stat.st_uid;
- p->gid = current_stat.st_gid;
+ p->uid = current_stat_info.stat.st_uid;
+ p->gid = current_stat_info.stat.st_gid;
p->sources = xmalloc (offsetof (struct string_list, string)
+ strlen (file_name) + 1);
p->sources->next = 0;
strcpy (p->sources->string, file_name);
- strcpy (p->target, current_link_name);
+ strcpy (p->target, current_stat_info.link_name);
h = delayed_set_stat_head;
if (h && ! h->after_symlinks
again_link:
{
- char const *link_name = safer_name_suffix (current_link_name, 1);
+ char const *link_name = safer_name_suffix (current_stat_info.link_name, 1);
struct stat st1, st2;
int e;
#if S_IFCHR
case CHRTYPE:
- current_stat.st_mode |= S_IFCHR;
+ current_stat_info.stat.st_mode |= S_IFCHR;
goto make_node;
#endif
#if S_IFBLK
case BLKTYPE:
- current_stat.st_mode |= S_IFBLK;
+ current_stat_info.stat.st_mode |= S_IFBLK;
#endif
#if S_IFCHR || S_IFBLK
if (! prepare_to_extract (file_name, 0))
break;
- status = mknod (file_name, current_stat.st_mode,
- current_stat.st_rdev);
+ status = mknod (file_name, current_stat_info.stat.st_mode,
+ current_stat_info.stat.st_rdev);
if (status != 0)
{
if (maybe_recoverable (file_name, &interdir_made))
undo_last_backup ();
break;
};
- set_stat (file_name, ¤t_stat, 0, 0,
+ set_stat (file_name, ¤t_stat_info.stat, 0, 0,
ARCHIVED_PERMSTATUS, typeflag);
break;
#endif
if (! prepare_to_extract (file_name, 0))
break;
- while (status = mkfifo (file_name, current_stat.st_mode),
+ while (status = mkfifo (file_name, current_stat_info.stat.st_mode),
status != 0)
if (!maybe_recoverable (file_name, &interdir_made))
break;
if (status == 0)
- set_stat (file_name, ¤t_stat, 0, 0,
+ set_stat (file_name, ¤t_stat_info.stat, NULL, 0,
ARCHIVED_PERMSTATUS, typeflag);
else
{
else if (typeflag == GNUTYPE_DUMPDIR)
skip_member ();
- mode = ((current_stat.st_mode
+ mode = ((current_stat_info.stat.st_mode
| (we_are_root ? 0 : MODE_WXUSR))
& MODE_RWX);
if (status == 0
|| old_files_option == DEFAULT_OLD_FILES
|| old_files_option == OVERWRITE_OLD_FILES)
- delay_set_stat (file_name, ¤t_stat,
- MODE_RWX & (mode ^ current_stat.st_mode),
+ delay_set_stat (file_name, ¤t_stat_info.stat,
+ MODE_RWX & (mode ^ current_stat_info.stat.st_mode),
(status == 0
? ARCHIVED_PERMSTATUS
: UNKNOWN_PERMSTATUS));
case GNUTYPE_VOLHDR:
if (verbose_option)
- fprintf (stdlis, _("Reading %s\n"), quote (current_file_name));
+ fprintf (stdlis, _("Reading %s\n"), quote (current_stat_info.file_name));
break;
case GNUTYPE_NAMES:
case GNUTYPE_MULTIVOL:
ERROR ((0, 0,
_("%s: Cannot extract -- file is continued from another volume"),
- quotearg_colon (current_file_name)));
+ quotearg_colon (current_stat_info.file_name)));
skip_member ();
if (backup_option)
undo_last_backup ();
/* GNU dump extensions to tar.
- Copyright (C) 1988, 92, 93, 94, 96, 97 Free Software Foundation, Inc.
+
+ Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
+ 2003 Free Software Foundation, Inc.
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
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"
-
-#include <time.h>
-time_t time ();
-
-#define ISDIGIT(Char) (ISASCII (Char) && isdigit (Char))
-#define ISSPACE(Char) (ISASCII (Char) && isspace (Char))
-
+#include <getline.h>
+#include <hash.h>
+#include <quotearg.h>
#include "common.h"
\f
/* Variable sized generic character buffers. */
struct accumulator
{
- int allocated;
- int length;
+ size_t allocated;
+ size_t length;
char *pointer;
};
/* Amount of space guaranteed just after a reallocation. */
#define ACCUMULATOR_SLACK 50
-/*---------------------------------------------------------.
-| Return the accumulated data from an ACCUMULATOR buffer. |
-`---------------------------------------------------------*/
-
+/* Return the accumulated data from an ACCUMULATOR buffer. */
static char *
get_accumulator (struct accumulator *accumulator)
{
return accumulator->pointer;
}
-/*-----------------------------------------------.
-| Allocate and return a new accumulator buffer. |
-`-----------------------------------------------*/
-
+/* Allocate and return a new accumulator buffer. */
static struct accumulator *
new_accumulator (void)
{
struct accumulator *accumulator
- = (struct accumulator *) xmalloc (sizeof (struct accumulator));
+ = xmalloc (sizeof (struct accumulator));
accumulator->allocated = ACCUMULATOR_SLACK;
- accumulator->pointer = (char *) xmalloc (ACCUMULATOR_SLACK);
+ accumulator->pointer = xmalloc (ACCUMULATOR_SLACK);
accumulator->length = 0;
return accumulator;
}
-/*-----------------------------------.
-| Deallocate an ACCUMULATOR buffer. |
-`-----------------------------------*/
-
+/* Deallocate an ACCUMULATOR buffer. */
static void
delete_accumulator (struct accumulator *accumulator)
{
free (accumulator);
}
-/*----------------------------------------------------------------------.
-| At the end of an ACCUMULATOR buffer, add a DATA block of SIZE bytes. |
-`----------------------------------------------------------------------*/
-
+/* At the end of an ACCUMULATOR buffer, add a DATA block of SIZE bytes. */
static void
add_to_accumulator (struct accumulator *accumulator,
- const char *data, int size)
+ const char *data, size_t size)
{
if (accumulator->length + size > accumulator->allocated)
{
accumulator->allocated = accumulator->length + size + ACCUMULATOR_SLACK;
- accumulator->pointer = (char *)
- xrealloc (accumulator->pointer, (size_t) accumulator->allocated);
+ accumulator->pointer =
+ xrealloc (accumulator->pointer, accumulator->allocated);
}
- memcpy (accumulator->pointer + accumulator->length, data, (size_t) size);
+ memcpy (accumulator->pointer + accumulator->length, data, size);
accumulator->length += size;
}
\f
/* Incremental dump specialities. */
-/* Current time. */
-static time_t time_now;
+/* Which child files to save under a directory. */
+enum children {NO_CHILDREN, CHANGED_CHILDREN, ALL_CHILDREN};
-/* List of directory names. */
+/* Directory attributes. */
struct directory
{
- struct directory *next; /* next entry in list */
- const char *name; /* path name of directory */
- int device_number; /* device number for directory */
- int inode_number; /* inode number for directory */
- int allnew;
- const char *dir_text;
+ dev_t device_number; /* device number for directory */
+ ino_t inode_number; /* inode number for directory */
+ enum children children;
+ bool nfs;
+ bool found;
+ char name[1]; /* path name of directory */
};
-static struct directory *directory_list = NULL;
-/*-------------------------------------------------------------------.
-| Create and link a new directory entry for directory NAME, having a |
-| DEVICE_NUMBER and a INODE_NUMBER, with some TEXT. |
-`-------------------------------------------------------------------*/
+static Hash_table *directory_table;
-static void
-note_directory (char *name, dev_t device_number, ino_t inode_number,
- const char *text)
-{
- struct directory *directory
- = (struct directory *) xmalloc (sizeof (struct directory));
+#if HAVE_ST_FSTYPE_STRING
+ static char const nfs_string[] = "nfs";
+# define NFS_FILE_STAT(st) (strcmp ((st).st_fstype, nfs_string) == 0)
+#else
+# define ST_DEV_MSB(st) (~ (dev_t) 0 << (sizeof (st).st_dev * CHAR_BIT - 1))
+# define NFS_FILE_STAT(st) (((st).st_dev & ST_DEV_MSB (st)) != 0)
+#endif
- directory->next = directory_list;
- directory_list = directory;
+/* Calculate the hash of a directory. */
+static unsigned
+hash_directory (void const *entry, unsigned n_buckets)
+{
+ struct directory const *directory = entry;
+ return hash_string (directory->name, n_buckets);
+}
- directory->device_number = device_number;
- directory->inode_number = inode_number;
- directory->name = xstrdup (name);
- directory->dir_text = text;
- directory->allnew = 0;
+/* Compare two directories for equality. */
+static bool
+compare_directories (void const *entry1, void const *entry2)
+{
+ struct directory const *directory1 = entry1;
+ struct directory const *directory2 = entry2;
+ return strcmp (directory1->name, directory2->name) == 0;
}
-/*------------------------------------------------------------------------.
-| Return a directory entry for a given path NAME, or NULL if none found. |
-`------------------------------------------------------------------------*/
+/* Create and link a new directory entry for directory NAME, having a
+ device number DEV and an inode number INO, with NFS indicating
+ whether it is an NFS device and FOUND indicating whether we have
+ found that the directory exists. */
+static struct directory *
+note_directory (char const *name, dev_t dev, ino_t ino, bool nfs, bool found)
+{
+ size_t size = offsetof (struct directory, name) + strlen (name) + 1;
+ struct directory *directory = xmalloc (size);
+
+ directory->device_number = dev;
+ directory->inode_number = ino;
+ directory->children = CHANGED_CHILDREN;
+ directory->nfs = nfs;
+ directory->found = found;
+ strcpy (directory->name, name);
+
+ if (! ((directory_table
+ || (directory_table = hash_initialize (0, 0, hash_directory,
+ compare_directories, 0)))
+ && hash_insert (directory_table, directory)))
+ xalloc_die ();
+
+ return directory;
+}
+/* Return a directory entry for a given path NAME, or zero if none found. */
static struct directory *
find_directory (char *name)
{
- struct directory *directory;
-
- for (directory = directory_list;
- directory;
- directory = directory->next)
+ if (! directory_table)
+ return 0;
+ else
{
- if (!strcmp (directory->name, name))
- return directory;
+ size_t size = offsetof (struct directory, name) + strlen (name) + 1;
+ struct directory *dir = alloca (size);
+ strcpy (dir->name, name);
+ return hash_lookup (directory_table, dir);
}
- return NULL;
}
-/*---.
-| ? |
-`---*/
-
static int
-compare_dirents (const voidstar first, const voidstar second)
+compare_dirents (const void *first, const void *second)
{
return strcmp ((*(char *const *) first) + 1,
(*(char *const *) second) + 1);
}
-/*---.
-| ? |
-`---*/
-
char *
-get_directory_contents (char *path, int device)
+get_directory_contents (char *path, dev_t device)
{
struct accumulator *accumulator;
/* Recursively scan the given PATH. */
{
- DIR *dirp = opendir (path); /* for scanning directory */
- struct dirent *entry; /* directory entry being scanned */
+ char *dirp = savedir (path); /* for scanning directory */
+ char const *entry; /* directory entry being scanned */
+ size_t entrylen; /* length of directory entry */
char *name_buffer; /* directory, `/', and directory member */
- int name_buffer_size; /* allocated size of name_buffer, minus 2 */
- int name_length; /* used length in name_buffer */
+ size_t name_buffer_size; /* allocated size of name_buffer, minus 2 */
+ size_t name_length; /* used length in name_buffer */
struct directory *directory; /* for checking if already already seen */
- int all_children;
+ enum children children;
- if (dirp == NULL)
+ if (! dirp)
{
- ERROR ((0, errno, _("Cannot open directory %s"), path));
- return NULL;
+ if (ignore_failed_read_option)
+ savedir_warn (path);
+ else
+ savedir_error (path);
}
- errno = 0; /* FIXME: errno should be read-only */
+ errno = 0;
name_buffer_size = strlen (path) + NAME_FIELD_SIZE;
- name_buffer = xmalloc ((size_t) (name_buffer_size + 2));
+ name_buffer = xmalloc (name_buffer_size + 2);
strcpy (name_buffer, path);
- if (path[strlen (path) - 1] != '/')
+ if (! ISSLASH (path[strlen (path) - 1]))
strcat (name_buffer, "/");
name_length = strlen (name_buffer);
directory = find_directory (path);
- all_children = directory ? directory->allnew : 0;
+ children = directory ? directory->children : CHANGED_CHILDREN;
accumulator = new_accumulator ();
- while (entry = readdir (dirp), entry)
- {
- struct stat stat_data;
+ if (dirp && children != NO_CHILDREN)
+ for (entry = dirp;
+ (entrylen = strlen (entry)) != 0;
+ entry += entrylen + 1)
+ {
+ if (name_buffer_size <= entrylen + name_length)
+ {
+ do
+ name_buffer_size += NAME_FIELD_SIZE;
+ while (name_buffer_size <= entrylen + name_length);
+ name_buffer = xrealloc (name_buffer, name_buffer_size + 2);
+ }
+ strcpy (name_buffer + name_length, entry);
- /* Skip `.' and `..'. */
+ if (excluded_name (name_buffer))
+ add_to_accumulator (accumulator, "N", 1);
+ else
+ {
+ struct stat stat_data;
- if (is_dot_or_dotdot (entry->d_name))
- continue;
+ if (deref_stat (dereference_option, name_buffer, &stat_data))
+ {
+ if (ignore_failed_read_option)
+ stat_warn (name_buffer);
+ else
+ stat_error (name_buffer);
+ continue;
+ }
- if ((int) NAMLEN (entry) + name_length >= name_buffer_size)
- {
- while ((int) NAMLEN (entry) + name_length >= name_buffer_size)
- name_buffer_size += NAME_FIELD_SIZE;
- name_buffer = (char *)
- xrealloc (name_buffer, (size_t) (name_buffer_size + 2));
- }
- strcpy (name_buffer + name_length, entry->d_name);
-
- if (dereference_option
-#ifdef AIX
- ? statx (name_buffer, &stat_data, STATSIZE, STX_HIDDEN)
- : statx (name_buffer, &stat_data, STATSIZE, STX_HIDDEN | STX_LINK)
-#else
- ? stat (name_buffer, &stat_data)
- : lstat (name_buffer, &stat_data)
-#endif
- )
- {
- ERROR ((0, errno, _("Cannot stat %s"), name_buffer));
- continue;
- }
-
- if ((one_file_system_option && device != stat_data.st_dev)
- || (exclude_option && check_exclude (name_buffer)))
- add_to_accumulator (accumulator, "N", 1);
-
-#ifdef AIX
- else if (S_ISHIDDEN (stat_data.st_mode))
- {
- add_to_accumulator (accumulator, "D", 1);
- strcat (entry->d_name, "A");
- entry->d_namlen++;
- }
+ if (S_ISDIR (stat_data.st_mode))
+ {
+ bool nfs = NFS_FILE_STAT (stat_data);
+
+ if (directory = find_directory (name_buffer), directory)
+ {
+ /* With NFS, the same file can have two different devices
+ if an NFS directory is mounted in multiple locations,
+ which is relatively common when automounting.
+ To avoid spurious incremental redumping of
+ directories, consider all NFS devices as equal,
+ relying on the i-node to establish differences. */
+
+ if (! (((directory->nfs & nfs)
+ || directory->device_number == stat_data.st_dev)
+ && directory->inode_number == stat_data.st_ino))
+ {
+ if (verbose_option)
+ WARN ((0, 0, _("%s: Directory has been renamed"),
+ quotearg_colon (name_buffer)));
+ directory->children = ALL_CHILDREN;
+ directory->nfs = nfs;
+ directory->device_number = stat_data.st_dev;
+ directory->inode_number = stat_data.st_ino;
+ }
+ directory->found = 1;
+ }
+ else
+ {
+ if (verbose_option)
+ WARN ((0, 0, _("%s: Directory is new"),
+ quotearg_colon (name_buffer)));
+ directory = note_directory (name_buffer,
+ stat_data.st_dev,
+ stat_data.st_ino, nfs, 1);
+ directory->children =
+ ((listed_incremental_option
+ || newer_mtime_option <= stat_data.st_mtime
+ || (after_date_option &&
+ newer_ctime_option <= stat_data.st_ctime))
+ ? ALL_CHILDREN
+ : CHANGED_CHILDREN);
+ }
+
+ if (one_file_system_option && device != stat_data.st_dev)
+ directory->children = NO_CHILDREN;
+ else if (children == ALL_CHILDREN)
+ directory->children = ALL_CHILDREN;
+
+ add_to_accumulator (accumulator, "D", 1);
+ }
+
+ else if (one_file_system_option && device != stat_data.st_dev)
+ add_to_accumulator (accumulator, "N", 1);
+
+#ifdef S_ISHIDDEN
+ else if (S_ISHIDDEN (stat_data.st_mode))
+ {
+ add_to_accumulator (accumulator, "D", 1);
+ add_to_accumulator (accumulator, entry, entrylen);
+ add_to_accumulator (accumulator, "A", 2);
+ continue;
+ }
#endif
- else if (S_ISDIR (stat_data.st_mode))
- {
- if (directory = find_directory (name_buffer), directory)
- {
- /* Devices having the high bit set are NFS devices, which are
- attributed somewhat randomly in automounting situations.
- For avoiding spurious incremental redumping of directories,
- we have to plainly consider all NFS devices as equal,
- relying on the i-node only to establish differences. */
-
- /* FIXME: Göran Uddeborg <goeran@uddeborg.pp.se> says, on
- 1996-09-20, that SunOS 5/Solaris 2 uses unsigned long for
- the device number type. */
-
- if ((((short) directory->device_number >= 0
- || (short) stat_data.st_dev >= 0)
- && directory->device_number != stat_data.st_dev)
- || directory->inode_number != stat_data.st_ino)
- {
- if (verbose_option)
- WARN ((0, 0, _("Directory %s has been renamed"),
- name_buffer));
- directory->allnew = 1;
- directory->device_number = stat_data.st_dev;
- directory->inode_number = stat_data.st_ino;
- }
- directory->dir_text = "";
- }
- else
- {
- if (verbose_option)
- WARN ((0, 0, _("Directory %s is new"), name_buffer));
- note_directory (name_buffer, stat_data.st_dev, stat_data.st_ino,
- "");
- directory = find_directory (name_buffer);
- directory->allnew = 1;
- }
- if (all_children && directory)
- directory->allnew = 1;
-
- add_to_accumulator (accumulator, "D", 1);
- }
+ else
+ if (children == CHANGED_CHILDREN
+ && stat_data.st_mtime < newer_mtime_option
+ && (!after_date_option
+ || stat_data.st_ctime < newer_ctime_option))
+ add_to_accumulator (accumulator, "N", 1);
+ else
+ add_to_accumulator (accumulator, "Y", 1);
+ }
- else
- if (!all_children
- && stat_data.st_mtime < newer_mtime_option
- && (!after_date_option
- || stat_data.st_ctime < newer_ctime_option))
- add_to_accumulator (accumulator, "N", 1);
- else
- add_to_accumulator (accumulator, "Y", 1);
+ add_to_accumulator (accumulator, entry, entrylen + 1);
+ }
- add_to_accumulator (accumulator,
- entry->d_name, (int) (NAMLEN (entry) + 1));
- }
add_to_accumulator (accumulator, "\000\000", 2);
free (name_buffer);
- closedir (dirp);
+ if (dirp)
+ free (dirp);
}
/* Sort the contents of the directory, now that we have it all. */
for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
counter++;
- if (counter == 0)
+ if (! counter)
{
delete_accumulator (accumulator);
- return NULL;
+ return 0;
}
- array = (char **) xmalloc (sizeof (char *) * (counter + 1));
+ array = xmalloc (sizeof (char *) * (counter + 1));
array_cursor = array;
for (cursor = pointer; *cursor; cursor += strlen (cursor) + 1)
*array_cursor++ = cursor;
- *array_cursor = NULL;
+ *array_cursor = 0;
- qsort ((voidstar) array, counter, sizeof (char *), compare_dirents);
+ qsort (array, counter, sizeof (char *), compare_dirents);
- buffer = (char *) xmalloc ((size_t) (cursor - pointer + 2));
+ buffer = xmalloc (cursor - pointer + 2);
cursor = buffer;
for (array_cursor = array; *array_cursor; array_cursor++)
return buffer;
}
}
-
-/*----------------------------------------------------------------------.
-| Add all the files in PATH, which is a directory, to the namelist. If |
-| any of the files is a directory, recurse on the subdirectory. |
-`----------------------------------------------------------------------*/
-
-static void
-add_hierarchy_to_namelist (char *path, int device)
-{
- char *buffer = get_directory_contents (path, device);
-
- {
- struct name *name;
-
- for (name = namelist; name; name = name->next)
- if (strcmp (name->name, path) == 0)
- break;
- if (name)
- name->dir_contents = buffer ? buffer : "\0\0\0\0";
- }
-
- if (buffer)
- {
- int name_length = strlen (path);
- int allocated_length = (name_length >= NAME_FIELD_SIZE
- ? name_length + NAME_FIELD_SIZE
- : NAME_FIELD_SIZE);
- char *name_buffer = xmalloc ((size_t) (allocated_length + 1));
- /* FIXME: + 2 above? */
- char *string;
- int string_length;
-
- strcpy (name_buffer, path);
- if (name_buffer[name_length - 1] != '/')
- {
- name_buffer[name_length++] = '/';
- name_buffer[name_length] = '\0';
- }
-
- for (string = buffer; *string; string += string_length + 1)
- {
- string_length = strlen (string);
- if (*string == 'D')
- {
- if (name_length + string_length >= allocated_length)
- {
- while (name_length + string_length >= allocated_length)
- allocated_length += NAME_FIELD_SIZE;
- name_buffer = (char *)
- xrealloc (name_buffer, (size_t) (allocated_length + 1));
- }
- strcpy (name_buffer + name_length, string + 1);
- addname (name_buffer);
- add_hierarchy_to_namelist (name_buffer, device);
- }
- }
-
- free (name_buffer);
- }
-}
\f
-/*---.
-| ? |
-`---*/
+static FILE *listed_incremental_stream;
-static void
+void
read_directory_file (void)
{
- dev_t device_number;
- ino_t inode_number;
- char *strp;
+ int fd;
FILE *fp;
- char buf[512];
- static char *path = NULL;
-
- if (path == NULL)
- path = xmalloc (PATH_MAX);
- time (&time_now);
- if (listed_incremental_option[0] != '/')
- {
-#if HAVE_GETCWD
- if (!getcwd (path, PATH_MAX))
- FATAL_ERROR ((0, 0, _("Could not get current directory")));
-#else
- char *getwd ();
-
- if (!getwd (path))
- FATAL_ERROR ((0, 0, _("Could not get current directory: %s"), path));
-#endif
-
- if (strlen (path) + 1 + strlen (listed_incremental_option) + 1 > PATH_MAX)
- ERROR ((TAREXIT_FAILURE, 0, _("File name %s/%s too long"),
- path, listed_incremental_option));
-
- strcat (path, "/");
- strcat (path, listed_incremental_option);
- listed_incremental_option = path;
- }
- fp = fopen (listed_incremental_option, "r");
- if (fp == 0 && errno != ENOENT)
+ char *buf = 0;
+ size_t bufsize;
+
+ /* Open the file for both read and write. That way, we can write
+ it later without having to reopen it, and don't have to worry if
+ we chdir in the meantime. */
+ fd = open (listed_incremental_option, O_RDWR | O_CREAT, MODE_RW);
+ if (fd < 0)
{
- ERROR ((0, errno, _("Cannot open %s"), listed_incremental_option));
+ open_error (listed_incremental_option);
return;
}
- if (!fp)
- return;
- fgets (buf, sizeof (buf), fp);
-
- /* FIXME: Using after_date_option as a first time flag looks fairly
- dubious to me! So, using -N with incremental might be buggy just
- because of the next few lines. I saw a few unexplained, almost harsh
- advices, from other GNU people, about *not* using -N with incremental
- dumps, and here might lie (part of) the reason. */
- if (!after_date_option)
- {
- newer_mtime_option = atol (buf);
- after_date_option = 1;
- }
- while (fgets (buf, sizeof (buf), fp))
+ fp = fdopen (fd, "r+");
+ if (! fp)
{
- strp = &buf[strlen (buf)];
- if (strp[-1] == '\n')
- strp[-1] = '\0';
- /* FIXME: For files ending with an incomplete line, maybe a NUL might
- be missing, here... */
-
- strp = buf;
- device_number = atol (strp);
- while (ISDIGIT (*strp))
- strp++;
- inode_number = atol (strp);
- while (ISSPACE (*strp))
- strp++;
- while (ISDIGIT (*strp))
- strp++;
- strp++;
- unquote_string (strp);
- note_directory (strp, device_number, inode_number, NULL);
+ open_error (listed_incremental_option);
+ close (fd);
+ return;
}
- if (fclose (fp) == EOF)
- ERROR ((0, errno, "%s", listed_incremental_option));
-}
-/*---.
-| ? |
-`---*/
-
-void
-write_dir_file (void)
-{
- FILE *fp;
- struct directory *directory;
- char *str;
+ listed_incremental_stream = fp;
- fp = fopen (listed_incremental_option, "w");
- if (fp == 0)
- {
- ERROR ((0, errno, _("Cannot write to %s"), listed_incremental_option));
- return;
- }
- fprintf (fp, "%lu\n", time_now);
- for (directory = directory_list; directory; directory = directory->next)
+ if (0 < getline (&buf, &bufsize, fp))
{
- if (!directory->dir_text)
- continue;
- str = quote_copy_string (directory->name);
- if (str)
+ char *ebuf;
+ int n;
+ long lineno = 1;
+ unsigned long u = (errno = 0, strtoul (buf, &ebuf, 10));
+ time_t t = u;
+ if (buf == ebuf || (u == 0 && errno == EINVAL))
+ ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option),
+ _("Invalid time stamp")));
+ else if (t != u || (u == -1 && errno == ERANGE))
+ ERROR ((0, 0, "%s:1: %s", quotearg_colon (listed_incremental_option),
+ _("Time stamp out of range")));
+ else
+ newer_mtime_option = t;
+
+ while (0 < (n = getline (&buf, &bufsize, fp)))
{
- fprintf (fp, "%u %u %s\n", directory->device_number,
- directory->inode_number, str);
- free (str);
+ dev_t dev;
+ ino_t ino;
+ bool nfs = buf[0] == '+';
+ char *strp = buf + nfs;
+
+ lineno++;
+
+ if (buf[n - 1] == '\n')
+ buf[n - 1] = '\0';
+
+ errno = 0;
+ dev = u = strtoul (strp, &ebuf, 10);
+ if (strp == ebuf || (u == 0 && errno == EINVAL))
+ ERROR ((0, 0, "%s:%ld: %s",
+ quotearg_colon (listed_incremental_option), lineno,
+ _("Invalid device number")));
+ else if (dev != u || (u == -1 && errno == ERANGE))
+ ERROR ((0, 0, "%s:%ld: %s",
+ quotearg_colon (listed_incremental_option), lineno,
+ _("Device number out of range")));
+ strp = ebuf;
+
+ errno = 0;
+ ino = u = strtoul (strp, &ebuf, 10);
+ if (strp == ebuf || (u == 0 && errno == EINVAL))
+ ERROR ((0, 0, "%s:%ld: %s",
+ quotearg_colon (listed_incremental_option), lineno,
+ _("Invalid inode number")));
+ else if (ino != u || (u == -1 && errno == ERANGE))
+ ERROR ((0, 0, "%s:%ld: %s",
+ quotearg_colon (listed_incremental_option), lineno,
+ _("Inode number out of range")));
+ strp = ebuf;
+
+ strp++;
+ unquote_string (strp);
+ note_directory (strp, dev, ino, nfs, 0);
}
- else
- fprintf (fp, "%u %u %s\n", directory->device_number,
- directory->inode_number, directory->name);
}
- if (fclose (fp) == EOF)
- ERROR ((0, errno, "%s", listed_incremental_option));
-}
-/*---.
-| ? |
-`---*/
+ if (ferror (fp))
+ read_error (listed_incremental_option);
+ if (buf)
+ free (buf);
+}
-static int
-compare_names (char *param1, char *param2)
+/* Output incremental data for the directory ENTRY to the file DATA.
+ Return nonzero if successful, preserving errno on write failure. */
+static bool
+write_directory_file_entry (void *entry, void *data)
{
- struct name *n1 = (struct name *) param1;
- struct name *n2 = (struct name *) param2;
+ struct directory const *directory = entry;
+ FILE *fp = data;
- if (n1->found)
- return n2->found ? strcmp (n1->name, n2->name) : -1;
-
- if (n2->found)
- return 1;
+ if (directory->found)
+ {
+ int e;
+ char *str = quote_copy_string (directory->name);
+ fprintf (fp, "+%lu %lu %s\n" + ! directory->nfs,
+ (unsigned long) directory->device_number,
+ (unsigned long) directory->inode_number,
+ str ? str : directory->name);
+ e = errno;
+ if (str)
+ free (str);
+ errno = e;
+ }
- return strcmp (n1->name, n2->name);
+ return ! ferror (fp);
}
-/*-------------------------------------------------------------------------.
-| Collect all the names from argv[] (or whatever), then expand them into a |
-| directory tree, and put all the directories at the beginning. |
-`-------------------------------------------------------------------------*/
-
void
-collect_and_sort_names (void)
+write_directory_file (void)
{
- struct name *name;
- struct name *next_name;
- int num_names;
- struct stat statbuf;
-
- name_gather ();
-
- if (listed_incremental_option)
- read_directory_file ();
-
- if (!namelist)
- addname (".");
-
- for (name = namelist; name; name = next_name)
- {
- next_name = name->next;
- if (name->found || name->dir_contents)
- continue;
- if (name->regexp) /* FIXME: just skip regexps for now */
- continue;
- if (name->change_dir)
- if (chdir (name->change_dir) < 0)
- {
- ERROR ((0, errno, _("Cannot chdir to %s"), name->change_dir));
- continue;
- }
-
- if (
-#ifdef AIX
- statx (name->name, &statbuf, STATSIZE, STX_HIDDEN | STX_LINK)
-#else
- lstat (name->name, &statbuf) < 0
-#endif
- )
- {
- ERROR ((0, errno, _("Cannot stat %s"), name->name));
- continue;
- }
- if (S_ISDIR (statbuf.st_mode))
- {
- name->found = 1;
- add_hierarchy_to_namelist (name->name, statbuf.st_dev);
- }
- }
+ FILE *fp = listed_incremental_stream;
- num_names = 0;
- for (name = namelist; name; name = name->next)
- num_names++;
- namelist = (struct name *)
- merge_sort ((voidstar) namelist, num_names,
- (char *) (&(namelist->next)) - (char *) namelist,
- compare_names);
-
- for (name = namelist; name; name = name->next)
- name->found = 0;
+ if (! fp)
+ return;
- if (listed_incremental_option)
- write_dir_file ();
+ if (fseek (fp, 0L, SEEK_SET) != 0)
+ seek_error (listed_incremental_option);
+ if (ftruncate (fileno (fp), (off_t) 0) != 0)
+ truncate_error (listed_incremental_option);
+
+ fprintf (fp, "%lu\n", (unsigned long) start_time);
+ if (! ferror (fp) && directory_table)
+ hash_do_for_each (directory_table, write_directory_file_entry, fp);
+ if (ferror (fp))
+ write_error (listed_incremental_option);
+ if (fclose (fp) != 0)
+ close_error (listed_incremental_option);
}
\f
/* Restoration of incremental dumps. */
-/*---.
-| ? |
-`---*/
-
void
-gnu_restore (int skipcrud)
+gnu_restore (char const *directory_name)
{
- char *current_dir;
char *archive_dir;
- struct accumulator *accumulator;
- char *p;
- DIR *dirp;
- struct dirent *d;
+ char *current_dir;
char *cur, *arc;
- long size, copied;
+ size_t size;
+ size_t copied;
union block *data_block;
char *to;
-#define CURRENT_FILE_NAME (skipcrud + current_file_name)
+ current_dir = savedir (directory_name);
- dirp = opendir (CURRENT_FILE_NAME);
-
- if (!dirp)
+ if (!current_dir)
{
/* The directory doesn't exist now. It'll be created. In any
case, we don't have to delete any files out of it. */
- skip_file ((long) current_stat.st_size);
+ skip_member ();
return;
}
- accumulator = new_accumulator ();
- while (d = readdir (dirp), d)
- {
- if (is_dot_or_dotdot (d->d_name))
- continue;
-
- add_to_accumulator (accumulator, d->d_name, (int) (NAMLEN (d) + 1));
- }
- closedir (dirp);
- add_to_accumulator (accumulator, "", 1);
-
- current_dir = get_accumulator (accumulator);
- archive_dir = (char *) xmalloc ((size_t) current_stat.st_size);
+ size = current_stat_info.stat.st_size;
+ if (size != current_stat_info.stat.st_size)
+ xalloc_die ();
+ archive_dir = xmalloc (size);
to = archive_dir;
- for (size = current_stat.st_size; size > 0; size -= copied)
+ for (; size > 0; size -= copied)
{
data_block = find_next_block ();
if (!data_block)
copied = available_space_after (data_block);
if (copied > size)
copied = size;
- memcpy (to, data_block->buffer, (size_t) copied);
+ memcpy (to, data_block->buffer, copied);
to += copied;
set_next_block_after ((union block *)
(data_block->buffer + copied - 1));
}
if (*arc == '\0')
{
- p = new_name (CURRENT_FILE_NAME, cur);
- if (interactive_option && !confirm ("delete", p))
+ char *p = new_name (directory_name, cur);
+ if (! interactive_option || confirm ("delete", p))
{
- free (p);
- continue;
+ if (verbose_option)
+ fprintf (stdlis, _("%s: Deleting %s\n"),
+ program_name, quote (p));
+ if (! remove_any_file (p, 1))
+ {
+ int e = errno;
+ ERROR ((0, e, _("%s: Cannot remove"), quotearg_colon (p)));
+ }
}
- if (verbose_option)
- fprintf (stdlis, _("%s: Deleting %s\n"), program_name, p);
- if (!remove_any_file (p, 1))
- ERROR ((0, errno, _("Error while deleting %s"), p));
free (p);
}
}
- delete_accumulator (accumulator);
+ free (current_dir);
free (archive_dir);
-
-#undef CURRENT_FILE_NAME
}