Subject: bug#33761: closed (Re: bug#33761: Allow split to write into FIFOs)

From: [email-masked] (GNU bug Tracking System)
Date: Sat, 15 Dec 2018 20:21:02 +0000

Your bug report

#33761: Allow split to write into FIFOs

which was filed against the coreutils package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
33761: http://debbugs.gnu.org/cgi/bugreport.cgi?bug=33761
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#33761: Allow split to write into FIFOs
From: Paul Eggert <[email-masked]>
To: [email-masked], [email-masked]
Date: Sat, 15 Dec 2018 12:20:00 -0800
List-Id: <debbugs-submit.debbugs.gnu.org>

Thanks for reporting that. I see similar problems in a couple of other coreutils 
programs. The style elsewhere seems to be to try ftruncate first, and report an 
error if it fails only if the file is not one for which POSIX specifies the 
behavior, so I think it'd be better if 'split' did that too. So I installed the 
attached patch to fix the problems that I found, and this should fix the bug you 
reported so I'll close the bug report.

Thanks also for the test case. To install that I'll need a copyright assignment 
form signed; can you do that for us? If so, please let me know and I'll get the 
paperwork ball rolling for you.
>From 5af28df87d7af0736b2ad5e09ae6b3784f6af3af Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Sat, 15 Dec 2018 12:13:58 -0800
Subject: [PATCH] shred,sort,split: fix ftruncate error reporting

Problem reported for split by Scott Worley (Bug#33761):
* src/shred.c (do_wipefd):
Also report an error if ftruncate fails	on a shared memory object.
* src/sort.c (get_outstatus): New function.
(stream_open, avoid_trashing_input): Use it.
* src/sort.c (stream_open):
* src/split.c (create):
If ftruncate fails, do not report an error
unless it is a regular file or a shared memory object.
---
 src/shred.c | 10 ++++++----
 src/sort.c  | 39 ++++++++++++++++++++++++++-------------
 src/split.c |  3 ++-
 3 files changed, 34 insertions(+), 18 deletions(-)

diff --git a/src/shred.c b/src/shred.c
index 270b1e942..3a6510cf3 100644
--- a/src/shred.c
+++ b/src/shred.c
@@ -975,11 +975,13 @@ do_wipefd (int fd, char const *qname, struct randint_source *s,
         }
     }
 
-  /* Now deallocate the data.  The effect of ftruncate on
-     non-regular files is unspecified, so don't worry about any
-     errors reported for them.  */
+  /* Now deallocate the data.  The effect of ftruncate is specified
+     on regular files and shared memory objects (also directories, but
+     they are not possible here); don't worry about errors reported
+     for other file types.  */
+
   if (flags->remove_file && ftruncate (fd, 0) != 0
-      && S_ISREG (st.st_mode))
+      && (S_ISREG (st.st_mode) || S_TYPEISSHM (&st)))
     {
       error (0, errno, _("%s: error truncating"), qname);
       ok = false;
diff --git a/src/sort.c b/src/sort.c
index dd6bce47f..ebe86d3fb 100644
--- a/src/sort.c
+++ b/src/sort.c
@@ -895,8 +895,21 @@ create_temp_file (int *pfd, bool survive_fd_exhaustion)
   return node;
 }
 
