File remove problem

Bruce Evans brde at optusnet.com.au
Sun Dec 2 00:35:41 PST 2007


On Sun, 2 Dec 2007, Kostik Belousov wrote:

> On Sat, Dec 01, 2007 at 02:07:50PM -0800, Don Lewis wrote:
>> On  1 Dec, Bruce Evans wrote:
>>> On Sat, 1 Dec 2007, Kostik Belousov wrote:
>>
>>>> +static int
>>>> +ffs_isronly(struct ufsmount *ump)
>>>> +{
>>>> +	struct fs *fs = ump->um_fs;
>>>> +
>>>> +	return (fs->fs_ronly);
>>>> +}
>>>> +
>>>
>>> Could be ump->um_fs->fs_ronly.
>>
>> That's the change that I would have made.  A #include for <ufs/ffs/fs.h>
>> would have to be added, which some might argue would be a layering
>> violation.  I'd prefer to avoid the extra indirection.
>
> I would argue that the ufs already knows too much about the ffs. But,
> this seems to be the first explicit reference to the ffs from the ufs
> code. With your approval, see below.

It's more like the fourth:
- ufs_itimes() is a layering violation.  However, with both ffs and ufs
   needing to set timestamps (for ffs, only in ffs_update()), and with
   both ffs and ufs both needing to set IN_* all over the place, it isn't
   clear which layer timstamps belong in.
- ufs_vnops.c now includes ffs_extern.h for some reason (5.2 didn't).
- ufs_gjournal.c includes both ffs_extern.h and fs.h.  It uses ip->i_fs
   a lot to access the superblock in ufs_gjournal_modref().

> diff --git a/sys/ufs/ufs/ufs_inode.c b/sys/ufs/ufs/ufs_inode.c
> index 448f436..22e29e9 100644
> --- a/sys/ufs/ufs/ufs_inode.c
> +++ b/sys/ufs/ufs/ufs_inode.c
> @@ -60,6 +60,7 @@ __FBSDID("$FreeBSD: src/sys/ufs/ufs/ufs_inode.c,v 1.69 2007/06/22 13:22:37 kib E
> #ifdef UFS_GJOURNAL
> #include <ufs/ufs/gjournal.h>
> #endif
> +#include <ufs/ffs/fs.h>
>
> /*
>  * Last reference to an inode.  If necessary, write or delete it.

ufs/ffs includes are conventionally separated from ufs/ufs includes by a
blank line.  About 2/3 of the files in ufs/ffs follow this convention.

> @@ -90,8 +91,7 @@ ufs_inactive(ap)
> 	ufs_gjournal_close(vp);
> #endif
> 	if ((ip->i_effnlink == 0 && DOINGSOFTDEP(vp)) ||
> -	    (ip->i_nlink <= 0 &&
> -	     (vp->v_mount->mnt_flag & MNT_RDONLY) == 0)) {
> +	    (ip->i_nlink <= 0 && !VFSTOUFS(mp)->um_fs->fs_ronly)) {
> 	loop:
> 		if (vn_start_secondary_write(vp, &mp, V_NOWAIT) != 0) {
> 			/* Cannot delete file while file system is suspended */
> @@ -121,7 +121,7 @@ ufs_inactive(ap)
> 	}
> 	if (ip->i_effnlink == 0 && DOINGSOFTDEP(vp))
> 		softdep_releasefile(ip);
> -	if (ip->i_nlink <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
> +	if (ip->i_nlink <= 0 && !VFSTOUFS(mp)->um_fs->fs_ronly) {
> #ifdef QUOTA
> 		if (!getinoquota(ip))
> 			(void)chkiq(ip, -1, NOCRED, FORCE);
>

Should be ip->i_fs->fs_ronly.

The locking for fs_ronly is unclear.  It seems to be locked mainly by
vn_start_write(), and that enough for everything except probably access
time changes.

I've now tested the following similar change in ufs_itimes() after
removing all other related fixes.

% Index: ufs_vnops.c
% ===================================================================
% RCS file: /home/ncvs/src/sys/ufs/ufs/ufs_vnops.c,v
% retrieving revision 1.293
% diff -u -2 -r1.293 ufs_vnops.c
% --- ufs_vnops.c	8 Nov 2007 17:21:51 -0000	1.293
% +++ ufs_vnops.c	2 Dec 2007 04:56:58 -0000
% @@ -89,4 +89,5 @@
%  #endif
% 
% +#include <ufs/ffs/fs.h>
%  #include <ufs/ffs/ffs_extern.h>
% 
% @@ -137,8 +138,38 @@
% 
%  	ip = VTOI(vp);
% +	/*
% +	 * MNT_RDONLY can barely be trusted here.  Full r/o mode is indicated
% +	 * by fs_ronly, and the MNT_RDONLY setting [should] differ from the
% +	 * fs_ronly setting only during transition from r/w mode to r/o mode.
% +	 * We set IN_ACCESS even in full r/o mode, so we must discard it
% +	 * unconditionally here.  During the transition, we must convert
% +	 * IN_CHANGE | IN_UPDATE to IN_MODIFIED, since some callers neglect
% +	 * to set IN_MODIFIED.  We also set the timestamps indicated by
% +	 * IN_CHANGE | IN_UPDATE normally during the transition, since the
% +	 * update marks may have been set correctly before the transition and
% +	 * not yet converted into timestamps.  Callers that set IN_CHANGE |
% +	 * IN_UPDATE during the transition are buggy, since userland writes
% +	 * are supposed to be denied (by MNT_RDONLY checks) during the
% +	 * transition, while kernel writes should should only be for syncs
% +	 * and syncs should not touch timestamps except to convert old
% +	 * update marks to timestamps.  Callers that set any update mark or
% +	 * modification flag except IN_ACCESS while in full r/o mode are
% +	 * broken; we will panic for them later.
% +	 */
%  	if ((vp->v_mount->mnt_flag & MNT_RDONLY) != 0)
% -		goto out;
% +		ip->i_flag &= ~IN_ACCESS;
%  	if ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_UPDATE)) == 0)
%  		return;
% +	if (ip->i_fs->fs_ronly) {	/* XXX locking? */
% +		vprint("ufs_itimes_locked: r/o mod", vp);
% +		/*
% +		 * Should panic here, or return and let ffs_update() panic.
% +		 * The fs_ronly check in ffs_update() is now almost redundant
% +		 * and should not succeed, so it should be replaced by a
% +		 * panic.  It detects more invariants failures than we detect
% +		 * here.
% +		 */
% +		goto out;
% +	}
% 
%  	if ((vp->v_type == VBLK || vp->v_type == VCHR) && !DOINGSOFTDEP(vp))

