From: Paul Eggert Date: Sat, 13 Jan 2001 05:59:29 +0000 (+0000) Subject: (): Do not include; system.h now does this. X-Git-Url: https://git.dogcows.com/gitweb?a=commitdiff_plain;h=eca497ef34cc119ad9134a7638fdad6ff36d7065;p=chaz%2Ftar (): Do not include; system.h now does this. (make_directories): Skip filesystem prefixes. Don't assume '/' is the only separator. (extract_sparse_file): Use new full_write semantics. On write error, return instead of invoking skip_file. Do not free sparsearray; caller does this now. (apply_nonancestor_delayed_set_stat): Do not assume '/' is the only separator. (extract_archive): Don't assume file name lengths fit in int. Report what got stripped from member name; it might be more than '/'. Use new full_write semantics. Do not pass redundant trailing "/" to mkdir, as POSIX does not allow mkdir to ignore it. Do not report mkdir error if old_files_option == KEEP_OLD_FILES. --- diff --git a/src/extract.c b/src/extract.c index 8cabb52..0f50d91 100644 --- a/src/extract.c +++ b/src/extract.c @@ -1,5 +1,5 @@ /* Extract files from a tar archive. - Copyright 1988, 92,93,94,96,97,98,99, 2000 Free Software Foundation, Inc. + Copyright 1988,92,93,94,96,97,98,99,2000,2001 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 @@ -18,7 +18,6 @@ #include "system.h" #include -#include #if HAVE_UTIME_H # include @@ -321,27 +320,30 @@ repair_delayed_set_stat (char const *dir_name, static int make_directories (char *file_name) { + char *cursor0 = file_name + FILESYSTEM_PREFIX_LEN (file_name); char *cursor; /* points into path */ int did_something = 0; /* did we do anything yet? */ int mode; int invert_permissions; int status; - for (cursor = strchr (file_name, '/'); - cursor; - cursor = strchr (cursor + 1, '/')) + + for (cursor = cursor0; *cursor; cursor++) { + if (! ISSLASH (*cursor)) + continue; + /* Avoid mkdir of empty string, if leading or double '/'. */ - if (cursor == file_name || cursor[-1] == '/') + if (cursor == cursor0 || ISSLASH (cursor[-1])) continue; /* Avoid mkdir where last part of path is "." or "..". */ if (cursor[-1] == '.' - && (cursor == file_name + 1 || cursor[-2] == '/' + && (cursor == cursor0 + 1 || ISSLASH (cursor[-2]) || (cursor[-2] == '.' - && (cursor == file_name + 2 || cursor[-3] == '/')))) + && (cursor == cursor0 + 2 || ISSLASH (cursor[-3]))))) continue; *cursor = '\0'; /* truncate the path there */ @@ -453,13 +455,13 @@ static void extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name) { int sparse_ind = 0; - size_t written; - ssize_t count; /* assuming sizeleft is initially totalsize */ while (*sizeleft > 0) { + size_t written; + size_t count; union block *data_block = find_next_block (); if (! data_block) { @@ -475,10 +477,13 @@ extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name) while (written > BLOCKSIZE) { count = full_write (fd, data_block->buffer, BLOCKSIZE); - if (count < 0) - write_error (name); written -= count; *sizeleft -= count; + if (count != BLOCKSIZE) + { + write_error_details (name, count, BLOCKSIZE); + return; + } set_next_block_after (data_block); data_block = find_next_block (); if (! data_block) @@ -489,21 +494,16 @@ extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name) } count = full_write (fd, data_block->buffer, written); + *sizeleft -= count; - if (count < 0) - write_error (name); - else if (count != written) + if (count != written) { write_error_details (name, count, written); - skip_file (*sizeleft); + return; } - written -= count; - *sizeleft -= count; set_next_block_after (data_block); } - - free (sparsearray); } /* Fix the statuses of all directories whose statuses need fixing, and @@ -518,8 +518,8 @@ apply_nonancestor_delayed_set_stat (char const *file_name) struct delayed_set_stat *data = delayed_set_stat_head; if (data->file_name_len < file_name_len && file_name[data->file_name_len] - && (file_name[data->file_name_len] == '/' - || file_name[data->file_name_len - 1] == '/') + && (ISSLASH (file_name[data->file_name_len]) + || ISSLASH (file_name[data->file_name_len - 1])) && memcmp (file_name, data->file_name, data->file_name_len) == 0) break; delayed_set_stat_head = data->next; @@ -536,13 +536,13 @@ extract_archive (void) union block *data_block; int fd; int status; - ssize_t sstatus; + size_t count; size_t name_length; size_t written; int openflag; mode_t mode; off_t size; - int skipcrud; + size_t skipcrud; int counter; int interdir_made = 0; char typeflag; @@ -569,24 +569,28 @@ extract_archive (void) skipcrud = 0; if (! absolute_names_option) { - while (CURRENT_FILE_NAME[0] == '/') + if (contains_dot_dot (CURRENT_FILE_NAME)) + { + ERROR ((0, 0, _("%s: Member name contains `..'"), + quotearg_colon (CURRENT_FILE_NAME))); + skip_member (); + return; + } + + skipcrud = FILESYSTEM_PREFIX_LEN (current_file_name); + while (ISSLASH (CURRENT_FILE_NAME[0])) + skipcrud++; + + if (skipcrud) { static int warned_once; if (!warned_once) { warned_once = 1; - WARN ((0, 0, _("Removing leading `/' from member names"))); + WARN ((0, 0, _("Removing leading `%.*s' from member names"), + (int) skipcrud, current_file_name)); } - skipcrud++; /* force relative path */ - } - - if (contains_dot_dot (CURRENT_FILE_NAME)) - { - ERROR ((0, 0, _("%s: Member name contains `..'"), - quotearg_colon (CURRENT_FILE_NAME))); - skip_member (); - return; } } @@ -693,7 +697,8 @@ extract_archive (void) suffix means a directory. */ name_length = strlen (CURRENT_FILE_NAME); - if (name_length && CURRENT_FILE_NAME[name_length - 1] == '/') + if (FILESYSTEM_PREFIX_LEN (CURRENT_FILE_NAME) < name_length + && CURRENT_FILE_NAME[name_length - 1] == '/') goto really_dir; /* FIXME: deal with protection issues. */ @@ -773,11 +778,10 @@ extract_archive (void) memcpy (name, CURRENT_FILE_NAME, name_length_bis); size = current_stat.st_size; extract_sparse_file (fd, &size, current_stat.st_size, name); + free (sparsearray); } else - for (size = current_stat.st_size; - size > 0; - size -= written) + for (size = current_stat.st_size; size > 0; ) { if (multi_volume_option) { @@ -802,21 +806,20 @@ extract_archive (void) if (written > size) written = size; errno = 0; - sstatus = full_write (fd, data_block->buffer, written); + count = full_write (fd, data_block->buffer, written); + size -= count; set_next_block_after ((union block *) (data_block->buffer + written - 1)); - if (sstatus == written) - continue; - - /* Error in writing to file. Print it, skip to next file in - archive. */ - - write_error_details (CURRENT_FILE_NAME, sstatus, written); - skip_file (size - written); - break; /* still do the close, mod time, chmod, etc */ + if (count != written) + { + write_error_details (CURRENT_FILE_NAME, count, written); + break; + } } + skip_file (size); + if (multi_volume_option) assign_string (&save_name, 0); @@ -847,7 +850,8 @@ extract_archive (void) break; if (absolute_names_option - || (current_link_name[0] != '/' + || (ISSLASH (current_link_name + [FILESYSTEM_PREFIX_LEN (current_link_name)]) && ! contains_dot_dot (current_link_name))) { while (status = symlink (current_link_name, CURRENT_FILE_NAME), @@ -1014,6 +1018,7 @@ extract_archive (void) name_length = strlen (CURRENT_FILE_NAME); really_dir: + if (incremental_option) { /* Read the entry and delete files that aren't listed in the @@ -1032,7 +1037,19 @@ extract_archive (void) & MODE_RWX); again_dir: - status = mkdir (CURRENT_FILE_NAME, mode); + { + /* Do not pass redundant trailing "/" to mkdir, as POSIX does + not allow mkdir to ignore it. */ + size_t len = name_length; + char ch = '\0'; + while (FILESYSTEM_PREFIX_LEN (CURRENT_FILE_NAME) < len + && CURRENT_FILE_NAME[len - 1] == '/') + len--, ch = '/'; + CURRENT_FILE_NAME[len] = '\0'; + status = mkdir (CURRENT_FILE_NAME, mode); + CURRENT_FILE_NAME[len] = ch; + } + if (status != 0) { if (errno == EEXIST && interdir_made) @@ -1049,7 +1066,7 @@ extract_archive (void) if (maybe_recoverable (CURRENT_FILE_NAME, &interdir_made)) goto again_dir; - if (errno != EEXIST || old_files_option == KEEP_OLD_FILES) + if (errno != EEXIST) { mkdir_error (CURRENT_FILE_NAME); if (backup_option)