[net.emacs] visit-file bug fix

glenn@nsc.UUCP (Glenn Skinner) (12/05/84)

Subject: visit-file can confuse new files for old
Index:	fileio.c, Gosling emacs #264 and #265

Description:
	The VisitFile routine in fileio.c checks each previously existent
	buffer to see whether its associated file is the same file as the
	new file to be visited.  If it finds a match, it reuses the old
	buffer instead of creating a new one.

	There are two conditions that constitute a match: 1) the file names
	are identical, or 2) the device and inode numbers (stat info) are
	the same.  Condition 2 is checked so that symbolic links don't
	prevent recognition of file identity.

	Unfortunately, there's a serious problem with the implementation of
	condition 2.  The buffer's stat info is recorded when the buffer's
	contents are read in and can become invalid by the time the info is
	used for this check.  That is, the original file could have been
	unlinked and a new file created that happens to reuse the same inode.
	This situation causes a spurious match, causing the old buffer's
	contents to be obliterated when they shouldn't be.

Repeat-By:
	Create a temporary file, visit it, remove the temporary file and
	create a new one of a different name (doing it quickly, so that
	the old one's inode will be reused), and visit the new temporary
	file.  The new file's contents will replace the previous contents
	of the original file's buffer.
Fix:
	Install the change given by the context diff below.  Line numbers
	will be off, since we add SCCS headers and have made other
	changes to the file.

		-- Glenn Skinner
		National Semiconductor, Microcomputer Systems Division
		(408) 733-2600 x 335
----------------------------------------------------------------

------- fileio.c -------
*** /tmp/d29677	Wed Dec  5 11:33:44 1984
--- fileio.c	Wed Dec  5 10:12:36 1984
***************
*** 286,297
  	st.st_dev = 0;
  	st.st_mtime = 0;
      }
!     for (b = buffers;
! 	    b && (b -> b_fname == 0
! 		|| (strcmp (fullname, b -> b_fname) != 0
! 		    && (st.st_dev == 0 || b -> b_dev == 0 ||
! 			st.st_dev != b -> b_dev || st.st_ino != b -> b_ino)));
! 	    b = b -> b_next);
      if (b) {
  	SetBfp (b);
  	if (strcmp (fullname, b->b_fname) != 0) {

--- 287,330 -----
  	st.st_dev = 0;
  	st.st_mtime = 0;
      }
!     /* Look for a buffer that already contains the file.  We deem this to
!        be the case if one of two criteria is satisfied: the names are the
!        same, or the devices and inodes are the same.  Unfortunately, there's
!        a serious problem with the second of these criteria.  Between the
!        times of recording the stat info for the buffer and for the file, the
!        original file could have been unlinked and a new one created using
!        the same inode.  This situation causes a spurious match.  Thus, we're
!        forced into regarding the buffer's recorded stat info as only a hint
!        and must reobtain it when we get a possible match. */
!     for (b = buffers; b; b = b -> b_next) {
! 	struct stat bsb;
! 
! 	if (b -> b_fname == 0)
! 	    continue;
! 	if (strcmp (fullname, b -> b_fname) == 0)
! 	    break;
! 	if (  st.st_dev == 0
! 	   || b -> b_dev == 0
! 	   || st.st_dev != b -> b_dev
! 	   || st.st_ino != b -> b_ino
! 	   )
! 	    continue;
! 
! 	/* We have a tentative stat info match.  Verify it. */
! 	if (stat (b -> b_fname, &bsb) < 0) {
! 	    /* The file the buffer was tied to no longer exists. */
! 	    b -> b_dev = 0;
! 	    continue;
! 	}
! 	else {
! 	    /* Update the buffer's stat info. */
! 	    b -> b_dev = bsb.st_dev;
! 	    b -> b_ino = bsb.st_ino;
! 	}
! 	/* Recheck. */
! 	if (st.st_dev == b -> b_dev && st.st_ino == b -> b_ino)
! 	    break;
!     }
      if (b) {
  	SetBfp (b);
  	if (strcmp (fullname, b->b_fname) != 0) {