X-Git-Url: https://git.brokenzipper.com/gitweb?a=blobdiff_plain;f=src%2Fextract.c;h=0f75f2653e015d5575dfb296a64d0185c1072238;hb=5c35fdb275b519373e6eb34621d956d09a52bf31;hp=89b870bbfaa9551aa0e80e2abc0fbfd63ae53324;hpb=c8764440d9cb0187fcbabe26e7f6191c0148178e;p=chaz%2Ftar diff --git a/src/extract.c b/src/extract.c index 89b870b..0f75f26 100644 --- a/src/extract.c +++ b/src/extract.c @@ -17,7 +17,7 @@ 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 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include @@ -36,7 +36,7 @@ struct utimbuf #include "common.h" -bool we_are_root; /* true if our effective uid == 0 */ +static bool we_are_root; /* true if our effective uid == 0 */ static mode_t newdir_umask; /* umask when creating new directories */ static mode_t current_umask; /* current umask (which is set to 0 if -p) */ @@ -119,7 +119,7 @@ extr_init (void) FIXME: Should the same be done after handling -C option ? */ if (one_file_system_option) { - struct stat st; + struct stat st; char *dir = xgetcwd (); if (deref_stat (true, dir, &st)) @@ -127,7 +127,7 @@ extr_init (void) 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. */ @@ -440,7 +440,8 @@ file_newer_p (const char *file_name, struct tar_stat_info *tar_stat) if (stat (file_name, &st)) { stat_warn (file_name); - return true; /* Be on the safe side */ + /* Be on the safe side: if the file does exist assume it is newer */ + return errno != ENOENT; } if (!S_ISDIR (st.st_mode) && st.st_mtime >= tar_stat->stat.st_mtime) @@ -450,43 +451,6 @@ file_newer_p (const char *file_name, struct tar_stat_info *tar_stat) return false; } -/* Prepare to extract a file. - Return zero if extraction should not proceed. */ - -static int -prepare_to_extract (char const *file_name) -{ - if (to_stdout_option) - return 0; - - switch (old_files_option) - { - 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"), quote (file_name))); - return 0; - } - break; - - default: - break; - } - - return 1; -} - /* Attempt repairing what went wrong with the extraction. Delete an already existing file or create missing intermediate directories. Return nonzero if we somewhat increased our chances at a successful @@ -602,14 +566,17 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_symlinks) } } + -void +/* Extractor functions for various member types */ + +static int extract_dir (char *file_name, int typeflag) { int status; mode_t mode; int interdir_made = 0; - + if (incremental_option) /* Read the entry and delete files that aren't listed in the archive. */ purge_directory (file_name); @@ -618,47 +585,40 @@ extract_dir (char *file_name, int typeflag) mode = (current_stat_info.stat.st_mode | (we_are_root ? 0 : MODE_WXUSR)) & MODE_RWX; - status = prepare_to_extract (file_name); - if (status == 0) - return; - - if (status > 0) - while ((status = mkdir (file_name, mode))) - { - if (errno == EEXIST - && (interdir_made - || old_files_option == DEFAULT_OLD_FILES - || old_files_option == OVERWRITE_OLD_FILES)) - { - struct stat st; - if (stat (file_name, &st) == 0) - { - if (interdir_made) - { - repair_delayed_set_stat (file_name, &st); - return; - } - if (S_ISDIR (st.st_mode)) - { - mode = st.st_mode & ~ current_umask; - break; - } - } - errno = EEXIST; - } - - if (maybe_recoverable (file_name, &interdir_made)) - continue; + while ((status = mkdir (file_name, mode))) + { + if (errno == EEXIST + && (interdir_made + || old_files_option == DEFAULT_OLD_FILES + || old_files_option == OVERWRITE_OLD_FILES)) + { + struct stat st; + if (stat (file_name, &st) == 0) + { + if (interdir_made) + { + repair_delayed_set_stat (file_name, &st); + return 0; + } + if (S_ISDIR (st.st_mode)) + { + mode = st.st_mode & ~ current_umask; + break; + } + } + errno = EEXIST; + } - if (errno != EEXIST) - { - mkdir_error (file_name); - if (backup_option) - undo_last_backup (); - return; - } - break; - } + if (maybe_recoverable (file_name, &interdir_made)) + continue; + + if (errno != EEXIST) + { + mkdir_error (file_name); + return 1; + } + break; + } if (status == 0 || old_files_option == DEFAULT_OLD_FILES @@ -668,6 +628,8 @@ extract_dir (char *file_name, int typeflag) (status == 0 ? ARCHIVED_PERMSTATUS : UNKNOWN_PERMSTATUS)); + + return status; } @@ -704,11 +666,11 @@ open_output_file (char *file_name, int typeflag) fd = open (file_name, openflag, mode); #endif /* not O_CTG */ - + return fd; } -static void +static int extract_file (char *file_name, int typeflag) { int fd; @@ -718,36 +680,33 @@ extract_file (char *file_name, int typeflag) size_t count; size_t written; int interdir_made = 0; - + /* FIXME: deal with protection issues. */ - do + if (to_stdout_option) + fd = STDOUT_FILENO; + else if (to_command_option) { - if (to_stdout_option) - fd = STDOUT_FILENO; - else + fd = sys_exec_command (file_name, 'f', ¤t_stat_info); + if (fd < 0) { - if (! prepare_to_extract (file_name)) - { - skip_member (); - if (backup_option) - undo_last_backup (); - return; - } - fd = open_output_file (file_name, typeflag); + skip_member (); + return 0; } } - while (fd < 0 && maybe_recoverable (file_name, &interdir_made)); - - if (fd < 0) + else { - open_error (file_name); - skip_member (); - if (backup_option) - undo_last_backup (); - return; + do + fd = open_output_file (file_name, typeflag); + while (fd < 0 && maybe_recoverable (file_name, &interdir_made)); + + if (fd < 0) + { + open_error (file_name); + return 1; + } } - + if (current_stat_info.is_sparse) sparse_extract_file (fd, ¤t_stat_info, &size); else @@ -755,7 +714,7 @@ extract_file (char *file_name, int typeflag) { if (multi_volume_option) { - assign_string (&save_name, current_stat_info.file_name); + assign_string (&save_name, current_stat_info.orig_file_name); save_totsize = current_stat_info.stat.st_size; save_sizeleft = size; } @@ -763,7 +722,7 @@ extract_file (char *file_name, int typeflag) /* Locate data, determine max length writeable, write it, block that we have used the data, then check if the write worked. */ - + data_block = find_next_block (); if (! data_block) { @@ -772,22 +731,24 @@ extract_file (char *file_name, int typeflag) } written = available_space_after (data_block); - + if (written > size) written = size; errno = 0; count = full_write (fd, data_block->buffer, written); - size -= count; - + size -= written; + set_next_block_after ((union block *) (data_block->buffer + written - 1)); if (count != written) { - write_error_details (file_name, count, written); + if (!to_command_option) + write_error_details (file_name, count, written); + /* FIXME: shouldn't we restore from backup? */ break; } } - + skip_file (size); if (multi_volume_option) @@ -797,38 +758,33 @@ extract_file (char *file_name, int typeflag) it doesn't exist, or we don't want to touch it anyway. */ if (to_stdout_option) - return; + return 0; status = close (fd); if (status < 0) - { - close_error (file_name); - if (backup_option) - undo_last_backup (); - } + close_error (file_name); - set_stat (file_name, ¤t_stat_info.stat, 0, 0, - (old_files_option == OVERWRITE_OLD_FILES - ? UNKNOWN_PERMSTATUS - : ARCHIVED_PERMSTATUS), - typeflag); -} + if (to_command_option) + sys_wait_command (); + else + set_stat (file_name, ¤t_stat_info.stat, 0, 0, + (old_files_option == OVERWRITE_OLD_FILES ? + UNKNOWN_PERMSTATUS : ARCHIVED_PERMSTATUS), + typeflag); -static void -extract_link (char *file_name) + return status; +} + +static int +extract_link (char *file_name, int typeflag) { - char const *link_name; + char const *link_name = safer_name_suffix (current_stat_info.link_name, true); int interdir_made = 0; - - if (!prepare_to_extract (file_name)) - return; do { struct stat st1, st2; int e; - link_name = safer_name_suffix (current_stat_info.link_name, true); - int status = link (link_name, file_name); e = errno; @@ -848,15 +804,15 @@ extract_link (char *file_name) ds->sources = p; break; } - return; + return 0; } else 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)) - return; - + return 0; + errno = e; } while (maybe_recoverable (file_name, &interdir_made)); @@ -864,27 +820,23 @@ extract_link (char *file_name) if (!(incremental_option && errno == EEXIST)) { link_error (link_name, file_name); - if (backup_option) - undo_last_backup (); + return 1; } -} + return 0; +} -static void -extract_symlink (char *file_name) +static int +extract_symlink (char *file_name, int typeflag) { #ifdef HAVE_SYMLINK int status, fd; int interdir_made = 0; - - if (! prepare_to_extract (file_name)) - return; if (absolute_names_option || ! (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), - status != 0) + while ((status = symlink (current_stat_info.link_name, file_name))) if (!maybe_recoverable (file_name, &interdir_made)) break; @@ -942,7 +894,7 @@ extract_symlink (char *file_name) do { h->after_symlinks = 1; - + if (stat (h->file_name, &st) != 0) stat_error (h->file_name); else @@ -953,13 +905,12 @@ extract_symlink (char *file_name) } while ((h = h->next) && ! h->after_symlinks); } - + status = 0; } } - if (status != 0 && backup_option) - undo_last_backup (); + return status; #else static int warned_once; @@ -969,47 +920,36 @@ extract_symlink (char *file_name) warned_once = 1; WARN ((0, 0, _("Attempting extraction of symbolic links as hard links"))); } - extract_link (file_name); + return extract_link (file_name, typeflag); #endif -} +} #if S_IFCHR || S_IFBLK -static void +static int extract_node (char *file_name, int typeflag) { int status; int interdir_made = 0; - + do - { - if (! prepare_to_extract (file_name)) - return; - - status = mknod (file_name, current_stat_info.stat.st_mode, - current_stat_info.stat.st_rdev); - } + status = mknod (file_name, current_stat_info.stat.st_mode, + current_stat_info.stat.st_rdev); while (status && maybe_recoverable (file_name, &interdir_made)); - + if (status != 0) - { - mknod_error (file_name); - if (backup_option) - undo_last_backup (); - } + mknod_error (file_name); else set_stat (file_name, ¤t_stat_info.stat, 0, 0, ARCHIVED_PERMSTATUS, typeflag); + return status; } #endif #if HAVE_MKFIFO || defined mkfifo -static void +static int extract_fifo (char *file_name, int typeflag) { int status; int interdir_made = 0; - - if (! prepare_to_extract (file_name)) - return; while ((status = mkfifo (file_name, current_stat_info.stat.st_mode))) if (!maybe_recoverable (file_name, &interdir_made)) @@ -1019,148 +959,220 @@ extract_fifo (char *file_name, int typeflag) set_stat (file_name, ¤t_stat_info.stat, NULL, 0, ARCHIVED_PERMSTATUS, typeflag); else - { - mkfifo_error (file_name); - if (backup_option) - undo_last_backup (); - } -} + mkfifo_error (file_name); + return status; +} #endif -/* Extract a file from the archive. */ -void -extract_archive (void) +static int +extract_mangle_wrapper (char *file_name, int typeflag) { - char typeflag; - char *file_name; - - set_next_block_after (current_header); - decode_header (current_header, ¤t_stat_info, ¤t_format, 1); - - if (interactive_option && !confirm ("extract", current_stat_info.file_name)) - { - skip_member (); - return; - } - - /* Print the block from current_header and current_stat. */ + extract_mangle (); + return 0; +} - if (verbose_option) - print_header (¤t_stat_info, -1); - 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; - } +static int +extract_failure (char *file_name, int typeflag) +{ + return 1; +} - apply_nonancestor_delayed_set_stat (file_name, 0); +typedef int (*tar_extractor_t) (char *file_name, int typeflag); - /* Take a safety backup of a previously existing file. */ + - if (backup_option && !to_stdout_option) - if (!maybe_backup_file (file_name, 0)) - { - int e = errno; - ERROR ((0, e, _("%s: Was unable to backup this file"), - quotearg_colon (file_name))); - skip_member (); - return; - } +/* Prepare to extract a file. Find extractor function. + Return zero if extraction should not proceed. */ - /* Extract the archive entry according to its type. */ +static int +prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun) +{ + int rc = 1; - /* KLUDGE */ - typeflag = sparse_member_p (¤t_stat_info) ? - GNUTYPE_SPARSE : current_header->header.typeflag; + if (EXTRACT_OVER_PIPE) + rc = 0; + /* Select the extractor */ switch (typeflag) { - case GNUTYPE_SPARSE: /* FIXME: Shouldn't we call extract_file at once? */ + case GNUTYPE_SPARSE: + *fun = extract_file; + rc = 1; + break; + case AREGTYPE: case REGTYPE: case CONTTYPE: - /* Appears to be a file. But BSD tar uses the convention that a slash suffix means a directory. */ - if (current_stat_info.had_trailing_slash) - extract_dir (file_name, typeflag); + *fun = extract_dir; else - extract_file (file_name, typeflag); + { + *fun = extract_file; + rc = 1; + } break; case SYMTYPE: - extract_symlink (file_name); + *fun = extract_symlink; break; - + case LNKTYPE: - extract_link (file_name); + *fun = extract_link; break; #if S_IFCHR case CHRTYPE: current_stat_info.stat.st_mode |= S_IFCHR; - extract_node (file_name, typeflag); + *fun = extract_node; break; #endif #if S_IFBLK case BLKTYPE: current_stat_info.stat.st_mode |= S_IFBLK; - extract_node (file_name, typeflag); + *fun = extract_node; break; #endif #if HAVE_MKFIFO || defined mkfifo case FIFOTYPE: - extract_fifo (file_name, typeflag); + *fun = extract_fifo; break; #endif case DIRTYPE: case GNUTYPE_DUMPDIR: - extract_dir (file_name, typeflag); + *fun = extract_dir; break; case GNUTYPE_VOLHDR: if (verbose_option) fprintf (stdlis, _("Reading %s\n"), quote (current_stat_info.file_name)); + *fun = NULL; break; case GNUTYPE_NAMES: - extract_mangle (); + *fun = extract_mangle_wrapper; break; case GNUTYPE_MULTIVOL: ERROR ((0, 0, _("%s: Cannot extract -- file is continued from another volume"), quotearg_colon (current_stat_info.file_name))); - skip_member (); - if (backup_option) - undo_last_backup (); + *fun = extract_failure; break; case GNUTYPE_LONGNAME: case GNUTYPE_LONGLINK: ERROR ((0, 0, _("Unexpected long name header"))); - skip_member (); - if (backup_option) - undo_last_backup (); + *fun = extract_failure; break; default: WARN ((0, 0, - _("%s: Unknown file type '%c', extracted as normal file"), + _("%s: Unknown file type `%c', extracted as normal file"), quotearg_colon (file_name), typeflag)); - extract_file (file_name, typeflag); + *fun = extract_file; } + + /* Determine whether the extraction should proceed */ + if (rc == 0) + return 0; + + switch (old_files_option) + { + 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 or same age"), + quote (file_name))); + return 0; + } + break; + + default: + break; + } + + return 1; +} + +/* Extract a file from the archive. */ +void +extract_archive (void) +{ + char typeflag; + char *file_name; + tar_extractor_t fun; + + set_next_block_after (current_header); + decode_header (current_header, ¤t_stat_info, ¤t_format, 1); + + if (interactive_option && !confirm ("extract", current_stat_info.file_name)) + { + skip_member (); + return; + } + + /* Print the block from current_header and current_stat. */ + + if (verbose_option) + print_header (¤t_stat_info, -1); + + 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); + + /* Take a safety backup of a previously existing file. */ + + if (backup_option) + if (!maybe_backup_file (file_name, 0)) + { + int e = errno; + ERROR ((0, e, _("%s: Was unable to backup this file"), + quotearg_colon (file_name))); + skip_member (); + return; + } + + /* Extract the archive entry according to its type. */ + + /* KLUDGE */ + typeflag = sparse_member_p (¤t_stat_info) ? + GNUTYPE_SPARSE : current_header->header.typeflag; + + if (prepare_to_extract (file_name, typeflag, &fun)) + { + if (fun && (*fun) (file_name, typeflag) && backup_option) + undo_last_backup (); + } + else + skip_member (); + } /* Extract the symbolic links whose final extraction were delayed. */