-/* Return a stream for FILE, opened with mode HOW.  A null FILE means
-   standard output; HOW should be "w".  When opening for input, "-"
+/* Return a pointer to stdout status, or NULL on failure.  */
+
+static struct stat *
+get_outstatus (void)
+{
+  static int outstat_errno;
+  static struct stat outstat;
+  if (outstat_errno == 0)
+    outstat_errno = fstat (STDOUT_FILENO, &outstat) == 0 ? -1 : errno;
+  return outstat_errno < 0 ? &outstat : NULL;
+}
+
+/* Return a stream for FILE, opened with mode HOW.  If HOW is "w",
+   the file is already open on standard output, and needs to be
+   truncated unless FILE is null.  When opening for input, "-"
    means standard input.  To avoid confusion, do not return file
    descriptors STDIN_FILENO, STDOUT_FILENO, or STDERR_FILENO when
    opening an ordinary FILE.  Return NULL if unsuccessful.
@@ -964,8 +977,13 @@ stream_open (char const *file, char const *how)
   else if (*how == 'w')
     {
       if (file && ftruncate (STDOUT_FILENO, 0) != 0)
-        die (SORT_FAILURE, errno, _("%s: error truncating"),
-             quotef (file));
+        {
+          int ftruncate_errno = errno;
+          struct stat *outst = get_outstatus ();
+          if (!outst || S_ISREG (outst->st_mode) || S_TYPEISSHM (outst))
+            die (SORT_FAILURE, ftruncate_errno, _("%s: error truncating"),
+                 quotef (file));
+        }
       fp = stdout;
     }
   else
@@ -3699,8 +3717,6 @@ static void
 avoid_trashing_input (struct sortfile *files, size_t ntemps,
                       size_t nfiles, char const *outfile)
 {
-  bool got_outstat = false;
-  struct stat outstat;
   struct tempnode *tempcopy = NULL;
 
   for (size_t i = ntemps; i < nfiles; i++)
@@ -3713,18 +3729,15 @@ avoid_trashing_input (struct sortfile *files, size_t ntemps,
         same = true;
       else
         {
-          if (! got_outstat)
-            {
-              if (fstat (STDOUT_FILENO, &outstat) != 0)
-                break;
-              got_outstat = true;
-            }
+          struct stat *outst = get_outstatus ();
+          if (!outst)
+            break;
 
           same = (((is_stdin
                     ? fstat (STDIN_FILENO, &instat)
                     : stat (files[i].name, &instat))
                    == 0)
-                  && SAME_INODE (instat, outstat));
+                  && SAME_INODE (instat, *outst));
         }
 
       if (same)
diff --git a/src/split.c b/src/split.c
index 60c14d36c..1431bdf65 100644
--- a/src/split.c
+++ b/src/split.c
@@ -470,7 +470,8 @@ create (const char *name)
       if (SAME_INODE (in_stat_buf, out_stat_buf))
         die (EXIT_FAILURE, 0, _("%s would overwrite input; aborting"),
              quoteaf (name));
-      if (ftruncate (fd, 0) != 0)
+      if (ftruncate (fd, 0) != 0
+          && (S_ISREG (out_stat_buf.st_mode) || S_TYPEISSHM (&out_stat_buf)))
         die (EXIT_FAILURE, errno, _("%s: error truncating"), quotef (name));
 
       return fd;
-- 
2.17.1

Subject: Allow split to write into FIFOs
From: [email-masked]
To: [email-masked]
Date: Sat, 15 Dec 2018 00:00:18 -0800
List-Id: <debbugs-submit.debbugs.gnu.org>

TL;DR: Attached: Patch to allow split to write into FIFOs.

Currently, split fails when its output files are FIFOs because
FIFOs cannot be ftruncate()d.  Split exits with this error message:

  split: xaa: error truncating: Invalid argument

The attached patch causes split to only attempt the ftruncate when its
output is a regular file.
--- coreutils-8.29/src/split.c.orig	2017-09-20 01:17:21.000000000 +0000
+++ coreutils-8.29/src/split.c	2018-12-15 07:37:31.000000000 +0000
@@ -470,7 +470,7 @@
       if (SAME_INODE (in_stat_buf, out_stat_buf))
         die (EXIT_FAILURE, 0, _("%s would overwrite input; aborting"),
              quoteaf (name));
-      if (ftruncate (fd, 0) != 0)
+      if (S_ISREG(out_stat_buf.st_mode) && ftruncate (fd, 0) != 0)
         die (EXIT_FAILURE, errno, _("%s: error truncating"), quotef (name));
 
       return fd;
--- coreutils-8.29/Makefile.in.orig	2017-12-27 18:25:58.000000000 +0000
+++ coreutils-8.29/Makefile.in	2018-12-15 07:03:11.000000000 +0000
@@ -5491,6 +5491,7 @@
   tests/split/additional-suffix.sh		\
   tests/split/b-chunk.sh			\
   tests/split/fail.sh				\
+  tests/split/fifo.sh				\
   tests/split/lines.sh				\
   tests/split/line-bytes.sh			\
   tests/split/l-chunk.sh			\
--- /dev/null	2018-08-27 15:55:28.530128552 +0000
+++ coreutils-8.29/tests/split/fifo.sh	2018-12-15 07:36:50.000000000 +0000
@@ -0,0 +1,47 @@
+#!/bin/sh
+# Verify that output can be written to FIFOs
+
+# Copyright (C) 2002-2017 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 <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ split
+
+printf '1\n2\n3\n' > in || framework_failure_
+
+cat <<\EOF > exp-1
+1
+2
+EOF
+cat <<\EOF > exp-2
+3
+EOF
+
+mkfifo xaa xab || framework_failure_
+
+split --lines=2 in > out &
+split_pid=$!
+
+for f in xaa xab;do
+  timeout 10 cat "$f" > "$f-data"
+done
+
+wait "$split_pid" || fail=1
+
+compare exp-1 xaa-data || fail=1
+compare exp-2 xab-data || fail=1
+test -f xac && fail=1
+
+Exit $fail