X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fextract.c;h=84e4ecdd733f84579b1d7524676894adb526653d;hb=f7ce5a4ec57028be0bdea6d45dce8fe1c23ec668;hp=c070a41a46d432f254c05fa73680a471db0b7662;hpb=675c5a2f243bb5e72c982c0e3c30762ec32e9a1b;p=chaz%2Ftar diff --git a/src/extract.c b/src/extract.c index c070a41..84e4ecd 100644 --- a/src/extract.c +++ b/src/extract.c @@ -21,6 +21,7 @@ #include "system.h" #include +#include #if HAVE_UTIME_H # include @@ -129,14 +130,15 @@ extr_init (void) } /* If restoring permissions, restore the mode for FILE_NAME from - information given in *STAT_INFO (where *CURRENT_STAT_INFO gives - the current status if CURRENT_STAT_INFO is nonzero); otherwise invert the + information given in *STAT_INFO (where *CUR_INFO gives + the current status if CUR_INFO is nonzero); otherwise invert the INVERT_PERMISSIONS bits from the file's current permissions. PERMSTATUS specifies the status of the file's permissions. TYPEFLAG specifies the type of the file. */ static void -set_mode (char const *file_name, struct stat const *stat_info, - struct stat const *current_stat_info, +set_mode (char const *file_name, + struct stat const *stat_info, + struct stat const *cur_info, mode_t invert_permissions, enum permstatus permstatus, char typeflag) { @@ -168,16 +170,16 @@ set_mode (char const *file_name, struct stat const *stat_info, that we created, so there's no point optimizing this code for other cases. */ struct stat st; - if (! current_stat_info) + if (! cur_info) { if (stat (file_name, &st) != 0) { stat_error (file_name); return; } - current_stat_info = &st; + cur_info = &st; } - mode = current_stat_info->st_mode ^ invert_permissions; + mode = cur_info->st_mode ^ invert_permissions; } if (chmod (file_name, mode) != 0) @@ -199,7 +201,7 @@ check_time (char const *file_name, time_t t) /* Restore stat attributes (owner, group, mode and times) for FILE_NAME, using information given in *STAT_INFO. - If CURRENT_STAT_INFO is nonzero, *CURRENT_STAT_INFO is the + If CUR_INFO is nonzero, *CUR_INFO is the file's currernt status. If not restoring permissions, invert the INVERT_PERMISSIONS bits from the file's current permissions. @@ -212,8 +214,9 @@ check_time (char const *file_name, time_t t) punt for the rest. Sigh! */ static void -set_stat (char const *file_name, struct stat const *stat_info, - struct stat const *current_stat_info, +set_stat (char const *file_name, + struct stat const *stat_info, + struct stat const *cur_info, mode_t invert_permissions, enum permstatus permstatus, char typeflag) { @@ -252,7 +255,7 @@ set_stat (char const *file_name, struct stat const *stat_info, 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, current_stat_info, + set_mode (file_name, stat_info, cur_info, invert_permissions, permstatus, typeflag); } @@ -332,9 +335,9 @@ repair_delayed_set_stat (char const *dir_name, if (st.st_dev == dir_stat_info->st_dev && st.st_ino == dir_stat_info->st_ino) { - data->stat_info = current_stat; - data->invert_permissions = (MODE_RWX - & (current_stat.st_mode ^ st.st_mode)); + data->stat_info = current_stat_info.stat; + data->invert_permissions = + (MODE_RWX & (current_stat_info.stat.st_mode ^ st.st_mode)); data->permstatus = ARCHIVED_PERMSTATUS; return; } @@ -388,7 +391,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 /* ignored */, + ¤t_stat_info.stat /* ignored */, invert_permissions, INTERDIR_PERMSTATUS); print_for_mkdir (file_name, cursor - file_name, mode); @@ -400,13 +403,13 @@ make_directories (char *file_name) *cursor = '/'; - if (errno == EEXIST -#if MSDOS - /* Turbo C mkdir gives a funny errno. */ - || errno == EACCES -#endif - ) - /* Directory already exists. */ + if (errno == EEXIST) + continue; /* Directory already exists. */ + else if ((errno == ENOSYS /* Automounted dirs on Solaris return + this. Reported by Warren Hyde + */ + || ERRNO_IS_EACCES) /* Turbo C mkdir gives a funny errno. */ + && access (file_name, W_OK) == 0) continue; /* Some other error in the mkdir. We return to the caller. */ @@ -483,147 +486,6 @@ maybe_recoverable (char *file_name, int *interdir_made) } } -/* Translate the sparse information on the header, and in any - subsequent extended headers, into an array of structures with true - numbers, as opposed to character strings. Return nonzero if - successful. - - This function invalidates current_header. */ - -bool -fill_in_sparse_array (void) -{ - off_t sparse_data_size = current_stat.st_size; - off_t file_size = OFF_FROM_HEADER (current_header->oldgnu_header.realsize); - int sparses; - int counter; - union block *h = current_header; - - init_sparsearray (); - - for (sparses = 0; sparses < SPARSES_IN_OLDGNU_HEADER; sparses++) - { - struct sparse const *s = &h->oldgnu_header.sp[sparses]; - off_t offset; - size_t numbytes; - if (s->numbytes[0] == '\0') - break; - sparsearray[sparses].offset = offset = OFF_FROM_HEADER (s->offset); - sparsearray[sparses].numbytes = numbytes = - SIZE_FROM_HEADER (s->numbytes); - sparse_data_size -= numbytes; - if (offset < 0 || file_size < offset + numbytes || sparse_data_size < 0) - goto invalid_member; - } - - if (h->oldgnu_header.isextended) - do - { - h = find_next_block (); - if (! h) - { - ERROR ((0, 0, _("Unexpected EOF in archive"))); - return 0; - } - - for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++) - { - struct sparse const *s = &h->sparse_header.sp[counter]; - off_t offset; - size_t numbytes; - if (s->numbytes[0] == '\0') - break; - - if (sparses == sp_array_size) - { - sp_array_size *= 2; - sparsearray = - xrealloc (sparsearray, - sp_array_size * sizeof *sparsearray); - } - - sparsearray[sparses].offset = offset = - OFF_FROM_HEADER (s->offset); - sparsearray[sparses].numbytes = numbytes = - SIZE_FROM_HEADER (s->numbytes); - sparse_data_size -= numbytes; - if (offset < 0 || file_size < offset + numbytes - || sparse_data_size < 0) - goto invalid_member; - sparses++; - } - - set_next_block_after (h); - - } while (h->sparse_header.isextended); - - return 1; - - invalid_member: - ERROR ((0, 0, "%s: invalid sparse archive member", current_file_name)); - return 0; -} - - -static off_t -extract_sparse_file (int fd, char const *name, - off_t sizeleft, off_t file_size) -{ - int sparse_ind = 0; - - while (sizeleft != 0) - { - size_t written; - size_t count; - union block *data_block = find_next_block (); - if (! data_block) - { - ERROR ((0, 0, _("Unexpected EOF in archive"))); - return sizeleft; - } - if (lseek (fd, sparsearray[sparse_ind].offset, SEEK_SET) < 0) - { - seek_error_details (name, sparsearray[sparse_ind].offset); - return sizeleft; - } - written = sparsearray[sparse_ind++].numbytes; - while (written > BLOCKSIZE) - { - count = full_write (fd, data_block->buffer, BLOCKSIZE); - written -= count; - sizeleft -= count; - if (count != BLOCKSIZE) - { - write_error_details (name, count, BLOCKSIZE); - return sizeleft; - } - set_next_block_after (data_block); - data_block = find_next_block (); - if (! data_block) - { - ERROR ((0, 0, _("Unexpected EOF in archive"))); - return sizeleft; - } - } - - count = full_write (fd, data_block->buffer, written); - sizeleft -= count; - - if (count != written) - { - write_error_details (name, count, written); - return sizeleft; - } - - set_next_block_after (data_block); - } - - if (ftruncate (fd, file_size) != 0) - truncate_error (name); - - return 0; -} - /* Fix the statuses of all directories whose statuses need fixing, and which are not ancestors of FILE_NAME. If AFTER_SYMLINKS is nonzero, do this for all such directories; otherwise, stop at the @@ -640,7 +502,7 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_symlinks) struct delayed_set_stat *data = delayed_set_stat_head; bool skip_this_one = 0; struct stat st; - struct stat const *current_stat_info = 0; + struct stat const *cur_info = 0; check_for_renamed_directories |= data->after_symlinks; @@ -654,7 +516,7 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_symlinks) if (check_for_renamed_directories) { - current_stat_info = &st; + cur_info = &st; if (stat (data->file_name, &st) != 0) { stat_error (data->file_name); @@ -671,7 +533,7 @@ apply_nonancestor_delayed_set_stat (char const *file_name, bool after_symlinks) } if (! skip_this_one) - set_stat (data->file_name, &data->stat_info, current_stat_info, + set_stat (data->file_name, &data->stat_info, cur_info, data->invert_permissions, data->permstatus, DIRTYPE); delayed_set_stat_head = data->next; @@ -694,13 +556,12 @@ extract_archive (void) off_t file_size; int interdir_made = 0; char typeflag; - union block *exhdr; char *file_name; set_next_block_after (current_header); - decode_header (current_header, ¤t_stat, ¤t_format, 1); + decode_header (current_header, ¤t_stat_info, ¤t_format, 1); - if (interactive_option && !confirm ("extract", current_file_name)) + if (interactive_option && !confirm ("extract", current_stat_info.file_name)) { skip_member (); return; @@ -709,10 +570,20 @@ extract_archive (void) /* Print the block from current_header and current_stat. */ if (verbose_option) - print_header (-1); - - file_name = safer_name_suffix (current_file_name, 0); + print_header (¤t_stat_info, -1); + file_name = safer_name_suffix (current_stat_info.file_name, 0); + if (strip_path_elements) + { + size_t prefix_len = stripped_prefix_len (file_name, strip_path_elements); + if (prefix_len == (size_t) -1) + { + skip_member (); + return; + } + file_name += prefix_len; + } + apply_nonancestor_delayed_set_stat (file_name, 0); /* Take a safety backup of a previously existing file. */ @@ -730,12 +601,14 @@ extract_archive (void) /* Extract the archive entry according to its type. */ typeflag = current_header->header.typeflag; + /*KLUDGE */ + if (current_format == POSIX_FORMAT + && current_stat_info.archive_file_size != current_stat_info.stat.st_size) + typeflag = GNUTYPE_SPARSE; + switch (typeflag) { case GNUTYPE_SPARSE: - file_size = OFF_FROM_HEADER (current_header->oldgnu_header.realsize); - if (! fill_in_sparse_array ()) - return; /* Fall through. */ case AREGTYPE: @@ -745,7 +618,7 @@ extract_archive (void) /* Appears to be a file. But BSD tar uses the convention that a slash suffix means a directory. */ - if (current_trailing_slash) + if (current_stat_info.had_trailing_slash) goto really_dir; /* FIXME: deal with protection issues. */ @@ -755,7 +628,7 @@ extract_archive (void) | (old_files_option == OVERWRITE_OLD_FILES ? O_TRUNC : O_EXCL)); - mode = current_stat.st_mode & MODE_RWX & ~ current_umask; + mode = current_stat_info.stat.st_mode & MODE_RWX & ~ current_umask; if (to_stdout_option) { @@ -776,7 +649,7 @@ extract_archive (void) the open call that creates them. */ if (typeflag == CONTTYPE) - fd = open (file_name, openflag | O_CTG, mode, current_stat.st_size); + fd = open (file_name, openflag | O_CTG, mode, current_stat_info.stat.st_size); else fd = open (file_name, openflag, mode); @@ -810,29 +683,15 @@ extract_archive (void) extract_file: if (typeflag == GNUTYPE_SPARSE) { - char *name; - size_t name_length_bis; - - /* Kludge alert. NAME is assigned to header.name because - during the extraction, the space that contains the header - will get scribbled on, and the name will get munged, so any - error messages that happen to contain the filename will look - REAL interesting unless we do this. */ - - name_length_bis = strlen (file_name) + 1; - name = xmalloc (name_length_bis); - memcpy (name, file_name, name_length_bis); - size = extract_sparse_file (fd, name, - current_stat.st_size, file_size); - free (sparsearray); + sparse_extract_file (fd, ¤t_stat_info, &size); } else - for (size = current_stat.st_size; size > 0; ) + for (size = current_stat_info.stat.st_size; size > 0; ) { if (multi_volume_option) { - assign_string (&save_name, current_file_name); - save_totsize = current_stat.st_size; + assign_string (&save_name, current_stat_info.file_name); + save_totsize = current_stat_info.stat.st_size; save_sizeleft = size; } @@ -883,7 +742,7 @@ extract_archive (void) undo_last_backup (); } - set_stat (file_name, ¤t_stat, 0, 0, + set_stat (file_name, ¤t_stat_info.stat, 0, 0, (old_files_option == OVERWRITE_OLD_FILES ? UNKNOWN_PERMSTATUS : ARCHIVED_PERMSTATUS), @@ -896,19 +755,19 @@ extract_archive (void) break; if (absolute_names_option - || ! (ISSLASH (current_link_name - [FILESYSTEM_PREFIX_LEN (current_link_name)]) - || contains_dot_dot (current_link_name))) + || ! (ISSLASH (current_stat_info.link_name + [FILESYSTEM_PREFIX_LEN (current_stat_info.link_name)]) + || contains_dot_dot (current_stat_info.link_name))) { - while (status = symlink (current_link_name, file_name), + while (status = symlink (current_stat_info.link_name, file_name), status != 0) if (!maybe_recoverable (file_name, &interdir_made)) break; if (status == 0) - set_stat (file_name, ¤t_stat, 0, 0, 0, SYMTYPE); + set_stat (file_name, ¤t_stat_info.stat, 0, 0, 0, SYMTYPE); else - symlink_error (current_link_name, file_name); + symlink_error (current_stat_info.link_name, file_name); } else { @@ -937,19 +796,19 @@ extract_archive (void) struct delayed_set_stat *h; struct delayed_symlink *p = xmalloc (offsetof (struct delayed_symlink, target) - + strlen (current_link_name) + 1); + + strlen (current_stat_info.link_name) + 1); p->next = delayed_symlink_head; delayed_symlink_head = p; p->dev = st.st_dev; p->ino = st.st_ino; p->mtime = st.st_mtime; - p->uid = current_stat.st_uid; - p->gid = current_stat.st_gid; + p->uid = current_stat_info.stat.st_uid; + p->gid = current_stat_info.stat.st_gid; p->sources = xmalloc (offsetof (struct string_list, string) + strlen (file_name) + 1); p->sources->next = 0; strcpy (p->sources->string, file_name); - strcpy (p->target, current_link_name); + strcpy (p->target, current_stat_info.link_name); h = delayed_set_stat_head; if (h && ! h->after_symlinks @@ -1002,7 +861,7 @@ extract_archive (void) again_link: { - char const *link_name = safer_name_suffix (current_link_name, 1); + char const *link_name = safer_name_suffix (current_stat_info.link_name, 1); struct stat st1, st2; int e; @@ -1049,13 +908,13 @@ extract_archive (void) #if S_IFCHR case CHRTYPE: - current_stat.st_mode |= S_IFCHR; + current_stat_info.stat.st_mode |= S_IFCHR; goto make_node; #endif #if S_IFBLK case BLKTYPE: - current_stat.st_mode |= S_IFBLK; + current_stat_info.stat.st_mode |= S_IFBLK; #endif #if S_IFCHR || S_IFBLK @@ -1063,8 +922,8 @@ extract_archive (void) if (! prepare_to_extract (file_name, 0)) break; - status = mknod (file_name, current_stat.st_mode, - current_stat.st_rdev); + status = mknod (file_name, current_stat_info.stat.st_mode, + current_stat_info.stat.st_rdev); if (status != 0) { if (maybe_recoverable (file_name, &interdir_made)) @@ -1074,7 +933,7 @@ extract_archive (void) undo_last_backup (); break; }; - set_stat (file_name, ¤t_stat, 0, 0, + set_stat (file_name, ¤t_stat_info.stat, 0, 0, ARCHIVED_PERMSTATUS, typeflag); break; #endif @@ -1084,13 +943,13 @@ extract_archive (void) if (! prepare_to_extract (file_name, 0)) break; - while (status = mkfifo (file_name, current_stat.st_mode), + while (status = mkfifo (file_name, current_stat_info.stat.st_mode), status != 0) if (!maybe_recoverable (file_name, &interdir_made)) break; if (status == 0) - set_stat (file_name, ¤t_stat, 0, 0, + set_stat (file_name, ¤t_stat_info.stat, NULL, 0, ARCHIVED_PERMSTATUS, typeflag); else { @@ -1114,7 +973,7 @@ extract_archive (void) else if (typeflag == GNUTYPE_DUMPDIR) skip_member (); - mode = ((current_stat.st_mode + mode = ((current_stat_info.stat.st_mode | (we_are_root ? 0 : MODE_WXUSR)) & MODE_RWX); @@ -1167,8 +1026,8 @@ extract_archive (void) if (status == 0 || old_files_option == DEFAULT_OLD_FILES || old_files_option == OVERWRITE_OLD_FILES) - delay_set_stat (file_name, ¤t_stat, - MODE_RWX & (mode ^ current_stat.st_mode), + delay_set_stat (file_name, ¤t_stat_info.stat, + MODE_RWX & (mode ^ current_stat_info.stat.st_mode), (status == 0 ? ARCHIVED_PERMSTATUS : UNKNOWN_PERMSTATUS)); @@ -1176,7 +1035,7 @@ extract_archive (void) case GNUTYPE_VOLHDR: if (verbose_option) - fprintf (stdlis, _("Reading %s\n"), quote (current_file_name)); + fprintf (stdlis, _("Reading %s\n"), quote (current_stat_info.file_name)); break; case GNUTYPE_NAMES: @@ -1186,7 +1045,7 @@ extract_archive (void) case GNUTYPE_MULTIVOL: ERROR ((0, 0, _("%s: Cannot extract -- file is continued from another volume"), - quotearg_colon (current_file_name))); + quotearg_colon (current_stat_info.file_name))); skip_member (); if (backup_option) undo_last_backup ();