X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fextract.c;h=51a5ff0ced4301ddf0c92711763d74bae215deec;hb=d36f5a3cc3280d6c4a58367bf51b527d5c14ac04;hp=6c3849274e1281e72f9334e5a6e4f288e5f33022;hpb=7a5a3708cb8b33494cd6ac64bfe163f631a33edf;p=chaz%2Ftar diff --git a/src/extract.c b/src/extract.c index 6c38492..51a5ff0 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,13 @@ struct delayed_set_stat /* Directory that the name is relative to. */ int change_dir; + /* extended attributes*/ + char *acls_a_ptr; + size_t acls_a_len; + char *acls_d_ptr; + size_t acls_d_len; + 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 +146,15 @@ struct delayed_link hard-linked together. */ struct string_list *sources; + /* ACLs */ + char *acls_a_ptr; + size_t acls_a_len; + char *acls_d_ptr; + size_t acls_d_len; + + size_t xattr_map_size; + struct xattr_array *xattr_map; + /* The desired target of the desired link. */ char target[1]; }; @@ -153,7 +171,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; @@ -363,6 +381,11 @@ 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); + xattrs_acls_set (st, file_name, typeflag); } /* For each entry H in the leading prefix of entries in HEAD that do @@ -434,6 +457,33 @@ 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 && st->acls_a_ptr) + { + data->acls_a_ptr = xmemdup (st->acls_a_ptr, st->acls_a_len + 1); + data->acls_a_len = st->acls_a_len; + } + else + { + data->acls_a_ptr = NULL; + data->acls_a_len = 0; + } + if (st && st->acls_d_ptr) + { + data->acls_d_ptr = xmemdup (st->acls_d_ptr, st->acls_d_len + 1); + data->acls_d_len = st->acls_d_len; + } + else + { + data->acls_d_ptr = NULL; + data->acls_d_len = 0; + } + 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)) @@ -649,7 +699,7 @@ maybe_recoverable (char *file_name, bool regular, bool *interdir_made) case KEEP_OLD_FILES: return RECOVER_NO; - + case KEEP_NEWER_FILES: if (file_newer_p (file_name, stp, ¤t_stat_info)) break; @@ -681,6 +731,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 @@ -741,12 +825,21 @@ 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.acls_a_ptr = data->acls_a_ptr; + sb.acls_a_len = data->acls_a_len; + sb.acls_d_ptr = data->acls_d_ptr; + sb.acls_d_len = data->acls_d_len; + 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->acls_a_ptr); + free (data->acls_d_ptr); free (data); } } @@ -862,7 +955,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; @@ -872,6 +966,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; @@ -887,7 +985,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 @@ -942,6 +1041,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; @@ -958,8 +1059,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); @@ -998,7 +1109,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 *) @@ -1099,6 +1210,11 @@ 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); + p->acls_a_ptr = NULL; + p->acls_a_len = 0; + p->acls_d_ptr = NULL; + p->acls_d_len = 0; + 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; @@ -1385,7 +1501,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; } @@ -1534,6 +1650,12 @@ apply_delayed_links (void) st1.stat.st_gid = ds->gid; st1.atime = ds->atime; st1.mtime = ds->mtime; + st1.acls_a_ptr = ds->acls_a_ptr; + st1.acls_a_len = ds->acls_a_len; + st1.acls_d_ptr = ds->acls_d_ptr; + st1.acls_d_len = ds->acls_d_len; + 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; @@ -1548,6 +1670,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);