summaryrefslogtreecommitdiff
path: root/libdiskfs/dir-renamed.c
diff options
context:
space:
mode:
authorMichael I. Bushnell <mib@gnu.org>1994-02-02 20:55:21 +0000
committerMichael I. Bushnell <mib@gnu.org>1994-02-02 20:55:21 +0000
commit3ee3f8ccac59014de06a7ee568842b23a44206d0 (patch)
tree749d68e76522be8d0e01c6fec4531ec7c02a10f0 /libdiskfs/dir-renamed.c
parentb5a27b1c53875dbb25c33a735b9bc22ac044ebba (diff)
Initial revision
Diffstat (limited to 'libdiskfs/dir-renamed.c')
-rw-r--r--libdiskfs/dir-renamed.c169
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;
+}