X-Git-Url: https://git.brokenzipper.com/gitweb?a=blobdiff_plain;f=src%2Fextract.c;h=2d8e175985e2cb0d9b2719f305628884e81b47fd;hb=696338043e52f440853e1143c52b81b41cd59723;hp=ecbde1a8c939a637d35e7addba297f7fbaac8951;hpb=189e4364741e927216a254b8a51d96af2d954df8;p=chaz%2Ftar diff --git a/src/extract.c b/src/extract.c index ecbde1a..2d8e175 100644 --- a/src/extract.c +++ b/src/extract.c @@ -1,7 +1,8 @@ /* Extract files from a tar archive. Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000, - 2001, 2003, 2004, 2005, 2006, 2007, 2010 Free Software Foundation, Inc. + 2001, 2003, 2004, 2005, 2006, 2007, 2010, 2012 + Free Software Foundation, Inc. Written by John Gilmore, on 1985-11-19. @@ -23,6 +24,7 @@ #include #include #include +#include #include #include "common.h" @@ -97,6 +99,9 @@ struct delayed_set_stat /* Directory that the name is relative to. */ int change_dir; + /* extended attributes*/ + size_t xattr_map_size; + struct xattr_array *xattr_map; /* Length and contents of name. */ size_t file_name_len; char file_name[1]; @@ -137,6 +142,9 @@ struct delayed_link hard-linked together. */ struct string_list *sources; + size_t xattr_map_size; + struct xattr_array *xattr_map; + /* The desired target of the desired link. */ char target[1]; }; @@ -153,7 +161,7 @@ struct string_list void extr_init (void) { - we_are_root = geteuid () == 0; + we_are_root = geteuid () == ROOT_UID; same_permissions_option += we_are_root; same_owner_option += we_are_root; @@ -271,15 +279,6 @@ set_mode (char const *file_name, } } -/* Return true if A and B are the same birthtimes. - Unavailable birthtimes, which have negative tv_nsec members, - all compare equal to each other. */ -static bool -same_birthtime (struct timespec a, struct timespec b) -{ - return (a.tv_nsec == b.tv_nsec && (a.tv_nsec < 0 || a.tv_sec == b.tv_sec)); -} - /* Check time after successfully setting FILE_NAME's time stamp to T. */ static void check_time (char const *file_name, struct timespec t) @@ -372,6 +371,10 @@ set_stat (char const *file_name, st->stat.st_mode & ~ current_umask, 0 < same_permissions_option && ! interdir ? MODE_ALL : MODE_RWX, fd, current_mode, current_mode_mask, typeflag, atflag); + + /* these three calls must be done *after* fd_chown() call because fd_chown + causes that linux capabilities becomes cleared. */ + xattrs_xattrs_set (st, file_name, typeflag, 1); } /* For each entry H in the leading prefix of entries in HEAD that do @@ -443,6 +446,13 @@ delay_set_stat (char const *file_name, struct tar_stat_info const *st, data->atflag = atflag; data->after_links = 0; data->change_dir = chdir_current; + if (st) + xheader_xattr_copy (st, &data->xattr_map, &data->xattr_map_size); + else + { + data->xattr_map = NULL; + data->xattr_map_size = 0; + } strcpy (data->file_name, file_name); delayed_set_stat_head = data; if (must_be_dot_or_slash (file_name)) @@ -651,9 +661,14 @@ maybe_recoverable (char *file_name, bool regular, bool *interdir_made) switch (old_files_option) { - case KEEP_OLD_FILES: + case SKIP_OLD_FILES: + WARNOPT (WARN_EXISTING_FILE, + (0, 0, _("%s: skipping existing file"), file_name)); return RECOVER_SKIP; + case KEEP_OLD_FILES: + return RECOVER_NO; + case KEEP_NEWER_FILES: if (file_newer_p (file_name, stp, ¤t_stat_info)) break; @@ -685,6 +700,40 @@ maybe_recoverable (char *file_name, bool regular, bool *interdir_made) return RECOVER_NO; } +/* Restore stat extended attributes (xattr) for FILE_NAME, using information + given in *ST. Restore before extraction because they may affect file layout + (e.g. on Lustre distributed parallel filesystem - setting info about how many + servers is this file striped over, stripe size, mirror copies, etc. + in advance dramatically improves the following performance of reading and + writing a file). If not restoring permissions, invert the INVERT_PERMISSIONS + bits from the file's current permissions. TYPEFLAG specifies the type of the + file. FILE_CREATED indicates set_xattr has created the file */ +static int +set_xattr (char const *file_name, struct tar_stat_info const *st, + mode_t invert_permissions, char typeflag, int *file_created) +{ + int status = 0; + +#ifdef HAVE_XATTRS + bool interdir_made = false; + + if ((xattrs_option > 0) && st->xattr_map_size) + { + mode_t mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask; + + do + status = mknodat (chdir_fd, file_name, mode ^ invert_permissions, 0); + while (status && maybe_recoverable ((char *)file_name, false, + &interdir_made)); + + xattrs_xattrs_set (st, file_name, typeflag, 0); + *file_created = 1; + } +#endif + + return(status); +} + /* Fix the statuses of all directories whose statuses need fixing, and which are not ancestors of FILE_NAME. If AFTER_LINKS is nonzero, do this for all such directories; otherwise, stop at the @@ -745,12 +794,15 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_links) sb.stat.st_gid = data->gid; sb.atime = data->atime; sb.mtime = data->mtime; + sb.xattr_map = data->xattr_map; + sb.xattr_map_size = data->xattr_map_size; set_stat (data->file_name, &sb, -1, current_mode, current_mode_mask, DIRTYPE, data->interdir, data->atflag); } delayed_set_stat_head = data->next; + xheader_xattr_free (data->xattr_map, data->xattr_map_size); free (data); } } @@ -866,7 +918,8 @@ extract_dir (char *file_name, int typeflag) static int open_output_file (char const *file_name, int typeflag, mode_t mode, - mode_t *current_mode, mode_t *current_mode_mask) + int file_created, mode_t *current_mode, + mode_t *current_mode_mask) { int fd; bool overwriting_old_files = old_files_option == OVERWRITE_OLD_FILES; @@ -876,6 +929,10 @@ open_output_file (char const *file_name, int typeflag, mode_t mode, ? O_TRUNC | (dereference_option ? 0 : O_NOFOLLOW) : O_EXCL)); + /* File might be created in set_xattr. So clear O_EXCL to avoid open() fail */ + if (file_created) + openflag = openflag & ~O_EXCL; + if (typeflag == CONTTYPE) { static int conttype_diagnosed; @@ -891,7 +948,8 @@ open_output_file (char const *file_name, int typeflag, mode_t mode, /* If O_NOFOLLOW is needed but does not work, check for a symlink separately. There's a race condition, but that cannot be avoided on hosts lacking O_NOFOLLOW. */ - if (! O_NOFOLLOW && overwriting_old_files && ! dereference_option) + if (! HAVE_WORKING_O_NOFOLLOW + && overwriting_old_files && ! dereference_option) { struct stat st; if (fstatat (chdir_fd, file_name, &st, AT_SYMLINK_NOFOLLOW) == 0 @@ -946,6 +1004,8 @@ extract_file (char *file_name, int typeflag) bool interdir_made = false; mode_t mode = (current_stat_info.stat.st_mode & MODE_RWX & ~ (0 < same_owner_option ? S_IRWXG | S_IRWXO : 0)); + mode_t invert_permissions = 0 < same_owner_option ? mode & (S_IRWXG | S_IRWXO) + : 0; mode_t current_mode = 0; mode_t current_mode_mask = 0; @@ -962,8 +1022,18 @@ extract_file (char *file_name, int typeflag) } else { + int file_created = 0; + if (set_xattr (file_name, ¤t_stat_info, invert_permissions, + typeflag, &file_created)) + { + skip_member (); + open_error (file_name); + return 1; + } + while ((fd = open_output_file (file_name, typeflag, mode, - ¤t_mode, ¤t_mode_mask)) + file_created, ¤t_mode, + ¤t_mode_mask)) < 0) { int recover = maybe_recoverable (file_name, true, &interdir_made); @@ -1002,7 +1072,7 @@ extract_file (char *file_name, int typeflag) if (written > size) written = size; errno = 0; - count = full_write (fd, data_block->buffer, written); + count = blocking_write (fd, data_block->buffer, written); size -= written; set_next_block_after ((union block *) @@ -1103,6 +1173,7 @@ create_placeholder_file (char *file_name, bool is_symlink, bool *interdir_made) + strlen (file_name) + 1); p->sources->next = 0; strcpy (p->sources->string, file_name); + xheader_xattr_copy (¤t_stat_info, &p->xattr_map, &p->xattr_map_size); strcpy (p->target, current_stat_info.link_name); h = delayed_set_stat_head; @@ -1146,7 +1217,8 @@ extract_link (char *file_name, int typeflag) if (ds->change_dir == chdir_current && ds->dev == st1.st_dev && ds->ino == st1.st_ino - && same_birthtime (ds->birthtime, get_stat_birthtime (&st1))) + && (timespec_cmp (ds->birthtime, get_stat_birthtime (&st1)) + == 0)) { struct string_list *p = xmalloc (offsetof (struct string_list, string) + strlen (file_name) + 1); @@ -1388,7 +1460,7 @@ prepare_to_extract (char const *file_name, int typeflag, tar_extractor_t *fun) default: WARNOPT (WARN_UNKNOWN_CAST, (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)); *fun = extract_file; } @@ -1512,7 +1584,7 @@ apply_delayed_links (void) if (fstatat (chdir_fd, source, &st, AT_SYMLINK_NOFOLLOW) == 0 && st.st_dev == ds->dev && st.st_ino == ds->ino - && same_birthtime (get_stat_birthtime (&st), ds->birthtime)) + && timespec_cmp (get_stat_birthtime (&st), ds->birthtime) == 0) { /* Unlink the placeholder, then create a hard link if possible, a symbolic link otherwise. */ @@ -1537,6 +1609,8 @@ apply_delayed_links (void) st1.stat.st_gid = ds->gid; st1.atime = ds->atime; st1.mtime = ds->mtime; + st1.xattr_map = ds->xattr_map; + st1.xattr_map_size = ds->xattr_map_size; set_stat (source, &st1, -1, 0, 0, SYMTYPE, false, AT_SYMLINK_NOFOLLOW); valid_source = source; @@ -1551,6 +1625,8 @@ apply_delayed_links (void) sources = next; } + xheader_xattr_free (ds->xattr_map, ds->xattr_map_size); + { struct delayed_link *next = ds->next; free (ds);