X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fextract.c;h=f348b822ed6620c2dcdd3854cdd460e9dc1166eb;hb=75bf036edc1425eb66dcc9623846c7204f943ce6;hp=d6af621c4307cf37130fef21838efc99a3183a32;hpb=d858a433c82626309e0d974ca923a79ac7f7f18f;p=chaz%2Ftar diff --git a/src/extract.c b/src/extract.c index d6af621..f348b82 100644 --- a/src/extract.c +++ b/src/extract.c @@ -1,7 +1,7 @@ /* Extract files from a tar archive. Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000, - 2001, 2003 Free Software Foundation, Inc. + 2001, 2003, 2004 Free Software Foundation, Inc. Written by John Gilmore, on 1985-11-19. @@ -19,8 +19,10 @@ with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#include "system.h" +#include #include +#include +#include #if HAVE_UTIME_H # include @@ -112,8 +114,20 @@ extr_init (void) we_are_root = geteuid () == 0; same_permissions_option += we_are_root; same_owner_option += we_are_root; - xalloc_fail_func = extract_finish; + /* Save 'root device' to avoid purging mount points. + FIXME: Should the same be done after handling -C option ? */ + if (one_file_system_option) + { + struct stat st; + char *dir = xgetcwd (); + + if (deref_stat (true, dir, &st)) + stat_diag (dir); + else + root_device = st.st_dev; + } + /* Option -p clears the kernel umask, so it does not affect proper restoration of file permissions. New intermediate directories will comply with umask at start of program. */ @@ -314,11 +328,11 @@ delay_set_stat (char const *file_name, struct stat const *stat_info, } /* Update the delayed_set_stat info for an intermediate directory - created on the path to DIR_NAME. The intermediate directory turned + created within the file name of DIR. The intermediate directory turned out to be the same as this directory, e.g. due to ".." or symbolic links. *DIR_STAT_INFO is the status of the directory. */ static void -repair_delayed_set_stat (char const *dir_name, +repair_delayed_set_stat (char const *dir, struct stat const *dir_stat_info) { struct delayed_set_stat *data; @@ -343,7 +357,7 @@ repair_delayed_set_stat (char const *dir_name, } ERROR ((0, 0, _("%s: Unexpected inconsistency when making directory"), - quotearg_colon (dir_name))); + quotearg_colon (dir))); } /* After a file/link/symlink/directory creation has failed, see if @@ -353,14 +367,14 @@ repair_delayed_set_stat (char const *dir_name, static int make_directories (char *file_name) { - char *cursor0 = file_name + FILESYSTEM_PREFIX_LEN (file_name); - char *cursor; /* points into path */ + char *cursor0 = file_name + FILE_SYSTEM_PREFIX_LEN (file_name); + char *cursor; /* points into the file name */ int did_something = 0; /* did we do anything yet? */ int mode; int invert_permissions; int status; - + for (cursor = cursor0; *cursor; cursor++) { if (! ISSLASH (*cursor)) @@ -371,7 +385,7 @@ make_directories (char *file_name) if (cursor == cursor0 || ISSLASH (cursor[-1])) continue; - /* Avoid mkdir where last part of path is "." or "..". */ + /* Avoid mkdir where last part of file name is "." or "..". */ if (cursor[-1] == '.' && (cursor == cursor0 + 1 || ISSLASH (cursor[-2]) @@ -379,7 +393,7 @@ make_directories (char *file_name) && (cursor == cursor0 + 2 || ISSLASH (cursor[-3]))))) continue; - *cursor = '\0'; /* truncate the path there */ + *cursor = '\0'; /* truncate the name there */ mode = MODE_RWX & ~ newdir_umask; invert_permissions = we_are_root ? 0 : MODE_WXUSR & ~ mode; status = mkdir (file_name, mode ^ invert_permissions); @@ -407,10 +421,7 @@ make_directories (char *file_name) else if ((errno == ENOSYS /* Automounted dirs on Solaris return this. Reported by Warren Hyde */ -#if MSDOS - || errno == EACCES /* Turbo C mkdir gives a funny errno. */ -#endif - ) + || ERRNO_IS_EACCES) /* Turbo C mkdir gives a funny errno. */ && access (file_name, W_OK) == 0) continue; @@ -421,21 +432,56 @@ make_directories (char *file_name) return did_something; /* tell them to retry if we made one */ } +static bool +file_newer_p (const char *file_name, struct tar_stat_info *tar_stat) +{ + struct stat st; + + if (stat (file_name, &st)) + { + stat_warn (file_name); + return true; /* Be on the safe side */ + } + if (!S_ISDIR (st.st_mode) + && st.st_mtime >= tar_stat->stat.st_mtime) + { + return true; + } + return false; +} + /* Prepare to extract a file. Return zero if extraction should not proceed. */ static int -prepare_to_extract (char const *file_name, bool directory) +prepare_to_extract (char const *file_name) { if (to_stdout_option) return 0; - if (old_files_option == UNLINK_FIRST_OLD_FILES - && !remove_any_file (file_name, recursive_unlink_option) - && errno && errno != ENOENT) + switch (old_files_option) { - unlink_error (file_name); - return 0; + case UNLINK_FIRST_OLD_FILES: + if (!remove_any_file (file_name, + recursive_unlink_option ? RECURSIVE_REMOVE_OPTION + : ORDINARY_REMOVE_OPTION) + && errno && errno != ENOENT) + { + unlink_error (file_name); + return 0; + } + break; + + case KEEP_NEWER_FILES: + if (file_newer_p (file_name, ¤t_stat_info)) + { + WARN ((0, 0, _("Current `%s' is newer"), file_name)); + return 0; + } + break; + + default: + break; } return 1; @@ -448,6 +494,8 @@ prepare_to_extract (char const *file_name, bool directory) static int maybe_recoverable (char *file_name, int *interdir_made) { + int e = errno; + if (*interdir_made) return 0; @@ -458,17 +506,28 @@ maybe_recoverable (char *file_name, int *interdir_made) switch (old_files_option) { - default: + case KEEP_OLD_FILES: return 0; + case KEEP_NEWER_FILES: + if (file_newer_p (file_name, ¤t_stat_info)) + { + errno = e; + return 0; + } + /* FALL THROUGH */ + case DEFAULT_OLD_FILES: case NO_OVERWRITE_DIR_OLD_FILES: case OVERWRITE_OLD_FILES: { - int r = remove_any_file (file_name, 0); + int r = remove_any_file (file_name, ORDINARY_REMOVE_OPTION); errno = EEXIST; return r; } + + case UNLINK_FIRST_OLD_FILES: + break; } case ENOENT: @@ -488,148 +547,6 @@ maybe_recoverable (char *file_name, int *interdir_made) } } -/* 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. Return nonzero if - successful. - - This function invalidates current_header. */ - -bool -fill_in_sparse_array (void) -{ - 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; - union block *h = current_header; - - init_sparsearray (); - - for (sparses = 0; sparses < SPARSES_IN_OLDGNU_HEADER; sparses++) - { - struct sparse const *s = &h->oldgnu_header.sp[sparses]; - off_t offset; - size_t numbytes; - if (s->numbytes[0] == '\0') - break; - sparsearray[sparses].offset = offset = OFF_FROM_HEADER (s->offset); - sparsearray[sparses].numbytes = numbytes = - SIZE_FROM_HEADER (s->numbytes); - sparse_data_size -= numbytes; - if (offset < 0 || file_size < offset + numbytes || sparse_data_size < 0) - goto invalid_member; - } - - if (h->oldgnu_header.isextended) - do - { - h = find_next_block (); - if (! h) - { - ERROR ((0, 0, _("Unexpected EOF in archive"))); - return 0; - } - - for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++) - { - struct sparse const *s = &h->sparse_header.sp[counter]; - off_t offset; - size_t numbytes; - if (s->numbytes[0] == '\0') - break; - - if (sparses == sp_array_size) - { - sp_array_size *= 2; - sparsearray = - xrealloc (sparsearray, - sp_array_size * sizeof *sparsearray); - } - - sparsearray[sparses].offset = offset = - OFF_FROM_HEADER (s->offset); - sparsearray[sparses].numbytes = numbytes = - SIZE_FROM_HEADER (s->numbytes); - sparse_data_size -= numbytes; - if (offset < 0 || file_size < offset + numbytes - || sparse_data_size < 0) - goto invalid_member; - sparses++; - } - - set_next_block_after (h); - - } while (h->sparse_header.isextended); - - return 1; - - invalid_member: - ERROR ((0, 0, _("%s: invalid sparse archive member"), - current_stat_info.file_name)); - return 0; -} - - -static off_t -extract_sparse_file (int fd, char const *name, - off_t sizeleft, off_t file_size) -{ - int sparse_ind = 0; - - while (sizeleft != 0) - { - size_t written; - size_t count; - union block *data_block = find_next_block (); - if (! data_block) - { - ERROR ((0, 0, _("Unexpected EOF in archive"))); - return sizeleft; - } - if (lseek (fd, sparsearray[sparse_ind].offset, SEEK_SET) < 0) - { - seek_error_details (name, sparsearray[sparse_ind].offset); - return sizeleft; - } - written = sparsearray[sparse_ind++].numbytes; - while (written > BLOCKSIZE) - { - count = full_write (fd, data_block->buffer, BLOCKSIZE); - written -= count; - sizeleft -= count; - if (count != BLOCKSIZE) - { - write_error_details (name, count, BLOCKSIZE); - return sizeleft; - } - set_next_block_after (data_block); - data_block = find_next_block (); - if (! data_block) - { - ERROR ((0, 0, _("Unexpected EOF in archive"))); - return sizeleft; - } - } - - count = full_write (fd, data_block->buffer, written); - sizeleft -= count; - - if (count != written) - { - write_error_details (name, count, written); - return sizeleft; - } - - set_next_block_after (data_block); - } - - if (ftruncate (fd, file_size) != 0) - truncate_error (name); - - return 0; -} - /* Fix the statuses of all directories whose statuses need fixing, and which are not ancestors of FILE_NAME. If AFTER_SYMLINKS is nonzero, do this for all such directories; otherwise, stop at the @@ -697,7 +614,6 @@ extract_archive (void) int openflag; mode_t mode; off_t size; - off_t file_size; int interdir_made = 0; char typeflag; char *file_name; @@ -714,9 +630,19 @@ extract_archive (void) /* Print the block from current_header and current_stat. */ if (verbose_option) - print_header (-1); + print_header (¤t_stat_info, -1); - file_name = safer_name_suffix (current_stat_info.file_name, 0); + file_name = safer_name_suffix (current_stat_info.file_name, false); + if (strip_name_components) + { + size_t prefix_len = stripped_prefix_len (file_name, strip_name_components); + if (prefix_len == (size_t) -1) + { + skip_member (); + return; + } + file_name += prefix_len; + } apply_nonancestor_delayed_set_stat (file_name, 0); @@ -734,13 +660,13 @@ extract_archive (void) /* Extract the archive entry according to its type. */ - typeflag = current_header->header.typeflag; + /* KLUDGE */ + typeflag = sparse_member_p (¤t_stat_info) ? + GNUTYPE_SPARSE : current_header->header.typeflag; + switch (typeflag) { case GNUTYPE_SPARSE: - file_size = OFF_FROM_HEADER (current_header->oldgnu_header.realsize); - if (! fill_in_sparse_array ()) - return; /* Fall through. */ case AREGTYPE: @@ -768,7 +694,7 @@ extract_archive (void) goto extract_file; } - if (! prepare_to_extract (file_name, 0)) + if (! prepare_to_extract (file_name)) { skip_member (); if (backup_option) @@ -813,23 +739,9 @@ extract_archive (void) } extract_file: - if (typeflag == GNUTYPE_SPARSE) + if (current_stat_info.is_sparse) { - char *name; - size_t name_length_bis; - - /* Kludge alert. NAME is assigned to header.name because - during the extraction, the space that contains the header - will get scribbled on, and the name will get munged, so any - error messages that happen to contain the filename will look - REAL interesting unless we do this. */ - - name_length_bis = strlen (file_name) + 1; - name = xmalloc (name_length_bis); - memcpy (name, file_name, name_length_bis); - size = extract_sparse_file (fd, name, - current_stat_info.stat.st_size, file_size); - free (sparsearray); + sparse_extract_file (fd, ¤t_stat_info, &size); } else for (size = current_stat_info.stat.st_size; size > 0; ) @@ -897,12 +809,11 @@ extract_archive (void) case SYMTYPE: #ifdef HAVE_SYMLINK - if (! prepare_to_extract (file_name, 0)) + if (! prepare_to_extract (file_name)) break; if (absolute_names_option - || ! (ISSLASH (current_stat_info.link_name - [FILESYSTEM_PREFIX_LEN (current_stat_info.link_name)]) + || ! (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name) || contains_dot_dot (current_stat_info.link_name))) { while (status = symlink (current_stat_info.link_name, file_name), @@ -981,7 +892,7 @@ extract_archive (void) status = 0; } } - + if (status != 0 && backup_option) undo_last_backup (); break; @@ -1002,29 +913,31 @@ extract_archive (void) #endif case LNKTYPE: - if (! prepare_to_extract (file_name, 0)) + if (! prepare_to_extract (file_name)) break; again_link: { - char const *link_name = safer_name_suffix (current_stat_info.link_name, 1); + char const *link_name = safer_name_suffix (current_stat_info.link_name, + true); struct stat st1, st2; int e; /* MSDOS does not implement links. However, djgpp's link() actually copies the file. */ status = link (link_name, file_name); + e = errno; if (status == 0) { struct delayed_symlink *ds = delayed_symlink_head; - if (ds && stat (link_name, &st1) == 0) + if (ds && lstat (link_name, &st1) == 0) for (; ds; ds = ds->next) if (ds->dev == st1.st_dev && ds->ino == st1.st_ino && ds->mtime == st1.st_mtime) { - struct string_list *p = + struct string_list *p = xmalloc (offsetof (struct string_list, string) + strlen (file_name) + 1); strcpy (p->string, file_name); @@ -1034,17 +947,20 @@ extract_archive (void) } break; } + + if ((e == EEXIST && strcmp (link_name, file_name) == 0) + || (lstat (link_name, &st1) == 0 + && lstat (file_name, &st2) == 0 + && st1.st_dev == st2.st_dev + && st1.st_ino == st2.st_ino)) + break; + + errno = e; if (maybe_recoverable (file_name, &interdir_made)) goto again_link; if (incremental_option && errno == EEXIST) break; - e = errno; - if (stat (link_name, &st1) == 0 - && stat (file_name, &st2) == 0 - && st1.st_dev == st2.st_dev - && st1.st_ino == st2.st_ino) - break; link_error (link_name, file_name); if (backup_option) @@ -1065,7 +981,7 @@ extract_archive (void) #if S_IFCHR || S_IFBLK make_node: - if (! prepare_to_extract (file_name, 0)) + if (! prepare_to_extract (file_name)) break; status = mknod (file_name, current_stat_info.stat.st_mode, @@ -1086,7 +1002,7 @@ extract_archive (void) #if HAVE_MKFIFO || defined mkfifo case FIFOTYPE: - if (! prepare_to_extract (file_name, 0)) + if (! prepare_to_extract (file_name)) break; while (status = mkfifo (file_name, current_stat_info.stat.st_mode), @@ -1114,7 +1030,7 @@ extract_archive (void) /* Read the entry and delete files that aren't listed in the archive. */ - gnu_restore (file_name); + purge_directory (file_name); } else if (typeflag == GNUTYPE_DUMPDIR) skip_member (); @@ -1123,7 +1039,7 @@ extract_archive (void) | (we_are_root ? 0 : MODE_WXUSR)) & MODE_RWX); - status = prepare_to_extract (file_name, 1); + status = prepare_to_extract (file_name); if (status == 0) break; if (status < 0) @@ -1155,7 +1071,7 @@ extract_archive (void) } errno = EEXIST; } - + if (maybe_recoverable (file_name, &interdir_made)) goto again_dir; @@ -1295,3 +1211,10 @@ fatal_exit (void) error (TAREXIT_FAILURE, 0, _("Error is not recoverable: exiting now")); abort (); } + +void +xalloc_die (void) +{ + error (0, 0, "%s", _("memory exhausted")); + fatal_exit (); +}