The comments in this are too verbose.

This seems to work for "rm a; mount -u -o ro /f" and "mv a b; mount ...",
but since this is without your change to ufs_inactive(), I'm now surprised
that it works.  I think it works for the simple test cases as follows:

- there are no unlinked open files, so ufs_inactive() should have already
   set up all the needed i/o, and the sync for the mount-update should
   finish that i/o.

- however, in ufs_inactive() seems to be called as part of the sync, and
   in ~5.2 where it doesn't do any read-only checks, it seems to call
   ffs_truncate().  This truncate should be null (in the simple test
   cases), but it seems to have the side effect of generating more i/o
   (I hope just to convert bogus settings of IN_CHANGE | IN_UPDATE into
   dinode changes).  Then other bugs cause an inconsistent fs if MNT_RDONLY
   is set.

   BTW, the other bugs don't affect plain 5.2, since it still has
   rev.1.182 of ufs_vnops.c to convert the bogus settings of IN_CHANGE |
   IN_UPDATE into IN_MODIFIED.  I was "lucky" to see almost the same
   bugs in ~5.2 as in -current because I have debugging code in
   ffs_update() instead of rev.1.182 in ufs_vnops.c, but the debugging
   code showed too many apparently-harmless problems so it was turned
   off.

In cases involving unlinked open files, the truncation has to be delayed
until the sync.  Things seem to work reasonably:  If a file on the fs
is open for read, then mount-update from rw to ro is allowed unless the
file is unlinked; if the file is unlinked then there is an EBUSY error
unless MNT_FORCE is used, but if MNT_FORCE is used, then the mount-update
must be allowed to complete and this involves truncating and otherwise
completing the removal of unlinked open files.  In -current, your patch
should make this work again, and with only my patch above the
"update error: blocks 32: files 1" is back because ufs_inactive() doesn't
do the truncation.

I don't understand how WRITECLOSE inter-operates with this -- mount-update
always sets it but there is still an EBUSY error unless MNT_FORCE is
used, while MNT_FORCE should kill all opens.

Bruce


More information about the freebsd-fs mailing list