diff options
author | Pádraig Brady <P@draigBrady.com> | 2012-06-22 09:32:34 +0100 |
---|---|---|
committer | Pádraig Brady <P@draigBrady.com> | 2012-06-22 11:34:21 +0100 |
commit | ae5846448f73d90ba9bd01cc9edc94bc0b168e46 (patch) | |
tree | 86d73149007b304586c0d8604ab39d0436528089 | |
parent | 30a604e631291b80b9812e03e0f325489ae0d77e (diff) | |
download | coreutils-ae5846448f73d90ba9bd01cc9edc94bc0b168e46.tar.xz |
split: ensure output doesn't overwrite input
* src/split.c (create): Check if output file is the
same inode as the input file.
* tests/split/guard-input: New test case.
* tests/Makefile.am: Reference new test case.
* NEWS: Mention the fix.
Improved-by: Jim Meyering
Reported-by: François Pinard
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | src/split.c | 27 | ||||
-rw-r--r-- | tests/Makefile.am | 1 | ||||
-rwxr-xr-x | tests/split/guard-input | 33 |
4 files changed, 58 insertions, 6 deletions
@@ -18,6 +18,9 @@ GNU coreutils NEWS -*- outline -*- ls --color would mis-color relative-named symlinks in / [bug introduced in coreutils-8.17] + split now ensures it doesn't overwrite the input file with generated output. + [the bug dates back to the initial implementation] + stat and df now report the correct file system usage, in all situations on GNU/Linux, by correctly determining the block size. [df bug since coreutils-5.0.91, stat bug since the initial implementation] diff --git a/src/split.c b/src/split.c index 46d2511ae..7ba743cb0 100644 --- a/src/split.c +++ b/src/split.c @@ -92,6 +92,9 @@ static char const *additional_suffix; /* Name of input file. May be "-". */ static char *infile; +/* stat buf for input file. */ +static struct stat in_stat_buf; + /* Descriptor on which output file is open. */ static int output_desc = -1; @@ -362,7 +365,20 @@ create (const char *name) { if (verbose) fprintf (stdout, _("creating file %s\n"), quote (name)); - return open (name, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, MODE_RW_UGO); + + int fd = open (name, O_WRONLY | O_CREAT | O_BINARY, MODE_RW_UGO); + if (fd < 0) + return fd; + struct stat out_stat_buf; + if (fstat (fd, &out_stat_buf) != 0) + error (EXIT_FAILURE, errno, _("failed to stat %s"), quote (name)); + if (SAME_INODE (in_stat_buf, out_stat_buf)) + error (EXIT_FAILURE, 0, _("%s would overwrite input; aborting"), + quote (name)); + if (ftruncate (fd, 0) != 0) + error (EXIT_FAILURE, errno, _("%s: error truncating"), quote (name)); + + return fd; } else { @@ -1057,7 +1073,6 @@ parse_chunk (uintmax_t *k_units, uintmax_t *n_units, char *slash) int main (int argc, char **argv) { - struct stat stat_buf; enum Split_type split_type = type_undef; size_t in_blk_size = 0; /* optimal block size of input file device */ char *buf; /* file i/o buffer */ @@ -1334,16 +1349,16 @@ main (int argc, char **argv) /* Get the optimal block size of input device and make a buffer. */ - if (fstat (STDIN_FILENO, &stat_buf) != 0) + if (fstat (STDIN_FILENO, &in_stat_buf) != 0) error (EXIT_FAILURE, errno, "%s", infile); if (in_blk_size == 0) - in_blk_size = io_blksize (stat_buf); + in_blk_size = io_blksize (in_stat_buf); if (split_type == type_chunk_bytes || split_type == type_chunk_lines) { off_t input_offset = lseek (STDIN_FILENO, 0, SEEK_CUR); - if (usable_st_size (&stat_buf)) - file_size = stat_buf.st_size; + if (usable_st_size (&in_stat_buf)) + file_size = in_stat_buf.st_size; else if (0 <= input_offset) { file_size = lseek (STDIN_FILENO, 0, SEEK_END); diff --git a/tests/Makefile.am b/tests/Makefile.am index d8bc93020..2155cee83 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -268,6 +268,7 @@ TESTS = \ split/l-chunk \ split/r-chunk \ split/numeric \ + split/guard-input \ misc/stat-birthtime \ misc/stat-fmt \ misc/stat-hyphen \ diff --git a/tests/split/guard-input b/tests/split/guard-input new file mode 100755 index 000000000..7a6fba3f5 --- /dev/null +++ b/tests/split/guard-input @@ -0,0 +1,33 @@ +#!/bin/sh +# ensure split doesn't overwrite input with output. + +# Copyright (C) 2012 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 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +. "${srcdir=.}/init.sh"; path_prepend_ ../src +print_ver_ split + +seq 10 | tee exp-1 > xaa +ln -s xaa in2 +ln xaa in3 + +split -C 6 xaa && fail=1 +split -C 6 in2 && fail=1 +split -C 6 in3 && fail=1 +split -C 6 - < xaa && fail=1 + +compare exp-1 xaa || fail=1 + +Exit $fail |