X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fextract.c;h=f16baadc62fcb7fd88c2262c5d30bfdbfb2ca411;hb=6f89b1fce52931364a16e9b1f16efc8c33bd27e0;hp=80f2f9ceec68d1616d311680c6537e30cb83b348;hpb=2f800ce3fb9c38360e1b6dac70ee0d8ceae72c6a;p=chaz%2Ftar diff --git a/src/extract.c b/src/extract.c index 80f2f9c..f16baad 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. @@ -57,7 +57,13 @@ enum permstatus struct delayed_set_stat { struct delayed_set_stat *next; - struct stat stat_info; + dev_t dev; + ino_t ino; + mode_t mode; + uid_t uid; + gid_t gid; + struct timespec atime; + struct timespec mtime; size_t file_name_len; mode_t invert_permissions; enum permstatus permstatus; @@ -76,7 +82,7 @@ struct delayed_link /* The device, inode number and last-modified time of the placeholder. */ dev_t dev; ino_t ino; - time_t mtime; + struct timespec mtime; /* True if the link is symbolic. */ bool is_symlink; @@ -109,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. */ @@ -206,23 +199,23 @@ check_time (char const *file_name, struct timespec t) gettime (&now); if (timespec_cmp (now, t) < 0) { - unsigned long int ds = t.tv_sec - now.tv_sec; - int dns = t.tv_nsec - now.tv_nsec; - char dnsbuf[sizeof ".FFFFFFFFF"]; - if (dns < 0) + char buf[TIMESPEC_STRSIZE_BOUND]; + struct timespec diff; + diff.tv_sec = t.tv_sec - now.tv_sec; + diff.tv_nsec = t.tv_nsec - now.tv_nsec; + if (diff.tv_nsec < 0) { - dns += 1000000000; - ds--; + diff.tv_nsec += BILLION; + diff.tv_sec--; } - code_ns_fraction (dns, dnsbuf); - WARN ((0, 0, _("%s: time stamp %s is %lu%s s in the future"), - file_name, tartime (t, true), ds, dnsbuf)); + WARN ((0, 0, _("%s: time stamp %s is %s s in the future"), + file_name, tartime (t, true), code_timespec (diff, buf))); } } } /* Restore stat attributes (owner, group, mode and times) for - FILE_NAME, using information given in *STAT_INFO. + FILE_NAME, using information given in *ST. If CUR_INFO is nonzero, *CUR_INFO is the file's currernt status. If not restoring permissions, invert the @@ -237,7 +230,7 @@ check_time (char const *file_name, struct timespec t) static void set_stat (char const *file_name, - struct stat const *stat_info, + struct tar_stat_info const *st, struct stat const *cur_info, mode_t invert_permissions, enum permstatus permstatus, char typeflag) @@ -256,8 +249,11 @@ set_stat (char const *file_name, /* FIXME: incremental_option should set ctime too, but how? */ struct timespec ts[2]; - ts[0] = incremental_option ? get_stat_atime (stat_info) : start_time; - ts[1] = get_stat_mtime (stat_info); + if (incremental_option) + ts[0] = st->atime; + else + ts[0] = start_time; + ts[1] = st->mtime; if (utimens (file_name, ts) != 0) utime_error (file_name); @@ -272,7 +268,7 @@ set_stat (char const *file_name, 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, cur_info, + set_mode (file_name, &st->stat, cur_info, invert_permissions, permstatus, typeflag); } @@ -286,48 +282,66 @@ set_stat (char const *file_name, if (typeflag == SYMTYPE) { #if HAVE_LCHOWN - if (lchown (file_name, stat_info->st_uid, stat_info->st_gid) < 0) + if (lchown (file_name, st->stat.st_uid, st->stat.st_gid) < 0) chown_error_details (file_name, - stat_info->st_uid, stat_info->st_gid); + st->stat.st_uid, st->stat.st_gid); #endif } else { - if (chown (file_name, stat_info->st_uid, stat_info->st_gid) < 0) + if (chown (file_name, st->stat.st_uid, st->stat.st_gid) < 0) chown_error_details (file_name, - stat_info->st_uid, stat_info->st_gid); + 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 (stat_info->st_mode & (S_ISUID | S_ISGID | S_ISVTX)) - set_mode (file_name, stat_info, 0, + if (st->stat.st_mode & (S_ISUID | S_ISGID | S_ISVTX)) + set_mode (file_name, &st->stat, 0, invert_permissions, permstatus, typeflag); } } } /* Remember to restore stat attributes (owner, group, mode and times) - for the directory FILE_NAME, using information given in *STAT_INFO, + for the directory FILE_NAME, using information given in *ST, 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 stat const *stat_info, +delay_set_stat (char const *file_name, struct tar_stat_info const *st, mode_t invert_permissions, enum permstatus permstatus) { size_t file_name_len = strlen (file_name); struct delayed_set_stat *data = xmalloc (offsetof (struct delayed_set_stat, file_name) + file_name_len + 1); + data->next = delayed_set_stat_head; + data->dev = st->stat.st_dev; + data->ino = st->stat.st_ino; + data->mode = st->stat.st_mode; + data->uid = st->stat.st_uid; + data->gid = st->stat.st_gid; + data->atime = st->atime; + data->mtime = st->mtime; data->file_name_len = file_name_len; - strcpy (data->file_name, file_name); data->invert_permissions = invert_permissions; data->permstatus = permstatus; data->after_links = 0; - data->stat_info = *stat_info; - data->next = delayed_set_stat_head; + strcpy (data->file_name, file_name); delayed_set_stat_head = data; } @@ -352,7 +366,13 @@ repair_delayed_set_stat (char const *dir, if (st.st_dev == dir_stat_info->st_dev && st.st_ino == dir_stat_info->st_ino) { - data->stat_info = current_stat_info.stat; + data->dev = current_stat_info.stat.st_dev; + data->ino = current_stat_info.stat.st_ino; + data->mode = current_stat_info.stat.st_mode; + data->uid = current_stat_info.stat.st_uid; + data->gid = current_stat_info.stat.st_gid; + 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)); data->permstatus = ARCHIVED_PERMSTATUS; @@ -372,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)) @@ -408,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.stat /* ignored */, + ¤t_stat_info, invert_permissions, INTERDIR_PERMSTATUS); print_for_mkdir (file_name, cursor - file_name, mode); @@ -448,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) - && st.st_mtime >= tar_stat->stat.st_mtime) + && tar_timespec_cmp (tar_stat->mtime, get_stat_mtime (&st)) <= 0) { return true; } @@ -551,8 +570,7 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links) stat_error (data->file_name); skip_this_one = 1; } - else if (! (st.st_dev == data->stat_info.st_dev - && (st.st_ino == data->stat_info.st_ino))) + else if (! (st.st_dev == data->dev && st.st_ino == data->ino)) { ERROR ((0, 0, _("%s: Directory renamed before its status could be extracted"), @@ -562,8 +580,16 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links) } if (! skip_this_one) - set_stat (data->file_name, &data->stat_info, cur_info, - data->invert_permissions, data->permstatus, DIRTYPE); + { + struct tar_stat_info st; + st.stat.st_mode = data->mode; + st.stat.st_uid = data->uid; + st.stat.st_gid = data->gid; + st.atime = data->atime; + st.mtime = data->mtime; + set_stat (data->file_name, &st, cur_info, + data->invert_permissions, data->permstatus, DIRTYPE); + } delayed_set_stat_head = data->next; free (data); @@ -581,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))) { @@ -606,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; } } @@ -627,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.stat, - 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; } @@ -711,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 @@ -755,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. */ @@ -771,7 +810,7 @@ extract_file (char *file_name, int typeflag) if (to_command_option) sys_wait_command (); else - set_stat (file_name, ¤t_stat_info.stat, 0, 0, + set_stat (file_name, ¤t_stat_info, NULL, 0, (old_files_option == OVERWRITE_OLD_FILES ? UNKNOWN_PERMSTATUS : ARCHIVED_PERMSTATUS), typeflag); @@ -815,7 +854,7 @@ create_placeholder_file (char *file_name, bool is_symlink, int *interdir_made) delayed_link_head = p; p->dev = st.st_dev; p->ino = st.st_ino; - p->mtime = st.st_mtime; + p->mtime = get_stat_mtime (&st); p->is_symlink = is_symlink; if (is_symlink) { @@ -842,8 +881,8 @@ create_placeholder_file (char *file_name, bool is_symlink, int *interdir_made) stat_error (h->file_name); else { - h->stat_info.st_dev = st.st_dev; - h->stat_info.st_ino = st.st_ino; + h->dev = st.st_dev; + h->ino = st.st_ino; } } while ((h = h->next) && ! h->after_links); @@ -879,7 +918,7 @@ extract_link (char *file_name, int typeflag) for (; ds; ds = ds->next) if (ds->dev == st1.st_dev && ds->ino == st1.st_ino - && ds->mtime == st1.st_mtime) + && timespec_cmp (ds->mtime, get_stat_mtime (&st1)) == 0) { struct string_list *p = xmalloc (offsetof (struct string_list, string) + strlen (file_name) + 1); @@ -926,7 +965,7 @@ extract_symlink (char *file_name, int typeflag) break; if (status == 0) - set_stat (file_name, ¤t_stat_info.stat, 0, 0, 0, SYMTYPE); + set_stat (file_name, ¤t_stat_info, NULL, 0, 0, SYMTYPE); else symlink_error (current_stat_info.link_name, file_name); return status; @@ -958,7 +997,8 @@ extract_node (char *file_name, int typeflag) if (status != 0) mknod_error (file_name); else - set_stat (file_name, ¤t_stat_info.stat, 0, 0, ARCHIVED_PERMSTATUS, typeflag); + set_stat (file_name, ¤t_stat_info, NULL, 0, + ARCHIVED_PERMSTATUS, typeflag); return status; } #endif @@ -975,7 +1015,7 @@ extract_fifo (char *file_name, int typeflag) break; if (status == 0) - set_stat (file_name, ¤t_stat_info.stat, NULL, 0, + set_stat (file_name, ¤t_stat_info, NULL, 0, ARCHIVED_PERMSTATUS, typeflag); else mkfifo_error (file_name); @@ -990,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) @@ -1065,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: @@ -1156,7 +1203,8 @@ extract_archive (void) false, absolute_names_option); if (strip_name_components) { - size_t prefix_len = stripped_prefix_len (file_name, strip_name_components); + size_t prefix_len = stripped_prefix_len (file_name, + strip_name_components); if (prefix_len == (size_t) -1) { skip_member (); @@ -1165,7 +1213,11 @@ extract_archive (void) 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 (file_name, 0); /* Take a safety backup of a previously existing file. */ @@ -1180,7 +1232,6 @@ extract_archive (void) } /* Extract the archive entry according to its type. */ - /* KLUDGE */ typeflag = sparse_member_p (¤t_stat_info) ? GNUTYPE_SPARSE : current_header->header.typeflag; @@ -1217,7 +1268,7 @@ apply_delayed_links (void) if (lstat (source, &st) == 0 && st.st_dev == ds->dev && st.st_ino == ds->ino - && st.st_mtime == ds->mtime) + && timespec_cmp (get_stat_mtime (&st), ds->mtime) == 0) { /* Unlink the placeholder, then create a hard link if possible, a symbolic link otherwise. */ @@ -1234,10 +1285,11 @@ apply_delayed_links (void) symlink_error (ds->target, source); else { + struct tar_stat_info st1; + st1.stat.st_uid = ds->uid; + st1.stat.st_gid = ds->gid; + set_stat (source, &st1, NULL, 0, 0, SYMTYPE); valid_source = source; - st.st_uid = ds->uid; - st.st_gid = ds->gid; - set_stat (source, &st, 0, 0, 0, SYMTYPE); } } } @@ -1275,6 +1327,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) {