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: