[comp.os.minix] A few bugs in 1.5.5: named pipes, sh

msm@nucleus.mi.org (Michael S. Mattone) (04/07/90)

I've noticed a few bugs in MINIX 1.5.5.  I'm posting this in case any
of these haven't been noticed before.  Maybe they will make it into
official 1.5.6 changes to be posted in April.

1. Pipe limit 7K.  The following fails if 'fd' is a pipe:
	write(fd, buf, 8000);
   This bug (feature?) has been annoying me since 1.2.  Anyone else?
   I posted my modifications to make this problem go away 2 1/2 times
   before.  Should I try for 3 1/2?  Or is this something POSIX requires?

2. Named Pipes.  I was playing with named pipes and ran into
   a few problems.  Since a named pipe doesn't disappear like a normal
   pipe until it is unlinked, the trick being used to allow de(1) to
   recover deleted files causes problems.  The message:
	FS freeing unused block of inode, bit = #
                                ^^ - typo?  should this be "or"
   is displayed when rm'ing a named pipe, and messes up the filesystem.
   This appears easy to fix in fs/link.c by calling wipe_inode()
   in truncate() if the inode is associated with a pipe.
   Cdiff included below.

3. Incompatible mknod().  The V7 UNIX mknod(2) takes three arguments,
   mknod(name, mode, addr)  but MINIX requires a fourth arg to
   specify the size of a device.  For portability and compatibility
   with UNIX, could the MINIX mknod() be renamed to something like
   mknodx() and a UNIX compatible mknod() that calls mknodx()
   with a 0 for the size argument?  MINIX programs that need to would
   use mknodx(), ported programs wouldn't need to know mknod() was
   different.

4. Shell read built-in.  The shell doesn't handle:
	read arg1 arg2 ... argN
   properly if fewer than N words are entered by the user.  As delivered
   the shell forces the user to type a return for each of the left over
   arguments that there aren't words for.
   Another cdiff for commands/sh/sh3.c is included.

--------------------------------cut here--------------------------------

echo x - link.cdif
sed '/^X/s///' > link.cdif << '/'
X*** link.c.old	Fri Apr  6 16:47:47 1990
X--- link.c	Fri Apr  6 16:47:47 1990
X***************
X*** 310,316 ****
X    register zone_nr z, *iz;
X    off_t position;
X    zone_type zone_size;
X!   int scale, file_type;
X    struct buf *bp;
X    dev_t dev;
X  
X--- 310,316 ----
X    register zone_nr z, *iz;
X    off_t position;
X    zone_type zone_size;
X!   int scale, file_type, waspipe;
X    struct buf *bp;
X    dev_t dev;
X  
X***************
X*** 319,325 ****
X    dev = rip->i_dev;		/* device on which inode resides */
X    scale = scale_factor(rip);
X    zone_size = (zone_type) BLOCK_SIZE << scale;
X!   if (rip->i_pipe == I_PIPE) rip->i_size = PIPE_SIZE;	/* pipes can shrink */
X  
X    /* Step through the file a zone at a time, finding and freeing the zones. */
X    for (position = 0; position < rip->i_size; position += zone_size) {
X--- 319,326 ----
X    dev = rip->i_dev;		/* device on which inode resides */
X    scale = scale_factor(rip);
X    zone_size = (zone_type) BLOCK_SIZE << scale;
X!   if (waspipe = (rip->i_pipe == I_PIPE))
X! 	rip->i_size = PIPE_SIZE;	/* pipes can shrink */
X  
X    /* Step through the file a zone at a time, finding and freeing the zones. */
X    for (position = 0; position < rip->i_size; position += zone_size) {
X***************
X*** 344,348 ****
X    }
X  
X    /* Leave zone numbers for de(1) to recover file after an unlink(2).  */
X!   rip->i_dirt = DIRTY;
X  }
X--- 345,351 ----
X    }
X  
X    /* Leave zone numbers for de(1) to recover file after an unlink(2).  */
X!   if (waspipe)
X! 	wipe_inode(rip);	/* clear out inode for pipes */
X!   rip->i_dirt = DIRTY;
X  }
/
echo x - sh3.cdif
sed '/^X/s///' > sh3.cdif << '/'
X*** sh3.c.old	Tue Feb 20 04:26:37 1990
X--- sh3.c	Tue Feb 20 12:29:01 1990
X***************
X*** 776,790 ****
X  {
X  	register char *cp, **wp;
X  	register nb;
X  
X  	if (t->words[1] == NULL) {
X  		err("Usage: read name ...");
X  		return(1);
X  	}
X  	for (wp = t->words+1; *wp; wp++) {
X! 		for (cp = e.linep; cp < elinep-1; cp++)
X! 			if ((nb = read(0, cp, sizeof(*cp))) != sizeof(*cp) ||
X! 			    *cp == '\n' ||
X  			    wp[1] && any(*cp, ifs->value))
X  				break;
X  		*cp = 0;
X--- 776,791 ----
X  {
X  	register char *cp, **wp;
X  	register nb;
X+ 	register int  nl = 0;
X  
X  	if (t->words[1] == NULL) {
X  		err("Usage: read name ...");
X  		return(1);
X  	}
X  	for (wp = t->words+1; *wp; wp++) {
X! 		for (cp = e.linep; !nl && cp < elinep-1; cp++)
X! 			if ((nb = read(0, cp, sizeof(*cp))) != sizeof(*cp) ||
X! 			    (nl = (*cp == '\n')) ||
X  			    wp[1] && any(*cp, ifs->value))
X  				break;
X  		*cp = 0;
/
exit 0

archer%segin4.segin.fr@prime.com (Vincent Archer) (04/09/90)

Michael S. Mattone <msm@NUCLEUS.MI.ORG> says:

MSM>1. Pipe limit 7K.  The following fails if 'fd' is a pipe:
MSM>        write(fd, buf, 8000);
MSM>   This bug (feature?) has been annoying me since 1.2.  Anyone else?
MSM>   I posted my modifications to make this problem go away 2 1/2 times
MSM>   before.  Should I try for 3 1/2?  Or is this something POSIX requires?

Better yet, it fails with a specific error code saying that it's too big (8000
that is). That's because Unix requires writes to be atomic. Otherwise, you
could find (assuming two processes writing onto the same pipe) the following
situation:

    P1 says: write(8000)
             the pipe accepts 7K, and P1 waits
    P1 gets: signal -9
             and is killed. FS notices this and purges the remaining 1K
    P2 says: read(8000)
             and gets the 7K, but never the 1K (missing)

In that situation, you get a partial block of data. If you're using your pipe
as a formatted packet stream, you wind up with a non-terminated packet. This
can get annoying sometimes, and Unix therefore tries to forbid a write to be
done in two (or more) chunks. Do not correct this behaviour, it's standard
practice on Unix.


    Vincent Archer

Email: archer%segin4.segin.fr@prime.com     (Yes, indirect address required!)