X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fextract.c;h=15fb9f2525c20b546bcb61e5e241329ca7eba949;hb=973b6112902adbe0a16dfec69c405ca90a843cee;hp=2689f547684a317574927089cb6069f9fc770376;hpb=c641a16cba34ec0b0f1fb41dcdd7a27c9b760a83;p=chaz%2Ftar diff --git a/src/extract.c b/src/extract.c index 2689f54..15fb9f2 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, 2004, 2005 Free Software Foundation, Inc. + 2001, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. Written by John Gilmore, on 1985-11-19. @@ -115,19 +115,6 @@ extr_init (void) same_permissions_option += we_are_root; same_owner_option += we_are_root; - /* 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. */ @@ -321,7 +308,19 @@ set_stat (char const *file_name, once we stop extracting files into that directory. If not restoring permissions, remember to invert the INVERT_PERMISSIONS bits from the file's current permissions. - PERMSTATUS specifies the status of the file's permissions. */ + PERMSTATUS specifies the status of the file's permissions. + + NOTICE: this works only if the archive has usual member order, i.e. + directory, then the files in that directory. Incremental archive have + somewhat reversed order: first go subdirectories, then all other + members. To help cope with this case the variable + delay_directory_restore_option is set by prepare_to_extract. + + If an archive was explicitely created so that its member order is + reversed, some directory timestamps can be restored incorrectly, + e.g.: + tar --no-recursion -cf archive dir dir/file1 foo dir/file2 +*/ static void delay_set_stat (char const *file_name, struct tar_stat_info const *st, mode_t invert_permissions, enum permstatus permstatus) @@ -393,13 +392,12 @@ static int make_directories (char *file_name) { char *cursor0 = file_name + FILE_SYSTEM_PREFIX_LEN (file_name); - char *cursor; /* points into the 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)) @@ -429,7 +427,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_info /* ignored */, + ¤t_stat_info, invert_permissions, INTERDIR_PERMSTATUS); print_for_mkdir (file_name, cursor - file_name, mode); @@ -469,7 +467,7 @@ file_newer_p (const char *file_name, struct tar_stat_info *tar_stat) return errno != ENOENT; } if (!S_ISDIR (st.st_mode) - && timespec_cmp (tar_stat->mtime, get_stat_mtime (&st)) <= 0) + && tar_timespec_cmp (tar_stat->mtime, get_stat_mtime (&st)) <= 0) { return true; } @@ -609,13 +607,27 @@ extract_dir (char *file_name, int typeflag) mode_t mode; int interdir_made = 0; + /* Save 'root device' to avoid purging mount points. */ + if (one_file_system_option && root_device == 0) + { + struct stat st; + char *dir = xgetcwd (); + + if (deref_stat (true, dir, &st)) + stat_diag (dir); + else + root_device = st.st_dev; + free (dir); + } + if (incremental_option) /* Read the entry and delete files that aren't listed in the archive. */ purge_directory (file_name); else if (typeflag == GNUTYPE_DUMPDIR) skip_member (); - mode = (current_stat_info.stat.st_mode | (we_are_root ? 0 : MODE_WXUSR)) & MODE_RWX; + mode = (current_stat_info.stat.st_mode | + (we_are_root ? 0 : MODE_WXUSR)) & MODE_RWX; while ((status = mkdir (file_name, mode))) { @@ -634,7 +646,7 @@ extract_dir (char *file_name, int typeflag) } if (S_ISDIR (st.st_mode)) { - mode = st.st_mode & ~ current_umask; + mode = st.st_mode; break; } } @@ -655,12 +667,16 @@ extract_dir (char *file_name, int typeflag) if (status == 0 || old_files_option == DEFAULT_OLD_FILES || old_files_option == OVERWRITE_OLD_FILES) - delay_set_stat (file_name, ¤t_stat_info, - MODE_RWX & (mode ^ current_stat_info.stat.st_mode), - (status == 0 - ? ARCHIVED_PERMSTATUS - : UNKNOWN_PERMSTATUS)); - + { + if (status == 0) + delay_set_stat (file_name, ¤t_stat_info, + MODE_RWX & (mode ^ current_stat_info.stat.st_mode), + ARCHIVED_PERMSTATUS); + else /* For an already existing directory, invert_perms must be 0 */ + delay_set_stat (file_name, ¤t_stat_info, + 0, + UNKNOWN_PERMSTATUS); + } return status; } @@ -739,17 +755,13 @@ extract_file (char *file_name, int typeflag) } } + mv_begin (¤t_stat_info); if (current_stat_info.is_sparse) sparse_extract_file (fd, ¤t_stat_info, &size); else for (size = current_stat_info.stat.st_size; size > 0; ) { - if (multi_volume_option) - { - assign_string (&save_name, current_stat_info.orig_file_name); - save_totsize = current_stat_info.stat.st_size; - save_sizeleft = size; - } + mv_size_left (size); /* Locate data, determine max length writeable, write it, block that we have used the data, then check if the write @@ -783,8 +795,7 @@ extract_file (char *file_name, int typeflag) skip_file (size); - if (multi_volume_option) - assign_string (&save_name, 0); + mv_end (); /* If writing to stdout, don't try to do anything to the filename; it doesn't exist, or we don't want to touch it anyway. */ @@ -1019,6 +1030,13 @@ extract_mangle_wrapper (char *file_name, int typeflag) return 0; } +static int +extract_volhdr (char *file_name, int typeflag) +{ + if (verbose_option) + fprintf (stdlis, _("Reading %s\n"), quote (current_stat_info.file_name)); + skip_member (); +} static int extract_failure (char *file_name, int typeflag) @@ -1094,12 +1112,12 @@ prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun) case DIRTYPE: case GNUTYPE_DUMPDIR: *fun = extract_dir; + if (current_stat_info.is_dumpdir) + delay_directory_restore_option = true; break; case GNUTYPE_VOLHDR: - if (verbose_option) - fprintf (stdlis, _("Reading %s\n"), quote (current_stat_info.file_name)); - *fun = NULL; + *fun = extract_volhdr; break; case GNUTYPE_NAMES: @@ -1164,59 +1182,49 @@ 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)) + if (!current_stat_info.file_name[0] + || (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, absolute_names_option); - 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); + /* Restore stats for all non-ancestor directories, unless + it is an incremental archive. + (see NOTICE in the comment to delay_set_stat above) */ + if (!delay_directory_restore_option) + apply_nonancestor_delayed_set_stat (current_stat_info.file_name, 0); /* Take a safety backup of a previously existing file. */ if (backup_option) - if (!maybe_backup_file (file_name, 0)) + if (!maybe_backup_file (current_stat_info.file_name, 0)) { int e = errno; ERROR ((0, e, _("%s: Was unable to backup this file"), - quotearg_colon (file_name))); + quotearg_colon (current_stat_info.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 (prepare_to_extract (current_stat_info.file_name, typeflag, &fun)) { - if (fun && (*fun) (file_name, typeflag) && backup_option) + if (fun && (*fun) (current_stat_info.file_name, typeflag) + && backup_option) undo_last_backup (); } else @@ -1305,6 +1313,39 @@ extract_finish (void) apply_nonancestor_delayed_set_stat ("", 1); } +bool +rename_directory (char *src, char *dst) +{ + if (rename (src, dst)) + { + int e = errno; + + switch (e) + { + case ENOENT: + if (make_directories (dst)) + { + if (rename (src, dst) == 0) + return true; + e = errno; + } + break; + + case EXDEV: + /* FIXME: Fall back to recursive copying */ + + default: + break; + } + + ERROR ((0, e, _("Cannot rename %s to %s"), + quote_n (0, src), + quote_n (1, dst))); + return false; + } + return true; +} + void fatal_exit (void) {