Subject: kern/18767: broken behaviour of pipes in Linux emulated shell scripts
To: None <>
From: None <>
List: netbsd-bugs
Date: 10/22/2002 09:21:16
>Number:         18767
>Category:       kern
>Synopsis:       broken behaviour of pipes in Linux emulated shell scripts
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    kern-bug-people
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue Oct 22 09:22:00 PDT 2002
>Originator:     Emmanuel.Dreyfus
>Release:        NetBSD-1.6
The NetBSD Project
NetBSD mmX 1.6_STABLE NetBSD 1.6_STABLE (MM) #4: Tue Oct 22 17:56:51 CEST 2002     manu@melancolie:/usr/src/sys/arch/i386/compile/MM i386

pipes in shell Linux shell scripts running with COMPAT_LINUX loose 
data, and this breaks some install scripts.

How it happens:
bash makes an ioctl(TCGETS) on stdin and adapt its behaviour depending 
on the result. If the ioctl succeeds, which is what NetBSD-1.6 does,
then it will read chunk of data with blocksize as the size, and it 
will lseek back on stdin to go back to the beginning of the lines.

Of course if stdin is a pipe it will not work, thus producing what we
have on NetBSD-1.6

If the ioctl returns EINVAL, bash will read one char at a time, doing 
dozens of read system calls. This is what happens on Linux, and it 
works with this.
cat /etc/group | while read line ; do echo ">>$line<<"; done

On 1.6/i386 with Linux userland from pkgsrc, I get this
Here is the fix to get the Linux behaviour:

Index: linux_termios.c
RCS file: /cvsroot/syssrc/sys/compat/linux/common/linux_termios.c,v
retrieving revision 1.15
diff -U4 -r1.15 linux_termios.c
--- linux_termios.c     2001/12/19 15:20:16     1.15
+++ linux_termios.c     2002/10/22 16:16:51
@@ -494,8 +494,10 @@
        retval[0] = 0;
        switch (com) {
        case LINUX_TCGETS:
+               if (fp->f_type == DTYPE_PIPE)
+                       return EINVAL;
                error = (*bsdioctl)(fp, TIOCGETA, (caddr_t)&tmpbts, p);
                if (error)
                        return error;
                bsd_termios_to_linux_termios(&tmpbts, &tmplts);

While tracking this problem, I discovered an inconsistency in 
Linux fstat emulation: When doing fstat on a pipe, st_mode is 1180 
(instead of 1000), and st_blocks, st_dev and st_rdev are set to zero.
Here is a fix to this inconsistency (does not fix anything yet, but
it will make a better emulation)

Index: linux_file64.c
RCS file: /cvsroot/syssrc/sys/compat/linux/common/linux_file64.c,v
retrieving revision 1.18
diff -U4 -r1.18 linux_file64.c
--- linux_file64.c      2002/05/20 06:45:11     1.18
+++ linux_file64.c      2002/10/22 16:16:50
@@ -106,8 +106,14 @@
        lsp->lst_ctime   = bsp->st_ctime;
        lsp->__lst_ino   = (linux_ino_t) bsp->st_ino;
+       if (lsp->lst_mode & S_IFIFO) {
+               lsp->lst_mode = (S_IFIFO | 0600);
+               lsp->lst_blocks = 0;
+               lsp->lst_dev = 0;
+               lsp->lst_rdev = 0;
+       }
  * The stat functions below are plain sailing. stat and lstat are handled

 Which is completely broken.