[net.bugs.4bsd] Standard I/O streams open for reading and writing

guy@rlgvax.UUCP (Guy Harris) (08/15/83)

The Standard I/O library for releases of UNIX from V7 on includes a feature
to permit you to open a stream for reading and writing.  This feature is
documented in USG UNIX (System III, System V).  There is one minor bug in
the V7 implementation, and an additional major bug in the 4.?BSD implementation.

The first bug is that if your "umask" takes away your own read or write
permission, you may not use the "w+" or "a+" modes.  The problem is that
these modes require "stdio" to do a "creat" to create the file, and then to
close and reopen the file in order to have it open for reading and writing
(USG doesn't have to do this, but interestingly enough the S3 "stdio" does
it anyway), so if your umask takes away read or write permissions the reopen
fails.  The fix is to change the lines

	f = creat(file, 0666);
	if (rw && f>=0) {
		close(f);
		f = open(file, 2);
	}

to

	if (rw) {
		int m = umask(0);
		f = creat(file, 0666);
		if (f>=0) {
			close(f);
			f = open(file, 2);
			chmod(file, 0666 & ~m);
		}
		umask(m);
	} else
		f = creat(file, 0666);

in "endopen.c" in V7, and in "fopen.c" and "freopen.c" in 4.?BSD.

The more serious bug is due to the way the 4.?BSD "stdio" handles "fseek".
If you have a stream open for reading and writing, you must do an "fseek"
if you want to switch from reading to writing or vice versa (this is documented
in the USG manuals, so it's not a buried feature).  However, if you do an
"fseek" to an odd-byte boundary on 4.?BSD on a stream where the last operation
was a read, it seeks to the previous byte and does a "getc" to move to the next
byte.  This is not a problem if the stream is open only for reading or only for
writing (I presume it was put in there for efficiency; PDP-11 UNIX moves data
from kernel to user or vice versa much more efficiently if it is on a word
boundary), but it makes it impossible to put an "fseek" after a read and
immediately before a write (because the "fseek" may do a read before you get
a chance to do your write).  The code in "fseek.c" to handle seeks on a
stream on which the last operation was a read has a section which reads:

		if (iop->_flag & _IORW) {
			iop->_ptr = iop->_base;
			iop->_flag &= ~_IOREAD;
		}

A line "resync = 0;" should be added after the "iop->_flag &= ~_IOREAD;".  This
way, "fseek" will behave as it did if the stream is only open for reading, but
will not do the "resync" if it is open for reading and writing.

	Guy Harris
	{seismo,mcnc,we13,brl-bmd,allegra}!rlgvax!guy