Subject: kern/857: namei cacheing of whiteouts incorrect; fix included
To: None <gnats-admin@NetBSD.ORG>
From: Lon Willett <lon%softt.uucp@math.utah.edu>
List: netbsd-bugs
Date: 03/08/1995 15:20:06
>Number:         857
>Category:       kern
>Synopsis:       namei cacheing of whiteouts incorrect; fix included
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    kern-bug-people (Kernel Bug People)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Mar  8 15:20:06 1995
>Originator:     Lon Willett
>Organization:
none
>Release:        NetBSD-current 8 Mar 95
>Environment:

System: NetBSD hilly 1.0A NetBSD 1.0A (HILLY) #150: Wed Mar 8 13:40:25 MST 1995 lon@hilly:/usr/src/sys/arch/i386/compile/HILLY i386

>Description:

"Whiteouts" are entered in the namei cache, i.e. as negative cache entries
(lookup-->ENOENT).  But since the cache doesn't record the fact that the
lookup failed because it was a whiteout, further accesses to this cache
entry from the top of a union mount produce incorrect results (it acts as
if there were no file on top; instead of a whiteout).

>How-To-Repeat:

Here's a little program that shows the problem.  Or just try using EMACS
on a union mounted filesystem.  It complains whenever you try to write a
file which has an underlying shadowed entry because of this.

/* Usual includes */
#include <unistd.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>

main ()
{
    struct stat st;
    if (unlink("foo") < 0 || undelete("foo") < 0 || unlink("foo") < 0) {
	fprintf (stderr,
"Run this program on a union mounted filesystem where the lower\n\
layer has a file named foo\n");
	exit(1);
    }
    printf ("\"foo\" is now whited out; a stat should fail\n");
    assert (stat ("foo", &st) < 0 && errno == ENOENT);
    printf ("It did.  But now the cache is bad; it will say that \"foo\"\n\
doesn't exist in the upper layer, instead of being a whiteout.\n\
So fstat will now succeed: returning the info from the lower layer!\n");
    assert (stat ("foo", &st) == 0);
    printf ("As I said.  This is wrong!\n");
    exit(0);
}

>Fix:

Here's a "diff -u" of my fix.  It just makes "cache_enter" refuse to cache
whiteouts.  Alternatively, you could:

	1 -- Modify the UFS code so that it doesn't try to cache
	whiteouts; this should work because I believe that UFS is the
	only FS that currently supports whiteouts.

	2 -- Modify the VFS cacheing to record the fact that a cached
	entry is a whiteout, and return the appropriate flag when it is
	looked up.  This is the "right" way to do it; but the
	performance hit from failing to cache whiteouts isn't enough to
	worry about it.

--- usr/src/sys/kern/vfs_cache.c.orig	Thu Dec 15 04:20:56 1994
+++ usr/src/sys/kern/vfs_cache.c	Wed Mar  8 13:24:25 1995
@@ -150,6 +150,11 @@
 #endif
 	if (!doingcache)
 		return;
+
+	/* XXX Don't currently have a flag to allow cacheing of whiteouts */
+	if (cnp->cn_flags&ISWHITEOUT)
+		return;
+
 	/*
 	 * Free the cache slot at head of lru chain.
 	 */
>Audit-Trail:
>Unformatted: