diff options
author | Michael I. Bushnell <mib@gnu.org> | 1994-02-02 20:55:21 +0000 |
---|---|---|
committer | Michael I. Bushnell <mib@gnu.org> | 1994-02-02 20:55:21 +0000 |
commit | 3ee3f8ccac59014de06a7ee568842b23a44206d0 (patch) | |
tree | 749d68e76522be8d0e01c6fec4531ec7c02a10f0 /libdiskfs/dir-renamed.c | |
parent | b5a27b1c53875dbb25c33a735b9bc22ac044ebba (diff) |
Initial revision
Diffstat (limited to 'libdiskfs/dir-renamed.c')
-rw-r--r-- | libdiskfs/dir-renamed.c | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/libdiskfs/dir-renamed.c b/libdiskfs/dir-renamed.c new file mode 100644 index 00000000..447c45a4 --- /dev/null +++ b/libdiskfs/dir-renamed.c @@ -0,0 +1,169 @@ +/* + Copyright (C) 1994 Free Software Foundation + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "priv.h" + + +/* Rename directory node FNP (whose parent is FDP, and which has name + FROMNAME in that directory) to have name TONAME inside directory + TDP. None of these nodes are locked, and none should be locked + upon return. This routine is serialized, so it doesn't have to be + reentrant. Directories will never be renamed except by this + 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) +{ + error_t err; + struct node *tnp, *tmpnp; + void *buf = alloca (diskfs_dirstat_size); + struct diskfs_dirstat *ds; + struct diskfs_dirstat *tmpds; + + 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). */ + mutex_lock (&fdp->lock); + if (fdp != tdp) + mutex_lock (&tdp->lock); + + /* 1: Lookup target; if it exists, make sure it's an empty directory. */ + mutex_lock (&tdp->lock); + ds = buf; + err = diskfs_lookup (tdp, toname, RENAME, &tnp, ds, tocred); + + if (tnp == fnp) + { + diskfs_drop_dirstat (ds); + diskfs_nrele (tnp); + mutex_unlock (&tdp->lock); + mutex_unlock (&ftp->lock); + return 0; + } + + /* Now we can safely lock fnp */ + mutex_lock (&fnp->lock); + + if (tnp) + { + if (! S_ISDIR(tnp->dn_stat.st_mode)) + err = ENOTDIR; + else if (!dirempty (tnp, tocred)) + err = ENOTEMPTY; + } + + if (err && err != ENOENT) + goto out; + + /* 2: Set our .. to point to the new parent */ + if (tdp->dn_stat.st_nlink == LINK_MAX - 1) + { + err = EMLINK; + return EMLINK; + } + tdp->dn_stat.st_nlink++; + tdp->dn_set_ctime = 1; + err = diskfs_checkdirmod (fnp, fnp->dn_stat.st_mode, fdp, fromcred); + if (err) + goto out; + + tmpds = alloca (diskfs_dirstat_size); + err = lookup (fnp, "..", RENAME | SPEC_DOTDOT, tmpnp, &tmpds, fromcred); + assert (err != ENOENT); + assert (tmpnp == fdp); + diskfs_nrele (tmpnp); + if (err) + { + diskfs_drop_dirstat (tmpds); + goto out; + } + + err = diskfs_dirrewrite (fnp, tdp, tmpds); + if (err) + goto out; + + fdp->dn_stat.st_nlink--; + fdp->dn_set_ctime = 1; + + + /* 3: Increment the link count on the node being moved and rewrite + tdp. */ + if (fnp->dn_stat.st_nlink == LINK_MAX - 1) + { + mutex_unlock (&fnp->lock); + diskfs_drop_dirstat (ds); + mutex_unlock (&tdp->lock); + if (tnp) + diskfs_nput (tnp); + return EMLINK; + } + fnp->dn_stat.st_nlink++; + fnp->dn_set_ctime = 1; + diskfs_node_update (fnp, 1); + + if (tnp) + { + err = diskfs_dirrewrite (tdp, fnp, ds); + ds = 0; + if (!err) + { + tnp->dn_stat.st_nlink--; + tnp->dn_set_ctime = 1; + } + diskfs_clear_directory (tnp, tdp, tocred); + } + else + err = diskfs_direnter (tdp, toname, fnp, ds, tocred); + + if (err) + goto out; + + /* 4: Remove the entry in fdp. */ + ds = buf; + mutex_unlock (&fnp->lock); + err = diskfs_lookup (fdp, fromname, REMOVE, tmpnp, ds, fromcred); + assert (tmpnp == fnp); + if (err) + goto out; + + dirremove (fdp, ds); + ds = 0; + fnp->dn_stat.st_nlink--; + fnp->dn_set_ctime = 1; + + out: + if (tdp) + mutex_unlock (&tdp->lock); + if (tnp) + diskfs_nput (tnp); + if (fdp) + mutex_unlock (&fdp->lock); + if (fnp) + mutex_unlock (&fnp->lock); + if (ds) + diskfs_drop_dirstat (ds); + return err; +} |