diff options
Diffstat (limited to 'libdiskfs/dir-renamed.c')
-rw-r--r-- | libdiskfs/dir-renamed.c | 62 |
1 files changed, 35 insertions, 27 deletions
diff --git a/libdiskfs/dir-renamed.c b/libdiskfs/dir-renamed.c index 6ef96ee3..79381b2c 100644 --- a/libdiskfs/dir-renamed.c +++ b/libdiskfs/dir-renamed.c @@ -1,5 +1,5 @@ -/* - Copyright (C) 1994, 1995, 1996 Free Software Foundation +/* + Copyright (C) 1994,95,96,97,98,99,2001,2003 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 @@ -31,8 +31,7 @@ checkpath(struct node *source, { error_t err; struct node *np; - - np = target; + for (np = target, err = 0; /* nothing */; /* This special lookup does a diskfs_nput on its first argument @@ -44,20 +43,20 @@ checkpath(struct node *source, diskfs_nput (np); return err; } - + if (np == source) { diskfs_nput (np); return EINVAL; } - + if (np == diskfs_root_node) { diskfs_nput (np); return 0; } } -} +} /* Rename directory node FNP (whose parent is FDP, and which has name FROMNAME in that directory) to have name TONAME inside directory @@ -67,9 +66,9 @@ checkpath(struct node *source, routine. FROMCRED and TOCRED are the users responsible for FDP/FNP and TDP respectively. */ error_t -diskfs_rename_dir (struct node *fdp, struct node *fnp, char *fromname, - struct node *tdp, char *toname, struct protid *fromcred, - struct protid *tocred) +diskfs_rename_dir (struct node *fdp, struct node *fnp, const char *fromname, + struct node *tdp, const char *toname, + struct protid *fromcred, struct protid *tocred) { error_t err; struct node *tnp, *tmpnp; @@ -81,32 +80,40 @@ diskfs_rename_dir (struct node *fdp, struct node *fnp, char *fromname, 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). */ 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); - + assert (err != EAGAIN); /* <-> assert (TONAME != "..") */ + if (tnp == fnp) { diskfs_drop_dirstat (tdp, ds); - diskfs_nrele (tnp); + diskfs_nput (tnp); mutex_unlock (&tdp->lock); if (fdp != tdp) mutex_unlock (&fdp->lock); 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) { @@ -114,7 +121,7 @@ diskfs_rename_dir (struct node *fdp, struct node *fnp, char *fromname, err = ENOTDIR; else if (!diskfs_dirempty (tnp, tocred)) err = ENOTEMPTY; - } + } if (err && err != ENOENT) goto out; @@ -131,24 +138,24 @@ diskfs_rename_dir (struct node *fdp, struct node *fnp, char *fromname, 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, + err = diskfs_lookup (fnp, "..", RENAME | SPEC_DOTDOT, &tmpnp, tmpds, fromcred); assert (err != ENOENT); - assert (tmpnp == fdp); if (err) { diskfs_drop_dirstat (fnp, tmpds); goto out; } + assert (tmpnp == fdp); err = diskfs_dirrewrite (fnp, fdp, tdp, "..", tmpds); if (diskfs_synchronous) diskfs_file_update (fnp, 1); if (err) goto out; - + fdp->dn_stat.st_nlink--; fdp->dn_set_ctime = 1; if (diskfs_synchronous) @@ -170,7 +177,7 @@ diskfs_rename_dir (struct node *fdp, struct node *fnp, char *fromname, 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); @@ -198,11 +205,12 @@ diskfs_rename_dir (struct node *fdp, struct node *fnp, char *fromname, 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; - + diskfs_dirremove (fdp, fnp, fromname, ds); ds = 0; fnp->dn_stat.st_nlink--; |