Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 30 Nov 2007 12:23:05 +1100 (EST)
From:      Bruce Evans <brde@optusnet.com.au>
To:        David Cecil <david.cecil@nokia.com>
Cc:        freebsd-fs@freebsd.org
Subject:   Re: File remove problem
Message-ID:  <20071130112043.H7217@besplex.bde.org>
In-Reply-To: <474F4E46.8030109@nokia.com>
References:  <474F4E46.8030109@nokia.com>

next in thread | previous in thread | raw e-mail | index | archive | help
On Fri, 30 Nov 2007, David Cecil wrote:

> I've been looking into a problem we're seeing on FreeBSD 6.1, though I 
> believe the bug will exist in current, or at least 7.0.
>
> Under certain circumstances, when a file is removed from the filesystem, and 
> the filesystem is remounted read-only immediately afterwards, an error such 
> as the following is displayed:
> g_vfs_done():mirror/gmroots1f[WRITE(offset=1349058560, length=16384)]error = 
> 1
>
> I have determined that the buffer contains an update to the inode for the 
> file that's deleted.  The inode for the directory change appears to be 
> updated correctly.  So what's not making it to disk is the updated file inode 
> with its changed link counts (should now be zero).  So, somehow this inode is 
> being missed during the sync prior to the remount completing.

I think I understand this now.  Try this fix (only the first half):

% Index: ufs/ufs/ufs_lookup.c
% ===================================================================
% RCS file: /home/ncvs/src/sys/ufs/ufs/ufs_lookup.c,v
% retrieving revision 1.70
% diff -u -2 -r1.70 ufs_lookup.c
% --- ufs/ufs/ufs_lookup.c	7 Apr 2004 03:47:20 -0000	1.70
% +++ ufs/ufs/ufs_lookup.c	29 Sep 2007 11:20:03 -0000
% @@ -1053,9 +1054,9 @@
%  			ip->i_nlink--;
%  			DIP(ip, i_nlink) = ip->i_nlink;
% -			ip->i_flag |= IN_CHANGE;
% +			ip->i_flag |= IN_CHANGE | IN_MODIFIED;
%  		}
%  		if (flags & DOWHITEOUT)
%  			error = bwrite(bp);
% -		else if (DOINGASYNC(dvp) && dp->i_count != 0) {
% +		else if (DOINGASYNC(dvp)) {
%  			bdwrite(bp);
%  			error = 0;

IN_MODIFIED should always be set when the inode is changed, but many
places are sloppy and only set IN_CHANGE, relying on IN_MODIFIED being
set later when the update mark IN_CHANGE is converted to an actual
change of the ctime.  Normally this is just an obfuscation, but in the
case of a r/w to a r/o transition, the update mark is discarded in
ufs_itimes().  The related complication that ffs_update() doesn't trust
IN_MODIFIED in the waitfor != 0 case apparently doesn't help.  This
complication is needed (modulo bugs like the one fixed above) only for
soft updates, and is used mainly when ffs_fsync() passes waitfor !=
0.  The r/w to r/o transition should be doing a waitfor != 0 (spelled
MNT_WAIT) sync, so I don't quite understand why the complication doesn't
help.

Note that there are 2 r/o flags, MNT_RDONLY in the (re)mount flags and
fs_ronly in the superblock.  During the transition, MNT_RDONLY is set,
so ufs_itimes() discards IN_CHANGE, while fs_ronly remains clear so
that the final inode update is allowed.  The transition requires even
more delicate handling for IN_ACCESS.

There are many other places in which IN_MODIFIED is not set correctly,
but the above should be enough to fix unlink().

I have used a version of the above for many years but just added back
the IN_CHANGE setting in it to minimise this change.

Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20071130112043.H7217>