From: Sergey Poznyakoff Date: Sun, 31 Aug 2003 22:41:32 +0000 (+0000) Subject: Use current_stat_info X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Ftar;a=commitdiff_plain;h=d858a433c82626309e0d974ca923a79ac7f7f18f Use current_stat_info --- diff --git a/src/buffer.c b/src/buffer.c index a2a138a..ab06752 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -669,8 +669,7 @@ open_archive (enum access_mode wanted_access) 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; @@ -845,8 +844,8 @@ open_archive (enum access_mode wanted_access) 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); @@ -1362,10 +1361,7 @@ close_archive (void) } #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 --git a/src/compare.c b/src/compare.c index d4871c4..6124c95 100644 --- a/src/compare.c +++ b/src/compare.c @@ -1,5 +1,8 @@ /* 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 @@ -14,14 +17,26 @@ 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 +#else +struct utimbuf + { + long actime; + long modtime; + }; +#endif + #if HAVE_LINUX_FD_H # include #endif +#include + #include "common.h" #include "rmt.h" @@ -29,105 +44,85 @@ #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; } @@ -135,28 +130,25 @@ process_dumpdir (long bytes, char *buffer) 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; } @@ -173,98 +165,9 @@ read_and_process (long size, int (*processor) (long, char *)) } } -/*---. -| ? | -`---*/ - -/* 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. */ @@ -273,56 +176,68 @@ fill_in_sparse_array (void) 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; @@ -338,27 +253,29 @@ diff_sparse_files (int size_of_file) 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; @@ -370,6 +287,8 @@ diff_sparse_files (int size_of_file) 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); @@ -381,7 +300,7 @@ diff_sparse_files (int size_of_file) /* 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 @@ -389,71 +308,56 @@ diff_sparse_files (int size_of_file) 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: @@ -463,92 +367,83 @@ diff_archive (void) /* 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; @@ -556,33 +451,21 @@ diff_archive (void) #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; @@ -592,80 +475,61 @@ diff_archive (void) } #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; } @@ -673,49 +537,46 @@ diff_archive (void) 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: @@ -725,73 +586,65 @@ diff_archive (void) { 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) { @@ -829,12 +682,10 @@ 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 @@ -849,17 +700,19 @@ verify_volume (void) 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)); } diff --git a/src/create.c b/src/create.c index b4b487f..1c5a204 100644 --- a/src/create.c +++ b/src/create.c @@ -359,7 +359,7 @@ write_eot (void) /* 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) @@ -367,10 +367,10 @@ 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; @@ -400,7 +400,7 @@ write_long (const char *p, char 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; @@ -411,7 +411,7 @@ start_header (const char *name, struct stat *st) 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'; @@ -419,12 +419,12 @@ start_header (const char *name, struct stat *st) /* 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 @@ -450,22 +450,22 @@ start_header (const char *name, struct stat *st) 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; @@ -496,8 +496,8 @@ start_header (const char *name, struct stat *st) } 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; @@ -541,7 +541,7 @@ finish_header (union block *header, off_t block_ordinal) /* 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); } @@ -859,7 +859,7 @@ static Hash_table *link_table; 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. */ @@ -881,7 +881,7 @@ dump_file (char *p, int top_level, dev_t parent_device) 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); @@ -890,12 +890,12 @@ dump_file (char *p, int top_level, dev_t parent_device) 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) @@ -911,9 +911,9 @@ dump_file (char *p, int top_level, dev_t parent_device) 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"), @@ -925,7 +925,7 @@ dump_file (char *p, int top_level, dev_t parent_device) #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))); @@ -933,7 +933,7 @@ dump_file (char *p, int top_level, dev_t parent_device) } #endif - if (S_ISDIR (current_stat.st_mode)) + if (S_ISDIR (current_stat_info.stat.st_mode)) { char *directory; char const *entry; @@ -941,7 +941,7 @@ dump_file (char *p, int top_level, dev_t parent_device) 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; @@ -979,18 +979,18 @@ dump_file (char *p, int top_level, dev_t parent_device) 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; @@ -1063,7 +1063,7 @@ dump_file (char *p, int top_level, dev_t parent_device) 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, @@ -1104,12 +1104,12 @@ dump_file (char *p, int top_level, dev_t parent_device) { /* 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))) { @@ -1121,10 +1121,10 @@ dump_file (char *p, int top_level, dev_t parent_device) 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. */ @@ -1146,8 +1146,8 @@ dump_file (char *p, int top_level, dev_t parent_device) /* 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; @@ -1193,14 +1193,14 @@ dump_file (char *p, int top_level, dev_t parent_device) 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; @@ -1221,15 +1221,15 @@ dump_file (char *p, int top_level, dev_t parent_device) . 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; @@ -1243,14 +1243,14 @@ dump_file (char *p, int top_level, dev_t parent_device) } } - 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 { @@ -1271,12 +1271,12 @@ dump_file (char *p, int top_level, dev_t parent_device) 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; @@ -1312,7 +1312,7 @@ dump_file (char *p, int top_level, dev_t parent_device) { if (f < 0 || finish_sparse_file (f, &sizeleft, - current_stat.st_size, p)) + current_stat_info.stat.st_size, p)) goto padit; } else @@ -1322,7 +1322,7 @@ dump_file (char *p, int top_level, dev_t parent_device) { 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 (); @@ -1346,7 +1346,7 @@ dump_file (char *p, int top_level, dev_t parent_device) (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; @@ -1428,12 +1428,12 @@ dump_file (char *p, int top_level, dev_t parent_device) 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); @@ -1448,11 +1448,11 @@ dump_file (char *p, int top_level, dev_t parent_device) 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; @@ -1467,18 +1467,18 @@ dump_file (char *p, int top_level, dev_t parent_device) 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; @@ -1491,14 +1491,14 @@ dump_file (char *p, int top_level, dev_t parent_device) 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); @@ -1517,14 +1517,14 @@ unknown: 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 diff --git a/src/delete.c b/src/delete.c index 4cb73b3..bb09af8 100644 --- a/src/delete.c +++ b/src/delete.c @@ -1,5 +1,7 @@ /* 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 @@ -13,97 +15,95 @@ 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 (); } @@ -113,19 +113,24 @@ write_record (int move_back_flag) { /* 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) @@ -135,16 +140,17 @@ 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) { @@ -152,19 +158,24 @@ delete_archive_members (void) 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; @@ -193,144 +204,159 @@ delete_archive_members (void) 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 (); } diff --git a/src/extract.c b/src/extract.c index c070a41..d6af621 100644 --- a/src/extract.c +++ b/src/extract.c @@ -129,14 +129,15 @@ extr_init (void) } /* 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) { @@ -168,16 +169,16 @@ set_mode (char const *file_name, struct stat const *stat_info, 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) @@ -199,7 +200,7 @@ check_time (char const *file_name, time_t t) /* 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. @@ -212,8 +213,9 @@ check_time (char const *file_name, time_t t) 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) { @@ -252,7 +254,7 @@ set_stat (char const *file_name, struct stat const *stat_info, 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); } @@ -332,9 +334,9 @@ repair_delayed_set_stat (char const *dir_name, 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; } @@ -388,7 +390,7 @@ make_directories (char *file_name) 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); @@ -400,13 +402,16 @@ make_directories (char *file_name) *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 + */ #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. */ @@ -493,7 +498,7 @@ maybe_recoverable (char *file_name, int *interdir_made) 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; @@ -560,7 +565,8 @@ fill_in_sparse_array (void) 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; } @@ -640,7 +646,7 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_symlinks) 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; @@ -654,7 +660,7 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool 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); @@ -671,7 +677,7 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_symlinks) } 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; @@ -694,13 +700,12 @@ extract_archive (void) 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; @@ -711,7 +716,7 @@ extract_archive (void) 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); @@ -745,7 +750,7 @@ extract_archive (void) /* 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. */ @@ -755,7 +760,7 @@ extract_archive (void) | (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) { @@ -776,7 +781,7 @@ extract_archive (void) 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); @@ -823,16 +828,16 @@ extract_archive (void) 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; } @@ -883,7 +888,7 @@ extract_archive (void) 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), @@ -896,19 +901,19 @@ extract_archive (void) 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 { @@ -937,19 +942,19 @@ extract_archive (void) 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 @@ -1002,7 +1007,7 @@ extract_archive (void) 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; @@ -1049,13 +1054,13 @@ extract_archive (void) #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 @@ -1063,8 +1068,8 @@ extract_archive (void) 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)) @@ -1074,7 +1079,7 @@ extract_archive (void) 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 @@ -1084,13 +1089,13 @@ extract_archive (void) 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 { @@ -1114,7 +1119,7 @@ extract_archive (void) 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); @@ -1167,8 +1172,8 @@ extract_archive (void) 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)); @@ -1176,7 +1181,7 @@ extract_archive (void) 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: @@ -1186,7 +1191,7 @@ extract_archive (void) 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 (); diff --git a/src/incremen.c b/src/incremen.c index bde8dc3..4bed23c 100644 --- a/src/incremen.c +++ b/src/incremen.c @@ -1,5 +1,7 @@ /* 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 @@ -13,60 +15,47 @@ 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_t time (); - -#define ISDIGIT(Char) (ISASCII (Char) && isdigit (Char)) -#define ISSPACE(Char) (ISASCII (Char) && isspace (Char)) - +#include +#include +#include #include "common.h" /* 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) { @@ -74,236 +63,262 @@ 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; } /* 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 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. */ @@ -320,22 +335,22 @@ get_directory_contents (char *path, int device) 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++) @@ -352,322 +367,178 @@ get_directory_contents (char *path, int device) 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); - } -} -/*---. -| ? | -`---*/ +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); } /* 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) @@ -678,7 +549,7 @@ gnu_restore (int skipcrud) 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)); @@ -694,22 +565,22 @@ gnu_restore (int skipcrud) } 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 }