diff options
author | Pádraig Brady <P@draigBrady.com> | 2012-07-01 01:14:42 +0100 |
---|---|---|
committer | Pádraig Brady <P@draigBrady.com> | 2012-07-02 21:14:29 +0200 |
commit | 44fbd3fd862e34d42006f8b74cb11c9c56346417 (patch) | |
tree | 2455d37347b3e504d30c33c607cd1f096c9c51c5 /src | |
parent | 8285c2aca0ee81ca2ce0507084259de8384e809f (diff) | |
download | coreutils-44fbd3fd862e34d42006f8b74cb11c9c56346417.tar.xz |
sort: avoid redundant processing with inaccessible inputs or output
* src/sort.c (check_inputs): A new function to verify all inputs
are accessible before further processing.
(check_output): A new function to open or create a specified
output file, before futher processing.
(stream_open): Adjust to truncating the previously opened
output file rather than opening directly.
(avoid_trashing_input): Optimize to stat the output file
descriptor, rather than the file name.
(main): Call the new functions to check accessibility of
inputs and output, before processing starts.
* tests/misc/sort: Adjust to the changed error message.
* tests/misc/sort-merge-fdlimit: Account for the earlier opened
file descriptor of the specified output file.
* tests/misc/sort-exit-early: A new test to exercise the improvements.
* tests/Makefile.am: Reference the new test.
* NEWS: Mention the improvement.
Suggested-by: Bernhard Voelker
Diffstat (limited to 'src')
-rw-r--r-- | src/sort.c | 65 |
1 files changed, 61 insertions, 4 deletions
diff --git a/src/sort.c b/src/sort.c index 5a48ce6f0..e4067c9fb 100644 --- a/src/sort.c +++ b/src/sort.c @@ -357,6 +357,9 @@ static bool unique; /* Nonzero if any of the input files are the standard input. */ static bool have_read_stdin; +/* File descriptor associated with -o. */ +static int outfd = -1; + /* List of key field comparisons to be tried. */ static struct keyfield *keylist; @@ -911,11 +914,12 @@ create_temp_file (int *pfd, bool survive_fd_exhaustion) static FILE * stream_open (char const *file, char const *how) { + FILE *fp; + if (!file) return stdout; if (*how == 'r') { - FILE *fp; if (STREQ (file, "-")) { have_read_stdin = true; @@ -924,9 +928,18 @@ stream_open (char const *file, char const *how) else fp = fopen (file, how); fadvise (fp, FADVISE_SEQUENTIAL); - return fp; } - return fopen (file, how); + else if (*how == 'w') + { + assert (outfd != -1); + if (ftruncate (outfd, 0) != 0) + error (EXIT_FAILURE, errno, _("%s: error truncating"), quote (file)); + fp = fdopen (outfd, how); + } + else + assert (!"unexpected mode passed to stream_open"); + + return fp; } /* Same as stream_open, except always return a non-null value; die on @@ -3637,7 +3650,7 @@ avoid_trashing_input (struct sortfile *files, size_t ntemps, if (! got_outstat) { if ((outfile - ? stat (outfile, &outstat) + ? fstat (outfd, &outstat) : fstat (STDOUT_FILENO, &outstat)) != 0) break; @@ -3666,6 +3679,44 @@ avoid_trashing_input (struct sortfile *files, size_t ntemps, } } +/* Scan the input files to ensure all are accessible. + Otherwise exit with a diagnostic. + + Note this will catch common issues with permissions etc. + but will fail to notice issues where you can open() but not read(), + like when a directory is specified on some systems. + Catching these obscure cases could slow down performance in + common cases. */ + +static void +check_inputs (char *const *files, size_t nfiles) +{ + size_t i; + for (i = 0; i < nfiles; i++) + { + if (STREQ (files[i], "-")) + continue; + + if (euidaccess (files[i], R_OK) != 0) + die (_("cannot read"), files[i]); + } +} + +/* Ensure a specified output file can be created or written to, + and cache the returned descriptor in the global OUTFD variable. + Otherwise exit with a diagnostic. */ + +static void +check_output (char const *outfile) +{ + if (outfile) + { + outfd = open (outfile, O_WRONLY | O_CREAT | O_BINARY, MODE_RW_UGO); + if (outfd < 0) + die (_("open failed"), outfile); + } +} + /* Merge the input FILES. NTEMPS is the number of files at the start of FILES that are temporary; it is zero at the top level. NFILES is the total number of files. Put the output in @@ -4620,6 +4671,12 @@ main (int argc, char **argv) exit (check (files[0], checkonly) ? EXIT_SUCCESS : SORT_OUT_OF_ORDER); } + /* Check all inputs are accessible, or exit immediately. */ + check_inputs (files, nfiles); + + /* Check output is writable, or exit immediately. */ + check_output (outfile); + if (mergeonly) { struct sortfile *sortfiles = xcalloc (nfiles, sizeof *sortfiles); |