patch-2.1.106 linux/fs/nfsd/vfs.c
Next file: linux/fs/proc/array.c
Previous file: linux/fs/nfsd/nfsproc.c
Back to the patch index
Back to the overall index
- Lines: 319
- Date:
Sat Jun 13 09:58:14 1998
- Orig file:
v2.1.105/linux/fs/nfsd/vfs.c
- Orig date:
Fri May 8 23:14:53 1998
diff -u --recursive --new-file v2.1.105/linux/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
@@ -31,14 +31,13 @@
#include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h>
+#include <linux/nfsd/nfsfh.h>
#include <linux/quotaops.h>
#if LINUX_VERSION_CODE >= 0x020100
#include <asm/uaccess.h>
#endif
-extern void fh_update(struct svc_fh*);
-
#define NFSDDBG_FACILITY NFSDDBG_FILEOP
/* Open mode for nfsd_open */
@@ -77,6 +76,41 @@
static struct raparms * raparm_cache = 0;
/*
+ * Lock a parent directory following the VFS locking protocol.
+ */
+int
+fh_lock_parent(struct svc_fh *parent_fh, struct dentry *dchild)
+{
+ int nfserr = 0;
+
+ fh_lock(parent_fh);
+ /*
+ * Make sure the parent->child relationship still holds,
+ * and that the child is still hashed.
+ */
+ if (dchild->d_parent != parent_fh->fh_dentry)
+ goto out_not_parent;
+ if (list_empty(&dchild->d_hash))
+ goto out_not_hashed;
+out:
+ return nfserr;
+
+out_not_parent:
+ printk(KERN_WARNING
+ "fh_lock_parent: %s/%s parent changed\n",
+ dchild->d_parent->d_name.name, dchild->d_name.name);
+ goto out_unlock;
+out_not_hashed:
+ printk(KERN_WARNING
+ "fh_lock_parent: %s/%s unhashed\n",
+ dchild->d_parent->d_name.name, dchild->d_name.name);
+out_unlock:
+ nfserr = nfserr_noent;
+ fh_unlock(parent_fh);
+ goto out;
+}
+
+/*
* Deny access to certain file systems
*/
static inline int
@@ -546,8 +580,11 @@
}
/*
- * Create a file (regular, directory, device, fifo).
- * UNIX sockets not yet implemented.
+ * Create a file (regular, directory, device, fifo); UNIX sockets
+ * not yet implemented.
+ * If the response fh has been verified, the parent directory should
+ * already be locked. Note that the parent directory is left locked.
+ *
* N.B. Every call to nfsd_create needs an fh_put for _both_ fhp and resfhp
*/
int
@@ -557,13 +594,12 @@
{
struct dentry *dentry, *dchild;
struct inode *dirp;
+ nfsd_dirop_t opfunc = NULL;
int err;
err = nfserr_perm;
if (!flen)
goto out;
- if (!(iap->ia_valid & ATTR_MODE))
- iap->ia_mode = 0;
err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
if (err)
goto out;
@@ -571,64 +607,74 @@
dentry = fhp->fh_dentry;
dirp = dentry->d_inode;
- /* Get all the sanity checks out of the way before we lock the parent. */
err = nfserr_notdir;
if(!dirp->i_op || !dirp->i_op->lookup)
goto out;
- err = nfserr_perm;
- if (type == S_IFREG) {
- if(!dirp->i_op->create)
- goto out;
- } else if(type == S_IFDIR) {
- if(!dirp->i_op->mkdir)
- goto out;
- } else if((type == S_IFCHR) || (type == S_IFBLK) || (type == S_IFIFO)) {
- if(!dirp->i_op->mknod)
- goto out;
- } else {
- goto out;
- }
-
/*
- * The response filehandle may have been setup already ...
+ * Check whether the response filehandle has been verified yet.
+ * If it has, the parent directory should already be locked.
*/
if (!resfhp->fh_dverified) {
dchild = lookup_dentry(fname, dget(dentry), 0);
err = PTR_ERR(dchild);
- if(IS_ERR(dchild))
+ if (IS_ERR(dchild))
goto out_nfserr;
fh_compose(resfhp, fhp->fh_export, dchild);
- } else
+ /* Lock the parent and check for errors ... */
+ err = fh_lock_parent(fhp, dchild);
+ if (err)
+ goto out;
+ } else {
dchild = resfhp->fh_dentry;
+ if (!fhp->fh_locked)
+ printk(KERN_ERR
+ "nfsd_create: parent %s/%s not locked!\n",
+ dentry->d_parent->d_name.name,
+ dentry->d_name.name);
+ }
/*
* Make sure the child dentry is still negative ...
*/
+ err = nfserr_exist;
if (dchild->d_inode) {
- printk("nfsd_create: dentry %s/%s not negative!\n",
- dentry->d_parent->d_name.name, dentry->d_name.name);
+ printk(KERN_WARNING
+ "nfsd_create: dentry %s/%s not negative!\n",
+ dentry->d_name.name, dchild->d_name.name);
+ goto out;
}
- /* Looks good, lock the directory. */
- fh_lock(fhp);
- DQUOT_INIT(dirp);
+ /*
+ * Get the dir op function pointer.
+ */
+ err = nfserr_perm;
switch (type) {
case S_IFREG:
- err = dirp->i_op->create(dirp, dchild, iap->ia_mode);
+ opfunc = (nfsd_dirop_t) dirp->i_op->create;
break;
case S_IFDIR:
- err = dirp->i_op->mkdir(dirp, dchild, iap->ia_mode);
+ opfunc = (nfsd_dirop_t) dirp->i_op->mkdir;
break;
case S_IFCHR:
case S_IFBLK:
case S_IFIFO:
- err = dirp->i_op->mknod(dirp, dchild, iap->ia_mode, rdev);
+ opfunc = dirp->i_op->mknod;
break;
}
- DQUOT_DROP(dirp);
- fh_unlock(fhp);
+ if (!opfunc)
+ goto out;
+ if (!(iap->ia_valid & ATTR_MODE))
+ iap->ia_mode = 0;
+
+ /*
+ * Call the dir op function to create the object.
+ */
+ DQUOT_INIT(dirp);
+ err = opfunc(dirp, dchild, iap->ia_mode, rdev);
+ DQUOT_DROP(dirp);
if (err < 0)
goto out_nfserr;
+
if (EX_ISSYNC(fhp->fh_export))
write_inode_now(dirp);
@@ -778,21 +824,29 @@
if (IS_ERR(dnew))
goto out_nfserr;
- err = -EEXIST;
+ /*
+ * Lock the parent before checking for existence
+ */
+ err = fh_lock_parent(fhp, dnew);
+ if (err)
+ goto out_compose;
+
+ err = nfserr_exist;
if (!dnew->d_inode) {
- fh_lock(fhp);
DQUOT_INIT(dirp);
err = dirp->i_op->symlink(dirp, dnew, path);
DQUOT_DROP(dirp);
- fh_unlock(fhp);
if (!err) {
if (EX_ISSYNC(fhp->fh_export))
write_inode_now(dirp);
- }
+ } else
+ err = nfserrno(-err);
}
+ fh_unlock(fhp);
+
+ /* Compose the fh so the dentry will be freed ... */
+out_compose:
fh_compose(resfhp, fhp->fh_export, dnew);
- if (err)
- goto out_nfserr;
out:
return err;
@@ -820,6 +874,10 @@
if (err)
goto out;
+ err = nfserr_perm;
+ if (!len)
+ goto out;
+
ddir = ffhp->fh_dentry;
dirp = ddir->d_inode;
@@ -827,44 +885,48 @@
err = PTR_ERR(dnew);
if (IS_ERR(dnew))
goto out_nfserr;
+ /*
+ * Lock the parent before checking for existence
+ */
+ err = fh_lock_parent(ffhp, dnew);
+ if (err)
+ goto out_dput;
- err = -EEXIST;
+ err = nfserr_exist;
if (dnew->d_inode)
- goto dput_and_out;
-
- err = -EPERM;
- if (!len)
- goto dput_and_out;
+ goto out_unlock;
dold = tfhp->fh_dentry;
dest = dold->d_inode;
- err = -EACCES;
+ err = nfserr_acces;
if (nfsd_iscovered(ddir, ffhp->fh_export))
- goto dput_and_out;
+ goto out_unlock;
+ /* FIXME: nxdev for NFSv3 */
if (dirp->i_dev != dest->i_dev)
- goto dput_and_out; /* FIXME: nxdev for NFSv3 */
+ goto out_unlock;
- err = -EPERM;
+ err = nfserr_perm;
if (IS_IMMUTABLE(dest) /* || IS_APPEND(dest) */ )
- goto dput_and_out;
+ goto out_unlock;
if (!dirp->i_op || !dirp->i_op->link)
- goto dput_and_out;
+ goto out_unlock;
- fh_lock(ffhp);
DQUOT_INIT(dirp);
err = dirp->i_op->link(dold, dirp, dnew);
DQUOT_DROP(dirp);
- fh_unlock(ffhp);
+ if (!err) {
+ if (EX_ISSYNC(ffhp->fh_export)) {
+ write_inode_now(dirp);
+ write_inode_now(dest);
+ }
+ } else
+ err = nfserrno(-err);
- if (!err && EX_ISSYNC(ffhp->fh_export)) {
- write_inode_now(dirp);
- write_inode_now(dest);
- }
-dput_and_out:
+out_unlock:
+ fh_unlock(ffhp);
+out_dput:
dput(dnew);
- if (err)
- goto out_nfserr;
out:
return err;
@@ -1005,7 +1067,10 @@
if (IS_ERR(rdentry))
goto out_nfserr;
- fh_lock(fhp);
+ err = fh_lock_parent(fhp, rdentry);
+ if (err)
+ goto out;
+
DQUOT_INIT(dirp);
if (type == S_IFDIR) {
err = -ENOTDIR;
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov