On Tue, Jul 15, 2003 at 05:43:24AM -0500, Marcus Brinkmann wrote: > 1. What errors do you still expect from the second time diskfs_lookup is > invoked with REMOVE? Is there any error that is allowed at that time? > If there is, the change is correct.
Before the second diskfs_lookup, mutex_unlock is called. This leaves a little time when someone could remove the node. > 2. Is it possible, and according to diskfs.h it should be, to just > call dir_lookup with REMOVE only once, at the point you introduced it > in your patch, and store the dirstat until it is needed for the actual > disks_dirremove call. This saves one lookup call. Can you try such a > change and look into the code if it is feasible? While trying to do that now, I recalled why this cannot be done. If diskfs_lookup is called only once in the beginning, the assertion in ext2fs/dir.c:716 is triggered with the following commands: $ mkdir x $ mv x y $ mv y x It seems that dirstat of ext2fs can break when there are other activities in the directory. It's possible that ext2fs is wrong, but I didn't look further. I send a revisited patch that I beleive is easier to read, and it fixes some tiny bugs too. Regards -- Ognyan Kulev <[EMAIL PROTECTED]>
--- libdiskfs/dir-renamed.c.orig 2003-07-15 23:44:32.000000000 +0300 +++ libdiskfs/dir-renamed.c 2003-07-16 00:52:38.000000000 +0300 @@ -1,5 +1,5 @@ /* - Copyright (C) 1994,95,96,97,98,99,2001 Free Software Foundation, Inc. + Copyright (C) 1994,95,96,97,98,99,2001,03 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -73,9 +73,7 @@ diskfs_rename_dir (struct node *fdp, str { error_t err; struct node *tnp, *tmpnp; - void *buf = alloca (diskfs_dirstat_size); - struct dirstat *ds; - struct dirstat *tmpds; + struct dirstat *ds, *tmpds; mutex_lock (&tdp->lock); diskfs_nref (tdp); /* reference and lock will get consumed by @@ -85,48 +83,51 @@ diskfs_rename_dir (struct node *fdp, str if (err) return err; - /* Now, lock the parent directories. This is legal because tdp is not - a child of fnp (guaranteed by checkpath above). */ + /* Now, lock the parent directories. This is legal because TDP is not + a child of FNP (guaranteed by checkpath above). */ mutex_lock (&fdp->lock); if (fdp != tdp) mutex_lock (&tdp->lock); /* 1: Lookup target; if it exists, make sure it's an empty directory. */ - ds = buf; + ds = alloca (diskfs_dirstat_size); err = diskfs_lookup (tdp, toname, RENAME, &tnp, ds, tocred); assert (err != EAGAIN); /* <-> assert (TONAME != "..") */ - - if (tnp == fnp) + if ((tnp == fnp) || (err && err != ENOENT)) { - diskfs_drop_dirstat (tdp, ds); - diskfs_nput (tnp); - mutex_unlock (&tdp->lock); - if (fdp != tdp) - mutex_unlock (&fdp->lock); - return 0; + if (tnp == fnp) + err = 0; /* Renaming node to itself. */ + fnp = 0; /* Don't unlock the unlocked FNP. */ + goto out; } - - /* Now we can safely lock fnp */ - mutex_lock (&fnp->lock); - if (tnp) { + assert (!err); if (! S_ISDIR(tnp->dn_stat.st_mode)) err = ENOTDIR; else if (!diskfs_dirempty (tnp, tocred)) err = ENOTEMPTY; + if (err) + goto out; } - if (err && err != ENOENT) + /* 2: Check permissions of the node being moved, and lock FNP. */ + tmpds = alloca (diskfs_dirstat_size); + err = diskfs_lookup (fdp, fromname, REMOVE, &tmpnp, tmpds, fromcred); + assert (!tmpnp || tmpnp == fnp); + if (tmpnp) + diskfs_nrele (tmpnp); + diskfs_drop_dirstat (fdp, tmpds); + if (err) goto out; - /* 2: Set our .. to point to the new parent */ + /* 3: Set our .. to point to the new parent */ if (fdp != tdp) { if (tdp->dn_stat.st_nlink == diskfs_link_max - 1) { err = EMLINK; - return EMLINK; + goto out; } tdp->dn_stat.st_nlink++; tdp->dn_set_ctime = 1; @@ -157,16 +158,12 @@ diskfs_rename_dir (struct node *fdp, str } - /* 3: Increment the link count on the node being moved and rewrite - tdp. */ + /* 4: Increment the link count on the node being moved and rewrite + TDP. */ if (fnp->dn_stat.st_nlink == diskfs_link_max - 1) { - mutex_unlock (&fnp->lock); - diskfs_drop_dirstat (tdp, ds); - mutex_unlock (&tdp->lock); - if (tnp) - diskfs_nput (tnp); - return EMLINK; + err = EMLINK; + goto out; } fnp->dn_stat.st_nlink++; fnp->dn_set_ctime = 1; @@ -188,6 +185,7 @@ diskfs_rename_dir (struct node *fdp, str else { err = diskfs_direnter (tdp, toname, fnp, ds, tocred); + ds = 0; if (diskfs_synchronous) diskfs_file_update (tdp, 1); } @@ -195,17 +193,19 @@ diskfs_rename_dir (struct node *fdp, str if (err) goto out; - /* 4: Remove the entry in fdp. */ - ds = buf; + /* 5: Remove the entry in FDP. */ mutex_unlock (&fnp->lock); - err = diskfs_lookup (fdp, fromname, REMOVE, &tmpnp, ds, fromcred); - assert (tmpnp == fnp); - diskfs_nrele (tmpnp); + err = diskfs_lookup (fdp, fromname, REMOVE, &tmpnp, tmpds, fromcred); + assert (!tmpnp || tmpnp == fnp); + if (tmpnp) + diskfs_nrele (tmpnp); if (err) - goto out; + { + diskfs_drop_dirstat (fdp, tmpds); + goto out; + } - diskfs_dirremove (fdp, fnp, fromname, ds); - ds = 0; + diskfs_dirremove (fdp, fnp, fromname, tmpds); fnp->dn_stat.st_nlink--; fnp->dn_set_ctime = 1; if (diskfs_synchronous)
_______________________________________________ Bug-hurd mailing list [EMAIL PROTECTED] http://mail.gnu.org/mailman/listinfo/bug-hurd