diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-09-19 00:37:43 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-09-19 03:14:47 +0300 |
commit | 1f6b4122815eb16dca584faac123ef46da2d538f (patch) | |
tree | 9934c37a74ed0f4ac5248eac345b971a6882d8fb | |
parent | a279052b65a53d228f0d2cd188114f4cf398cc82 (diff) | |
download | cpio-1f6b4122815eb16dca584faac123ef46da2d538f.tar.gz cpio-1f6b4122815eb16dca584faac123ef46da2d538f.tar.bz2 |
Fix error handling in disk_empty_output_buffer and sparse_write
* src/extern.h (delayed_seek_count): Remove.
(disk_empty_output_buffer): Change signature.
* src/util.c (disk_empty_output_buffer): Take two arguments.
Correctly handle partial writes (errno is not meaningful).
(delayed_seek_count): Remove variable.
(sparse_write): Change return type and signature. Rewrite.
Return number actual number of bytes written or -1 on error.
Check returns from lseek and write.
* src/copyin.c (copyin_regular_file): Call disk_empty_output_buffer
with flush=true before closing the file.
* src/copypass.c (process_copy_pass): Likewise.
-rw-r--r-- | src/copyin.c | 12 | ||||
-rw-r--r-- | src/copypass.c | 13 | ||||
-rw-r--r-- | src/extern.h | 3 | ||||
-rw-r--r-- | src/util.c | 181 |
4 files changed, 90 insertions, 119 deletions
diff --git a/src/copyin.c b/src/copyin.c index 22e33dc..3ab5dac 100644 --- a/src/copyin.c +++ b/src/copyin.c @@ -515,13 +515,13 @@ copyin_regular_file (struct cpio_file_stat* file_hdr, int in_file_des) swapping_bytes = true; else error (0, 0, _("cannot swap bytes of %s: odd number of bytes"), file_hdr->c_name); } copy_files_tape_to_disk (in_file_des, out_file_des, file_hdr->c_filesize); - disk_empty_output_buffer (out_file_des); + disk_empty_output_buffer (out_file_des, true); if (to_stdout_option) { if (archive_format == arf_crcascii) { if (crc != file_hdr->c_chksum) @@ -529,22 +529,12 @@ copyin_regular_file (struct cpio_file_stat* file_hdr, int in_file_des) file_hdr->c_name, crc, file_hdr->c_chksum); } tape_skip_padding (in_file_des, file_hdr->c_filesize); return; } - /* Debian hack to fix a bug in the --sparse option. - This bug has been reported to - "bug-gnu-utils@prep.ai.mit.edu". (96/7/10) -BEM */ - if (delayed_seek_count > 0) - { - lseek (out_file_des, delayed_seek_count-1, SEEK_CUR); - write (out_file_des, "", 1); - delayed_seek_count = 0; - } - set_perms (out_file_des, file_hdr); if (close (out_file_des) < 0) close_error (file_hdr->c_name); if (archive_format == arf_crcascii) diff --git a/src/copypass.c b/src/copypass.c index 5b1d594..a3ccb71 100644 --- a/src/copypass.c +++ b/src/copypass.c @@ -197,23 +197,14 @@ process_copy_pass () open_error (output_name.ds_string); close (in_file_des); continue; } copy_files_disk_to_disk (in_file_des, out_file_des, in_file_stat.st_size, input_name.ds_string); - disk_empty_output_buffer (out_file_des); - /* Debian hack to fix a bug in the --sparse option. - This bug has been reported to - "bug-gnu-utils@prep.ai.mit.edu". (96/7/10) -BEM */ - if (delayed_seek_count > 0) - { - lseek (out_file_des, delayed_seek_count-1, SEEK_CUR); - write (out_file_des, "", 1); - delayed_seek_count = 0; - } - + disk_empty_output_buffer (out_file_des, true); + set_copypass_perms (out_file_des, output_name.ds_string, &in_file_stat); if (reset_time_flag) { set_file_times (in_file_des, diff --git a/src/extern.h b/src/extern.h index c25a6ef..be329ae 100644 --- a/src/extern.h +++ b/src/extern.h @@ -73,13 +73,12 @@ extern char *new_media_message; extern char *new_media_message_with_number; extern char *new_media_message_after_number; extern int archive_des; extern char *archive_name; extern char *rsh_command_option; extern unsigned long crc; -extern int delayed_seek_count; #ifdef DEBUG_CPIO extern int debug_flag; #endif extern char *input_buffer, *output_buffer; extern char *in_buff, *out_buff; @@ -154,13 +153,13 @@ int is_tar_filename_too_long (char *name); /* userspec.c */ char *parse_user_spec (char *name, uid_t *uid, gid_t *gid, char **username, char **groupname); /* util.c */ void tape_empty_output_buffer (int out_des); -void disk_empty_output_buffer (int out_des); +void disk_empty_output_buffer (int out_des, bool flush); void swahw_array (char *ptr, int count); void tape_buffered_write (char *in_buf, int out_des, off_t num_bytes); void tape_buffered_read (char *in_buf, int in_des, off_t num_bytes); int tape_buffered_peek (char *peek_buf, int in_des, int num_bytes); void tape_toss_input (int in_des, off_t num_bytes); void copy_files_tape_to_disk (int in_des, int out_des, off_t num_bytes); @@ -1,9 +1,9 @@ /* util.c - Several utility routines for cpio. - Copyright (C) 1990, 1991, 1992, 2001, 2004, 2006, 2007, 2010 Free - Software Foundation, Inc. + Copyright (C) 1990, 1991, 1992, 2001, 2004, 2006, 2007, 2010, + 2011 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. @@ -97,28 +97,28 @@ tape_empty_output_buffer (int out_des) } output_bytes += output_size; out_buff = output_buffer; output_size = 0; } -static int sparse_write (int fildes, char *buf, unsigned int nbyte); +static ssize_t sparse_write (int fildes, char *buf, size_t nbyte, bool flush); /* Write `output_size' bytes of `output_buffer' to file descriptor OUT_DES and reset `output_size' and `out_buff'. If `swapping_halfwords' or `swapping_bytes' is set, do the appropriate swapping first. Our callers have to make sure to only set these flags if `output_size' is appropriate (a multiple of 4 for `swapping_halfwords', 2 for `swapping_bytes'). The fact that DISK_IO_BLOCK_SIZE must always be a multiple of 4 helps us (and our callers) insure this. */ void -disk_empty_output_buffer (int out_des) +disk_empty_output_buffer (int out_des, bool flush) { - int bytes_written; + ssize_t bytes_written; if (swapping_halfwords || swapping_bytes) { if (swapping_halfwords) { int complete_words; @@ -133,19 +133,22 @@ disk_empty_output_buffer (int out_des) complete_halfwords = output_size /2; swab_array (output_buffer, complete_halfwords); } } if (sparse_flag) - bytes_written = sparse_write (out_des, output_buffer, output_size); + bytes_written = sparse_write (out_des, output_buffer, output_size, flush); else bytes_written = write (out_des, output_buffer, output_size); if (bytes_written != output_size) { - error (1, errno, _("write error")); + if (bytes_written == -1) + error (1, errno, _("write error")); + else + error (1, 0, _("write error: partial write")); } output_bytes += output_size; out_buff = output_buffer; output_size = 0; } @@ -272,13 +275,13 @@ disk_buffered_write (char *in_buf, int out_des, off_t num_bytes) off_t space_left; /* Room left in output buffer. */ while (bytes_left > 0) { space_left = DISK_IO_BLOCK_SIZE - output_size; if (space_left == 0) - disk_empty_output_buffer (out_des); + disk_empty_output_buffer (out_des, false); else { if (bytes_left < space_left) space_left = bytes_left; memcpy (out_buff, in_buf, (unsigned) space_left); out_buff += space_left; @@ -1108,113 +1111,101 @@ buf_all_zeros (char *buf, int bufsize) if (*buf++ != '\0') return 0; } return 1; } -int delayed_seek_count = 0; +/* Write NBYTE bytes from BUF to file descriptor FILDES, trying to + create holes instead of writing blockfuls of zeros. + + Return the number of bytes written (including bytes in zero + regions) on success, -1 on error. -/* Write NBYTE bytes from BUF to remote tape connection FILDES. - Return the number of bytes written on success, -1 on error. */ + If FLUSH is set, make sure the trailing zero region is flushed + on disk. +*/ -static int -sparse_write (int fildes, char *buf, unsigned int nbyte) +static ssize_t +sparse_write (int fildes, char *buf, size_t nbytes, bool flush) { - int complete_block_count; - int leftover_bytes_count; - int seek_count; - int write_count; - char *cur_write_start; - int lseek_rc; - int write_rc; - int i; - enum { begin, in_zeros, not_in_zeros } state; - - complete_block_count = nbyte / DISKBLOCKSIZE; - leftover_bytes_count = nbyte % DISKBLOCKSIZE; - - if (delayed_seek_count != 0) - state = in_zeros; - else - state = begin; + size_t nwritten = 0; + ssize_t n; + char *start_ptr = buf; - seek_count = delayed_seek_count; + static off_t delayed_seek_count = 0; + off_t seek_count = 0; - for (i = 0; i < complete_block_count; ++i) + enum { begin, in_zeros, not_in_zeros } state = + delayed_seek_count ? in_zeros : begin; + + while (nbytes) { - switch (state) + size_t rest = nbytes; + + if (rest < DISKBLOCKSIZE) + /* Force write */ + state = not_in_zeros; + else { - case begin : - if (buf_all_zeros (buf, DISKBLOCKSIZE)) - { - seek_count = DISKBLOCKSIZE; - state = in_zeros; - } - else - { - cur_write_start = buf; - write_count = DISKBLOCKSIZE; - state = not_in_zeros; - } - buf += DISKBLOCKSIZE; - break; - - case in_zeros : - if (buf_all_zeros (buf, DISKBLOCKSIZE)) - { - seek_count += DISKBLOCKSIZE; - } - else - { - lseek (fildes, seek_count, SEEK_CUR); - cur_write_start = buf; - write_count = DISKBLOCKSIZE; - state = not_in_zeros; - } - buf += DISKBLOCKSIZE; - break; - - case not_in_zeros : - if (buf_all_zeros (buf, DISKBLOCKSIZE)) - { - write_rc = write (fildes, cur_write_start, write_count); - seek_count = DISKBLOCKSIZE; - state = in_zeros; - } - else - { - write_count += DISKBLOCKSIZE; - } - buf += DISKBLOCKSIZE; - break; + if (buf_all_zeros (buf, rest)) + { + if (state == not_in_zeros) + { + ssize_t bytes = buf - start_ptr + rest; + + n = write (fildes, start_ptr, bytes); + if (n == -1) + return -1; + nwritten += n; + if (n < bytes) + return nwritten + seek_count; + start_ptr = NULL; + } + else + seek_count += rest; + state = in_zeros; + } + else + { + seek_count += delayed_seek_count; + if (lseek (fildes, seek_count, SEEK_CUR) == -1) + return -1; + delayed_seek_count = seek_count = 0; + state = not_in_zeros; + start_ptr = buf; + } } + buf += rest; + nbytes -= rest; } - switch (state) + if (state != in_zeros) { - case begin : - case in_zeros : - delayed_seek_count = seek_count; - break; - - case not_in_zeros : - write_rc = write (fildes, cur_write_start, write_count); - delayed_seek_count = 0; - break; + seek_count += delayed_seek_count; + if (seek_count && lseek (fildes, seek_count, SEEK_CUR) == -1) + return -1; + delayed_seek_count = seek_count = 0; + + n = write (fildes, start_ptr, buf - start_ptr); + if (n == -1) + return n; + nwritten += n; } + delayed_seek_count += seek_count; - if (leftover_bytes_count != 0) + if (flush && delayed_seek_count) { - if (delayed_seek_count != 0) - { - lseek_rc = lseek (fildes, delayed_seek_count, SEEK_CUR); - delayed_seek_count = 0; - } - write_rc = write (fildes, buf, leftover_bytes_count); - } - return nbyte; + if (lseek (fildes, delayed_seek_count - 1, SEEK_CUR) == -1) + return -1; + n = write (fildes, "", 1); + if (n != 1) + return n; + delayed_seek_count = 0; + } + + return nwritten + seek_count; } #define CPIO_UID(uid) (set_owner_flag ? set_owner : (uid)) #define CPIO_GID(gid) (set_group_flag ? set_group : (gid)) void |