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

Reply via email to