[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Bug#190732: [PATCH] hurd/libdiskfs/dir-renamed.c
From: |
Ognyan Kulev |
Subject: |
Bug#190732: [PATCH] hurd/libdiskfs/dir-renamed.c |
Date: |
Wed, 11 Jun 2003 18:48:18 +0300 |
User-agent: |
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.3.1) Gecko/20030527 Debian/1.3.1-2 |
Hi,
I made a somewhat reworked version of lbidiskfs/dir-renamed.c. This is
the second attached patch. As you may guess, I prefer it over the other
one.
But there is a much smaller patch that fixes the bug too. It is the
first attached patch.
Regards
--
Ognyan Kulev <ogi@fmi.uni-sofia.bg>, "\"Programmer\""
7D9F 66E6 68B7 A62B 0FCF EB04 80BF 3A8C A252 9782
2003-06-11 Ognyan Kulev <ogi@fmi.uni-sofia.bg>
* dir-renamed.c (diskfs_rename_dir): Fixed assertion. Check
permissions to remove FROMNAME before any modification could take
place.
--- hurd/libdiskfs/dir-renamed.c.~1.22.~ 2001-10-12 05:49:17.000000000
+0300
+++ hurd/libdiskfs/dir-renamed.c 2003-06-11 18:46:21.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
@@ -106,8 +106,15 @@
return 0;
}
- /* Now we can safely lock fnp */
- mutex_lock (&fnp->lock);
+ /* Check permissions to remove FROMNAME 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;
if (tnp)
{
@@ -199,8 +206,9 @@
ds = buf;
mutex_unlock (&fnp->lock);
err = diskfs_lookup (fdp, fromname, REMOVE, &tmpnp, ds, fromcred);
- assert (tmpnp == fnp);
- diskfs_nrele (tmpnp);
+ assert (!tmpnp || tmpnp == fnp);
+ if (tmpnp)
+ diskfs_nrele (tmpnp);
if (err)
goto out;
2003-06-11 Ognyan Kulev <ogi@fmi.uni-sofia.bg>
* dir-renamed.c (diskfs_rename_dir): Reorder the statements so
that the whole function behave as atomic as possible when errors
occur.
(checkpath): Removed redundant statement.
--- hurd/libdiskfs/dir-renamed.c.~1.22.~ 2001-10-12 05:49:17.000000000
+0300
+++ hurd/libdiskfs/dir-renamed.c 2003-06-11 21:17:25.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
@@ -32,9 +32,8 @@ checkpath(struct node *source,
error_t err;
struct node *np;
- np = target;
for (np = target, err = 0;
- /* nothing */;
+ np != diskfs_root_node;
/* This special lookup does a diskfs_nput on its first argument
when it succeeds. */
err = diskfs_lookup (np, "..", LOOKUP | SPEC_DOTDOT, &np, 0, cred))
@@ -50,13 +49,11 @@ checkpath(struct node *source,
diskfs_nput (np);
return EINVAL;
}
-
- if (np == diskfs_root_node)
- {
- diskfs_nput (np);
- return 0;
- }
}
+
+ /* We've reached the root node. */
+ diskfs_nput (np);
+ return 0;
}
/* Rename directory node FNP (whose parent is FDP, and which has name
@@ -72,45 +69,43 @@ diskfs_rename_dir (struct node *fdp, str
struct protid *fromcred, struct protid *tocred)
{
error_t err;
- struct node *tnp, *tmpnp;
- void *buf = alloca (diskfs_dirstat_size);
- struct dirstat *ds;
- struct dirstat *tmpds;
+ struct node *tnp, *fn_tmp, *fnddp;
+ struct dirstat *tn_ds = 0, *fndd_ds = 0, *fn_ds = 0;
+
+ /* 1: Prepare for modifications. Various checks are performed so
+ that as many errors as possible are catched before any change in
+ disk nodes. */
+ /* Is TDP child of FNP? */
mutex_lock (&tdp->lock);
diskfs_nref (tdp); /* reference and lock will get consumed by
checkpath */
err = checkpath (fnp, tdp, tocred);
-
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;
- err = diskfs_lookup (tdp, toname, RENAME, &tnp, ds, tocred);
+ /* Get TONAME's node in TNP and its dirstat in TN_DS. */
+ tn_ds = alloca (diskfs_dirstat_size);
+ err = diskfs_lookup (tdp, toname, RENAME, &tnp, tn_ds, tocred);
assert (err != EAGAIN); /* <-> assert (TONAME != "..") */
if (tnp == fnp)
+ /* Source and target are the same node. */
{
- diskfs_drop_dirstat (tdp, ds);
- diskfs_nput (tnp);
- mutex_unlock (&tdp->lock);
- if (fdp != tdp)
- mutex_unlock (&fdp->lock);
- return 0;
+ fnp = 0; /* Don't unlock FNP as it's not locked yet. */
+ err = 0;
+ goto out;
}
- /* Now we can safely lock fnp */
- mutex_lock (&fnp->lock);
-
if (tnp)
{
+ /* If TONAME exists, then TNP must be empty directory. */
if (! S_ISDIR(tnp->dn_stat.st_mode))
err = ENOTDIR;
else if (!diskfs_dirempty (tnp, tocred))
@@ -118,64 +113,89 @@ diskfs_rename_dir (struct node *fdp, str
}
if (err && err != ENOENT)
- goto out;
+ {
+ fnp = 0;
+ goto out;
+ }
+
+ /* Get FROMNAME's dirstat in FN_DS. Lock FNP. */
+ fn_ds = alloca (diskfs_dirstat_size);
+ mutex_unlock (&fnp->lock);
+ err = diskfs_lookup (fdp, fromname, REMOVE, &fn_tmp, fn_ds, fromcred);
+ assert (!fn_tmp || fn_tmp == fnp);
+ if (fn_tmp)
+ diskfs_nrele (fn_tmp);
+ if (err)
+ {
+ fnp = 0;
+ goto out;
+ }
+ diskfs_drop_dirstat (fdp, fn_ds);
+ fn_ds = 0;
- /* 2: Set our .. to point to the new parent */
if (fdp != tdp)
+ /* We'll move across directories. */
{
- if (tdp->dn_stat.st_nlink == diskfs_link_max - 1)
+ /* Get FNP's ".." dirstat in FNDD_DS. */
+ fndd_ds = alloca (diskfs_dirstat_size);
+ err = diskfs_lookup (fnp, "..", RENAME | SPEC_DOTDOT,
+ &fnddp, fndd_ds, fromcred);
+ assert (err != ENOENT);
+ if (err)
+ goto out;
+ assert (fnddp == fdp);
+
+ if (tdp->dn_stat.st_nlink == diskfs_link_max - 1
+ || fnp->dn_stat.st_nlink == diskfs_link_max - 1)
+ /* TDP can't afford another link (by FNP) to it,
+ or FNP can't afford another link (by TDP) to it. */
{
err = EMLINK;
- return EMLINK;
+ goto out;
}
+ }
+
+ /* 2: Modify disk nodes to match the new state. */
+
+ if (fdp != tdp)
+ /* Set FNP's .. to point to the new parent TDP. */
+ {
+ /* TDP has new child (FNP). */
tdp->dn_stat.st_nlink++;
tdp->dn_set_ctime = 1;
if (diskfs_synchronous)
diskfs_node_update (tdp, 1);
- tmpds = alloca (diskfs_dirstat_size);
- err = diskfs_lookup (fnp, "..", RENAME | SPEC_DOTDOT,
- &tmpnp, tmpds, fromcred);
- assert (err != ENOENT);
- if (err)
- {
- diskfs_drop_dirstat (fnp, tmpds);
- goto out;
- }
- assert (tmpnp == fdp);
-
- err = diskfs_dirrewrite (fnp, fdp, tdp, "..", tmpds);
+ /* FNP has new parent (TDP). */
+ err = diskfs_dirrewrite (fnp, fdp, tdp, "..", fndd_ds);
+ fndd_ds = 0;
if (diskfs_synchronous)
diskfs_file_update (fnp, 1);
- if (err)
- goto out;
+ /* FNP is no longer child of FDP. */
fdp->dn_stat.st_nlink--;
fdp->dn_set_ctime = 1;
if (diskfs_synchronous)
diskfs_node_update (fdp, 1);
+
+ if (err)
+ goto out;
}
+ /* Increment the link count on the node being moved and rewrite
+ TDP. */
- /* 3: 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;
- }
+ /* Add FNP to TDP. */
+ /* XXX ogi: Why is this needed when below st_nlink is decremented? */
fnp->dn_stat.st_nlink++;
fnp->dn_set_ctime = 1;
diskfs_node_update (fnp, 1);
if (tnp)
{
- err = diskfs_dirrewrite (tdp, tnp, fnp, toname, ds);
- ds = 0;
+ /* Replace TNP with FNP. */
+ err = diskfs_dirrewrite (tdp, tnp, fnp, toname, tn_ds);
+ tn_ds = 0;
if (!err)
{
tnp->dn_stat.st_nlink--;
@@ -187,7 +207,9 @@ diskfs_rename_dir (struct node *fdp, str
}
else
{
- err = diskfs_direnter (tdp, toname, fnp, ds, tocred);
+ /* FNP is new child of TDP. */
+ err = diskfs_direnter (tdp, toname, fnp, tn_ds, tocred);
+ tn_ds = 0;
if (diskfs_synchronous)
diskfs_file_update (tdp, 1);
}
@@ -195,17 +217,25 @@ diskfs_rename_dir (struct node *fdp, str
if (err)
goto out;
- /* 4: Remove the entry in fdp. */
- ds = buf;
+ /* Get FROMNAME's dirstat in FN_DS. XXX: This is the same code as
+ in the preparation. The reason is that the information taken
+ before is unreliable but it serves to check permissions. */
+ fn_ds = alloca (diskfs_dirstat_size);
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, &fn_tmp, fn_ds, fromcred);
+ assert (!fn_tmp || fn_tmp == fnp);
+ if (fn_tmp)
+ diskfs_nrele (fn_tmp);
if (err)
- goto out;
+ {
+ fnp = 0;
+ goto out;
+ }
- diskfs_dirremove (fdp, fnp, fromname, ds);
- ds = 0;
+ /* Remove FNP from FDP. */
+ err = diskfs_dirremove (fdp, fnp, fromname, fn_ds);
+ assert (!err);
+ fn_ds = 0;
fnp->dn_stat.st_nlink--;
fnp->dn_set_ctime = 1;
if (diskfs_synchronous)
@@ -214,6 +244,8 @@ diskfs_rename_dir (struct node *fdp, str
diskfs_node_update (fnp, 1);
}
+ /* 3: Cleanups when all is over or error has occurred. */
+
out:
if (tdp)
mutex_unlock (&tdp->lock);
@@ -223,7 +255,11 @@ diskfs_rename_dir (struct node *fdp, str
mutex_unlock (&fdp->lock);
if (fnp)
mutex_unlock (&fnp->lock);
- if (ds)
- diskfs_drop_dirstat (tdp, ds);
+ if (tn_ds)
+ diskfs_drop_dirstat (tdp, tn_ds);
+ if (fn_ds)
+ diskfs_drop_dirstat (tdp, fn_ds);
+ if (fndd_ds)
+ diskfs_drop_dirstat (tdp, fndd_ds);
return err;
}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- Bug#190732: [PATCH] hurd/libdiskfs/dir-renamed.c,
Ognyan Kulev <=