X-Git-Url: https://git.brokenzipper.com/gitweb?a=blobdiff_plain;f=src%2Fextract.c;h=aa8f93c13670214cef2f9979df3d0d51b639e447;hb=c270f6f1b2454650c984b3b002f9fdd6a9f32f1a;hp=ffb4c33311c097728739cddef7c8bc425d28e305;hpb=6a8c91fef33385cbd552a78061b75f1429f59542;p=chaz%2Ftar diff --git a/src/extract.c b/src/extract.c index ffb4c33..aa8f93c 100644 --- a/src/extract.c +++ b/src/extract.c @@ -1,5 +1,5 @@ /* Extract files from a tar archive. - Copyright (C) 1988, 92,93,94,96,97,98, 1999 Free Software Foundation, Inc. + Copyright 1988, 92,93,94,96,97,98, 1999 Free Software Foundation, Inc. Written by John Gilmore, on 1985-11-19. This program is free software; you can redistribute it and/or modify it @@ -19,7 +19,9 @@ #include "system.h" #include +#ifndef time time_t time (); +#endif #if HAVE_UTIME_H # include @@ -63,7 +65,7 @@ static struct delayed_set_stat *delayed_set_stat_head; void extr_init (void) { - now = time ((time_t *) 0); + now = time (0); we_are_root = geteuid () == 0; /* Option -p clears the kernel umask, so it does not affect proper @@ -91,18 +93,21 @@ extr_init (void) static void set_mode (char *file_name, struct stat *stat_info) { - /* We ought to force permission when -k is not selected, because if the + /* Do nothing unless we are restoring the original permissions. + + We must force permission when -k and -U are not selected, because if the file already existed, open or creat would save the permission bits from the previously created file, ignoring the ones we specified. - But with -k selected, we know *we* created this file, so the mode + But with -k or -U selected, we know *we* created this file, so the mode bits were set by our open. If the file has abnormal mode bits, we must chmod since writing or chown has probably reset them. If the file is normal, we merely skip the chmod. This works because we did umask (0) when -p, so umask will have left the specified mode alone. */ - if (!keep_old_files_option - || (stat_info->st_mode & (S_ISUID | S_ISGID | S_ISVTX))) + if ((we_are_root || same_permissions_option) + && ((!keep_old_files_option && !unlink_first_option) + || (stat_info->st_mode & (S_ISUID | S_ISGID | S_ISVTX)))) if (chmod (file_name, ~current_umask & stat_info->st_mode) < 0) ERROR ((0, errno, _("%s: Cannot change mode to %04lo"), file_name, @@ -226,7 +231,7 @@ make_directories (char *file_name) int status; for (cursor = strchr (file_name, '/'); - cursor != NULL; + cursor; cursor = strchr (cursor + 1, '/')) { /* Avoid mkdir of empty string, if leading or double '/'. */ @@ -244,16 +249,6 @@ make_directories (char *file_name) if (status == 0) { - /* Fix ownership. */ - - if (we_are_root) - if (chown (file_name, current_stat.st_uid, current_stat.st_gid) < 0) - ERROR ((0, errno, - _("%s: Cannot change owner to uid %lu, gid %lu"), - file_name, - (unsigned long) current_stat.st_uid, - (unsigned long) current_stat.st_gid)); - print_for_mkdir (file_name, cursor - file_name, ~newdir_umask & MODE_RWX); did_something = 1; @@ -281,6 +276,25 @@ make_directories (char *file_name) return did_something; /* tell them to retry if we made one */ } +/*--------------------------------------------------------------------. +| Unlink the destination, if we are supposed to do so. | +| Return zero if extraction should not proceed. | +`--------------------------------------------------------------------*/ + +static int +unlink_destination (char const *file_name) +{ + if (unlink_first_option + && !remove_any_file (file_name, recursive_unlink_option) + && errno != ENOENT) + { + ERROR ((0, errno, _("Cannot remove %s"), file_name)); + return 0; + } + + return 1; +} + /*--------------------------------------------------------------------. | Attempt repairing what went wrong with the extraction. Delete an | | already existing file or create missing intermediate directories. | @@ -294,10 +308,10 @@ maybe_recoverable (char *file_name) switch (errno) { case EEXIST: - /* Attempt deleting an existing file. However, with -k, just stay + /* Attempt deleting an existing file. However, with -k or -U, just stay quiet. */ - if (keep_old_files_option) + if (keep_old_files_option || unlink_first_option) return 0; return remove_any_file (file_name, 0); @@ -330,7 +344,7 @@ extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name) while (*sizeleft > 0) { union block *data_block = find_next_block (); - if (data_block == NULL) + if (! data_block) { ERROR ((0, 0, _("Unexpected EOF on archive file"))); return; @@ -353,6 +367,11 @@ extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name) *sizeleft -= count; set_next_block_after (data_block); data_block = find_next_block (); + if (! data_block) + { + ERROR ((0, 0, _("Unexpected EOF on archive file"))); + return; + } } count = full_write (fd, data_block->buffer, written); @@ -425,15 +444,14 @@ extract_archive (void) skipcrud = 0; while (!absolute_names_option && CURRENT_FILE_NAME[0] == '/') { - static int warned_once = 0; + static int warned_once; - skipcrud++; /* force relative path */ if (!warned_once) { warned_once = 1; - WARN ((0, 0, _("\ -Removing leading `/' from absolute path names in the archive"))); + WARN ((0, 0, _("Removing leading `/' from archive names"))); } + skipcrud++; /* force relative path */ } /* Take a safety backup of a previously existing file. */ @@ -470,15 +488,15 @@ Removing leading `/' from absolute path names in the archive"))); case GNUTYPE_SPARSE: sp_array_size = 10; - sparsearray = (struct sp_array *) + sparsearray = xmalloc (sp_array_size * sizeof (struct sp_array)); for (counter = 0; counter < SPARSES_IN_OLDGNU_HEADER; counter++) { sparsearray[counter].offset = - OFF_FROM_OCT (current_header->oldgnu_header.sp[counter].offset); + OFF_FROM_CHARS (current_header->oldgnu_header.sp[counter].offset); sparsearray[counter].numbytes = - SIZE_FROM_OCT (current_header->oldgnu_header.sp[counter].numbytes); + SIZE_FROM_CHARS (current_header->oldgnu_header.sp[counter].numbytes); if (!sparsearray[counter].numbytes) break; } @@ -494,6 +512,11 @@ Removing leading `/' from absolute path names in the archive"))); while (1) { exhdr = find_next_block (); + if (! exhdr) + { + ERROR ((0, 0, _("Unexpected EOF on archive file"))); + return; + } for (counter = 0; counter < SPARSES_IN_SPARSE_HEADER; counter++) { if (counter + ind > sp_array_size - 1) @@ -502,18 +525,16 @@ Removing leading `/' from absolute path names in the archive"))); room. */ sp_array_size *= 2; - sparsearray = (struct sp_array *) + sparsearray = xrealloc (sparsearray, - sp_array_size * (sizeof (struct sp_array))); + sp_array_size * sizeof (struct sp_array)); } - /* Compare to 0, or use !(int)..., for Pyramid's dumb - compiler. */ - if (exhdr->sparse_header.sp[counter].numbytes == 0) + if (exhdr->sparse_header.sp[counter].numbytes[0] == 0) break; sparsearray[counter + ind].offset = - OFF_FROM_OCT (exhdr->sparse_header.sp[counter].offset); + OFF_FROM_CHARS (exhdr->sparse_header.sp[counter].offset); sparsearray[counter + ind].numbytes = - SIZE_FROM_OCT (exhdr->sparse_header.sp[counter].numbytes); + SIZE_FROM_CHARS (exhdr->sparse_header.sp[counter].numbytes); } if (!exhdr->sparse_header.isextended) break; @@ -541,9 +562,9 @@ Removing leading `/' from absolute path names in the archive"))); /* FIXME: deal with protection issues. */ again_file: - openflag = (keep_old_files_option ? - O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_EXCL : - O_BINARY | O_NDELAY | O_WRONLY | O_CREAT | O_TRUNC) + openflag = (keep_old_files_option || unlink_first_option ? + O_WRONLY | O_BINARY | O_NONBLOCK | O_CREAT | O_EXCL : + O_WRONLY | O_BINARY | O_NONBLOCK | O_CREAT | O_TRUNC) | ((typeflag == GNUTYPE_SPARSE) ? 0 : O_APPEND); /* JK - The last | is a kludge to solve the problem the O_APPEND @@ -561,8 +582,15 @@ Removing leading `/' from absolute path names in the archive"))); goto extract_file; } - if (unlink_first_option) - remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option); + if (!unlink_destination (CURRENT_FILE_NAME)) + { + if (current_header->oldgnu_header.isextended) + skip_extended_headers (); + skip_file (current_stat.st_size); + if (backup_option) + undo_last_backup (); + break; + } #if O_CTG /* Contiguous files (on the Masscomp) have to specify the size in @@ -577,7 +605,7 @@ Removing leading `/' from absolute path names in the archive"))); #else /* not O_CTG */ if (typeflag == CONTTYPE) { - static int conttype_diagnosed = 0; + static int conttype_diagnosed; if (!conttype_diagnosed) { @@ -617,7 +645,7 @@ Removing leading `/' from absolute path names in the archive"))); REAL interesting unless we do this. */ name_length_bis = strlen (CURRENT_FILE_NAME) + 1; - name = (char *) xmalloc (name_length_bis); + name = xmalloc (name_length_bis); memcpy (name, CURRENT_FILE_NAME, name_length_bis); size = current_stat.st_size; extract_sparse_file (fd, &size, current_stat.st_size, name); @@ -639,7 +667,7 @@ Removing leading `/' from absolute path names in the archive"))); worked. */ data_block = find_next_block (); - if (data_block == NULL) + if (! data_block) { ERROR ((0, 0, _("Unexpected EOF on archive file"))); break; /* FIXME: What happens, then? */ @@ -673,7 +701,7 @@ Removing leading `/' from absolute path names in the archive"))); } if (multi_volume_option) - assign_string (&save_name, NULL); + assign_string (&save_name, 0); /* 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. */ @@ -696,9 +724,9 @@ Removing leading `/' from absolute path names in the archive"))); if (to_stdout_option) break; -#ifdef S_ISLNK - if (unlink_first_option) - remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option); +#ifdef HAVE_SYMLINK + if (!unlink_destination (CURRENT_FILE_NAME)) + break; while (status = symlink (current_link_name, CURRENT_FILE_NAME), status != 0) @@ -723,27 +751,27 @@ Removing leading `/' from absolute path names in the archive"))); } break; -#else /* not S_ISLNK */ +#else { - static int warned_once = 0; + static int warned_once; if (!warned_once) { warned_once = 1; - WARN ((0, 0, _("\ -Attempting extraction of symbolic links as hard links"))); + WARN ((0, 0, + _("Attempting extraction of symbolic links as hard links"))); } } /* Fall through. */ -#endif /* not S_ISLNK */ +#endif case LNKTYPE: if (to_stdout_option) break; - if (unlink_first_option) - remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option); + if (!unlink_destination (CURRENT_FILE_NAME)) + break; again_link: { @@ -789,8 +817,8 @@ Attempting extraction of symbolic links as hard links"))); if (to_stdout_option) break; - if (unlink_first_option) - remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option); + if (!unlink_destination (CURRENT_FILE_NAME)) + break; status = mknod (CURRENT_FILE_NAME, current_stat.st_mode, current_stat.st_rdev); @@ -808,13 +836,13 @@ Attempting extraction of symbolic links as hard links"))); break; #endif -#ifdef S_ISFIFO +#if HAVE_MKFIFO || defined mkfifo case FIFOTYPE: if (to_stdout_option) break; - if (unlink_first_option) - remove_any_file (CURRENT_FILE_NAME, recursive_unlink_option); + if (!unlink_destination (CURRENT_FILE_NAME)) + break; while (status = mkfifo (CURRENT_FILE_NAME, current_stat.st_mode), status != 0) @@ -918,7 +946,7 @@ Attempting extraction of symbolic links as hard links"))); } #if !MSDOS - /* MSDOS does not associate timestamps with directories. In this + /* MSDOS does not associate time stamps with directories. In this case, no need to try delaying their restoration. */ if (touch_option) @@ -931,8 +959,7 @@ Attempting extraction of symbolic links as hard links"))); else { - data = ((struct delayed_set_stat *) - xmalloc (sizeof (struct delayed_set_stat))); + data = xmalloc (sizeof (struct delayed_set_stat)); data->file_name = xstrdup (CURRENT_FILE_NAME); data->stat_info = current_stat; data->next = delayed_set_stat_head; @@ -951,8 +978,8 @@ Attempting extraction of symbolic links as hard links"))); break; case GNUTYPE_MULTIVOL: - ERROR ((0, 0, _("\ -Cannot extract `%s' -- file is continued from another volume"), + ERROR ((0, 0, + _("Cannot extract `%s' -- file is continued from another volume"), current_file_name)); skip_file (current_stat.st_size); if (backup_option) @@ -986,7 +1013,7 @@ apply_delayed_set_stat (void) { struct delayed_set_stat *data; - while (delayed_set_stat_head != NULL) + while (delayed_set_stat_head) { data = delayed_set_stat_head; delayed_set_stat_head = delayed_set_stat_head->next;