From ae5846448f73d90ba9bd01cc9edc94bc0b168e46 Mon Sep 17 00:00:00 2001 From: =?utf8?q?P=C3=A1draig=20Brady?= Date: Fri, 22 Jun 2012 09:32:34 +0100 Subject: [PATCH] split: ensure output doesn't overwrite input MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit * 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 --- NEWS | 3 +++ src/split.c | 27 +++++++++++++++++++++------ tests/Makefile.am | 1 + tests/split/guard-input | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 6 deletions(-) create mode 100755 tests/split/guard-input diff --git a/NEWS b/NEWS index 54d24c3..8c75a32 100644 --- a/NEWS +++ b/NEWS @@ -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 46d2511..7ba743c 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 d8bc930..2155cee 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 0000000..7a6fba3 --- /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 . + +. "${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 -- 2.7.4