X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fextract.c;h=b0127f2840610983cafcab74a352579feadf7b02;hb=062b37115f4ab2bbdc72a128807adbb010291f8c;hp=2689f547684a317574927089cb6069f9fc770376;hpb=c641a16cba34ec0b0f1fb41dcdd7a27c9b760a83;p=chaz%2Ftar diff --git a/src/extract.c b/src/extract.c index 2689f54..b0127f2 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. @@ -37,7 +37,8 @@ enum permstatus /* This file may have existed already; its permissions are unknown. */ UNKNOWN_PERMSTATUS, - /* This file was created using the permissions from the archive. */ + /* This file was created using the permissions from the archive, + except with S_IRWXG | S_IRWXO masked out if 0 < same_owner_option. */ ARCHIVED_PERMSTATUS, /* This is an intermediate directory; the archive did not specify @@ -115,19 +116,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. */ @@ -162,12 +150,15 @@ set_mode (char const *file_name, { mode = stat_info->st_mode; - /* If we created the file and it has a usual mode, then its mode - is normally set correctly already. But on many hosts, some + /* If we created the file and it has a mode that we set already + with O_CREAT, then its mode is often set correctly already. + But if we are changing ownership, the mode's group and and + other permission bits were omitted originally, so it's less + likely that the mode is OK now. Also, on many hosts, some directories inherit the setgid bits from their parents, so we we must set directories' modes explicitly. */ - if (permstatus == ARCHIVED_PERMSTATUS - && ! (mode & ~ MODE_RWX) + if ((permstatus == ARCHIVED_PERMSTATUS + && ! (mode & ~ (0 < same_owner_option ? S_IRWXU : MODE_RWX))) && typeflag != DIRTYPE && typeflag != GNUTYPE_DUMPDIR) return; @@ -206,7 +197,7 @@ check_time (char const *file_name, struct timespec t) if (t.tv_sec <= 0) WARN ((0, 0, _("%s: implausibly old time stamp %s"), file_name, tartime (t, true))); - else if (timespec_cmp (start_time, t) < 0) + else if (timespec_cmp (volume_start_time, t) < 0) { struct timespec now; gettime (&now); @@ -230,7 +221,7 @@ check_time (char const *file_name, struct timespec t) /* Restore stat attributes (owner, group, mode and times) for FILE_NAME, using information given in *ST. If CUR_INFO is nonzero, *CUR_INFO is the - file's currernt status. + file's current status. If not restoring permissions, invert the INVERT_PERMISSIONS bits from the file's current permissions. PERMSTATUS specifies the status of the file's permissions. @@ -278,11 +269,11 @@ set_stat (char const *file_name, } /* Some systems allow non-root users to give files away. Once this - 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, &st->stat, cur_info, - invert_permissions, permstatus, typeflag); + done, it is not possible anymore to change file permissions. + However, setting file permissions now would be incorrect, since + they would apply to the wrong user, and there would be a race + condition. So, don't use systems that allow non-root users to + give files away. */ } if (0 < same_owner_option && permstatus != INTERDIR_PERMSTATUS) @@ -291,29 +282,36 @@ set_stat (char const *file_name, the symbolic link itself. In this case, a mere chown would change the attributes of the file the symbolic link is pointing to, and should be avoided. */ + int chown_result = 1; if (typeflag == SYMTYPE) { #if HAVE_LCHOWN - if (lchown (file_name, st->stat.st_uid, st->stat.st_gid) < 0) - chown_error_details (file_name, - st->stat.st_uid, st->stat.st_gid); + chown_result = lchown (file_name, st->stat.st_uid, st->stat.st_gid); #endif } else { - if (chown (file_name, st->stat.st_uid, st->stat.st_gid) < 0) - chown_error_details (file_name, - st->stat.st_uid, st->stat.st_gid); - - /* On a few systems, and in particular, those allowing to give files - away, changing the owner or group destroys the suid or sgid bits. - So let's attempt setting these bits once more. */ - if (st->stat.st_mode & (S_ISUID | S_ISGID | S_ISVTX)) - set_mode (file_name, &st->stat, 0, - invert_permissions, permstatus, typeflag); + chown_result = chown (file_name, st->stat.st_uid, st->stat.st_gid); + } + + if (chown_result == 0) + { + /* Changing the owner can flip st_mode bits in some cases, so + ignore cur_info if it might be obsolete now. */ + if (cur_info + && cur_info->st_mode & S_IXUGO + && cur_info->st_mode & (S_ISUID | S_ISGID)) + cur_info = NULL; } + else if (chown_result < 0) + chown_error_details (file_name, + st->stat.st_uid, st->stat.st_gid); } + + if (typeflag != SYMTYPE) + set_mode (file_name, &st->stat, cur_info, + invert_permissions, permstatus, typeflag); } /* Remember to restore stat attributes (owner, group, mode and times) @@ -321,7 +319,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) @@ -375,7 +385,8 @@ repair_delayed_set_stat (char const *dir, data->atime = current_stat_info.atime; data->mtime = current_stat_info.mtime; data->invert_permissions = - (MODE_RWX & (current_stat_info.stat.st_mode ^ st.st_mode)); + ((current_stat_info.stat.st_mode ^ st.st_mode) + & MODE_RWX & ~ current_umask); data->permstatus = ARCHIVED_PERMSTATUS; return; } @@ -393,13 +404,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 +439,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 +479,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 +619,28 @@ 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); + if (0 < same_owner_option || current_stat_info.stat.st_mode & ~ MODE_RWX) + mode &= S_IRWXU; while ((status = mkdir (file_name, mode))) { @@ -634,7 +659,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,25 +680,29 @@ 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 ^ current_stat_info.stat.st_mode) + & MODE_RWX & ~ current_umask), + 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; } static int -open_output_file (char *file_name, int typeflag) +open_output_file (char *file_name, int typeflag, mode_t mode) { int fd; int openflag = (O_WRONLY | O_BINARY | O_CREAT | (old_files_option == OVERWRITE_OLD_FILES ? O_TRUNC : O_EXCL)); - mode_t mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask; #if O_CTG /* Contiguous files (on the Masscomp) have to specify the size in @@ -712,6 +741,9 @@ extract_file (char *file_name, int typeflag) size_t count; size_t written; int interdir_made = 0; + mode_t mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask; + mode_t invert_permissions = + 0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) : 0; /* FIXME: deal with protection issues. */ @@ -729,27 +761,24 @@ extract_file (char *file_name, int typeflag) else { do - fd = open_output_file (file_name, typeflag); + fd = open_output_file (file_name, typeflag, mode ^ invert_permissions); while (fd < 0 && maybe_recoverable (file_name, &interdir_made)); if (fd < 0) { + skip_member (); open_error (file_name); return 1; } } + 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 +812,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. */ @@ -799,7 +827,7 @@ extract_file (char *file_name, int typeflag) if (to_command_option) sys_wait_command (); else - set_stat (file_name, ¤t_stat_info, NULL, 0, + set_stat (file_name, ¤t_stat_info, NULL, invert_permissions, (old_files_option == OVERWRITE_OLD_FILES ? UNKNOWN_PERMSTATUS : ARCHIVED_PERMSTATUS), typeflag); @@ -860,7 +888,7 @@ create_placeholder_file (char *file_name, bool is_symlink, int *interdir_made) if (h && ! h->after_links && strncmp (file_name, h->file_name, h->file_name_len) == 0 && ISSLASH (file_name[h->file_name_len]) - && (base_name (file_name) == file_name + h->file_name_len + 1)) + && (last_component (file_name) == file_name + h->file_name_len + 1)) { do { @@ -886,10 +914,12 @@ create_placeholder_file (char *file_name, bool is_symlink, int *interdir_made) static int extract_link (char *file_name, int typeflag) { - char const *link_name = safer_name_suffix (current_stat_info.link_name, - true, absolute_names_option); int interdir_made = 0; + char const *link_name; + transform_member_name (¤t_stat_info.link_name, true); + link_name = current_stat_info.link_name; + if (! absolute_names_option && contains_dot_dot (link_name)) return create_placeholder_file (file_name, false, &interdir_made); @@ -944,6 +974,8 @@ extract_symlink (char *file_name, int typeflag) int status; int interdir_made = 0; + transform_member_name (¤t_stat_info.link_name, true); + if (! absolute_names_option && (IS_ABSOLUTE_FILE_NAME (current_stat_info.link_name) || contains_dot_dot (current_stat_info.link_name))) @@ -977,16 +1009,19 @@ extract_node (char *file_name, int typeflag) { int status; int interdir_made = 0; + mode_t mode = current_stat_info.stat.st_mode & ~ current_umask; + mode_t invert_permissions = + 0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) : 0; do - status = mknod (file_name, current_stat_info.stat.st_mode, + status = mknod (file_name, mode ^ invert_permissions, current_stat_info.stat.st_rdev); while (status && maybe_recoverable (file_name, &interdir_made)); if (status != 0) mknod_error (file_name); else - set_stat (file_name, ¤t_stat_info, NULL, 0, + set_stat (file_name, ¤t_stat_info, NULL, invert_permissions, ARCHIVED_PERMSTATUS, typeflag); return status; } @@ -998,13 +1033,16 @@ extract_fifo (char *file_name, int typeflag) { int status; int interdir_made = 0; + mode_t mode = current_stat_info.stat.st_mode & ~ current_umask; + mode_t invert_permissions = + 0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) : 0; - while ((status = mkfifo (file_name, current_stat_info.stat.st_mode))) + while ((status = mkfifo (file_name, mode)) != 0) if (!maybe_recoverable (file_name, &interdir_made)) break; if (status == 0) - set_stat (file_name, ¤t_stat_info, NULL, 0, + set_stat (file_name, ¤t_stat_info, NULL, invert_permissions, ARCHIVED_PERMSTATUS, typeflag); else mkfifo_error (file_name); @@ -1013,13 +1051,14 @@ extract_fifo (char *file_name, int typeflag) #endif static int -extract_mangle_wrapper (char *file_name, int typeflag) +extract_volhdr (char *file_name, int typeflag) { - extract_mangle (); + if (verbose_option) + fprintf (stdlis, _("Reading %s\n"), quote (current_stat_info.file_name)); + skip_member (); return 0; } - static int extract_failure (char *file_name, int typeflag) { @@ -1094,16 +1133,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; - break; - - case GNUTYPE_NAMES: - *fun = extract_mangle_wrapper; + *fun = extract_volhdr; break; case GNUTYPE_MULTIVOL: @@ -1164,59 +1199,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 +1330,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) {