From 3ee3f8ccac59014de06a7ee568842b23a44206d0 Mon Sep 17 00:00:00 2001 From: "Michael I. Bushnell" Date: Wed, 2 Feb 1994 20:55:21 +0000 Subject: Initial revision --- libdiskfs/dir-clear.c | 3 - libdiskfs/dir-init.c | 58 +++++++++++++++++ libdiskfs/dir-renamed.c | 169 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+), 3 deletions(-) create mode 100644 libdiskfs/dir-init.c create mode 100644 libdiskfs/dir-renamed.c (limited to 'libdiskfs') diff --git a/libdiskfs/dir-clear.c b/libdiskfs/dir-clear.c index 4b48b38c..e42cd04e 100644 --- a/libdiskfs/dir-clear.c +++ b/libdiskfs/dir-clear.c @@ -17,9 +17,6 @@ #include "priv.h" -/* Clear the `.' and `..' entries from directory DP. Its parent is PDP, - and the user responsible for this is identified by CRED. Both - directories must be locked. */ error_t diskfs_clear_directory (struct node *dp, struct node *pdp, diff --git a/libdiskfs/dir-init.c b/libdiskfs/dir-init.c new file mode 100644 index 00000000..a9e14a7d --- /dev/null +++ b/libdiskfs/dir-init.c @@ -0,0 +1,58 @@ +/* + 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" + +/* Locked node DP is a new directory; add whatever links are necessary + to give it structure; its parent is the (locked) node PDP. + This routine may not call diskfs_lookup on PDP. The new directory + must be clear within the meaning of diskfs_dirempty. */ +error_t +diskfs_init_dir (struct node *dp, struct node *pdp) +{ + struct dirstat *ds = alloca (diskfs_dirstat_size); + struct node *foo; + + /* New links */ + if (pdp->dn_stat.st_nlink == LINK_MAX - 1) + return EMLINK; + + np->dn_stat.st_nlink++; /* for `.' */ + np->dn_set_ctime = 1; + err = diskfs_lookup (np, ".", CREATE, &foo, dirds, cred); + assert (err == ENOENT); + err = diskfs_direnter (np, ".", np, dirds, cred); + if (err) + { + np->dn_stat.st_nlink--; + np->dn_set_ctime = 1; + return err; + } + + pdp->dn_stat.st_nlink++; /* for `..' */ + pdp->dn_set_ctime = 1; + err = diskfs_lookup (np, "..", CREATE, &foo, dirds, cred); + assert (err == ENOENT); + err = diskfs_direnter (np, "..", dir, dirds, cred); + if (err) + { + pdp->dn_stat.st_nlink--; + pdp->dn_set_ctime = 1; + return err; + } + diskfs_node_update (dir, 1); +} 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; +} -- cgit v1.2.3