patch-2.3.99-pre2 linux/fs/nfsd/vfs.c
Next file: linux/fs/open.c
Previous file: linux/fs/nfsd/nfsproc.c
Back to the patch index
Back to the overall index
- Lines: 501
- Date:
Fri Mar 17 15:43:32 2000
- Orig file:
v2.3.99-pre1/linux/fs/nfsd/vfs.c
- Orig date:
Fri Mar 10 16:40:48 2000
diff -u --recursive --new-file v2.3.99-pre1/linux/fs/nfsd/vfs.c linux/fs/nfsd/vfs.c
@@ -52,9 +52,6 @@
*/
#define IS_ISMNDLK(i) (S_ISREG((i)->i_mode) && MANDATORY_LOCK(i))
-/* Check for dir entries '.' and '..' */
-#define isdotent(n, l) (l < 3 && n[0] == '.' && (l == 1 || n[1] == '.'))
-
/*
* This is a cache of readahead params that help us choose the proper
* readahead strategy. Initially, we set all readahead parameters to 0
@@ -77,47 +74,6 @@
static struct raparms * raparml = NULL;
static struct raparms * raparm_cache = NULL;
-
-/*
- * We need to do a check-parent every time
- * after we have locked the parent - to verify
- * that the parent is still our parent and
- * that we are still hashed onto it..
- *
- * This is required in case two processes race
- * on removing (or moving) the same entry: the
- * parent lock will serialize them, but the
- * other process will be too late..
- *
- * Note that this nfsd_check_parent is identical
- * the check_parent in linux/fs/namei.c.
- */
-#define nfsd_check_parent(dir, dentry) \
- ((dir) == (dentry)->d_parent && !d_unhashed(dentry))
-
-/*
- * Lock a parent directory following the VFS locking protocol.
- */
-int
-fh_lock_parent(struct svc_fh *parent_fh, struct dentry *dchild)
-{
- fh_lock(parent_fh);
- /*
- * Make sure the parent->child relationship still holds,
- * and that the child is still hashed.
- */
- if (nfsd_check_parent(parent_fh->fh_dentry, dchild))
- return 0;
-
- printk(KERN_WARNING
- "fh_lock_parent: %s/%s parent changed or child unhashed\n",
- dchild->d_parent->d_name.name, dchild->d_name.name);
-
- fh_unlock(parent_fh);
- return nfserr_noent;
-}
-
-
/*
* Look up one component of a pathname.
* N.B. After this call _both_ fhp and resfh need an fh_put
@@ -156,35 +112,57 @@
err = nfserr_acces;
/* Lookup the name, but don't follow links */
- if (strcmp(name,"..")==0 && dparent->d_covers != dparent)
- dchild = dget(dparent);
- else
+ if (strcmp(name, "..")==0) {
+ /* checking mountpoint crossing is very different when stepping up */
+ if (dparent == exp->ex_dentry) {
+ if (!EX_CROSSMNT(exp))
+ dchild = dget(dparent); /* .. == . just like at / */
+ else
+ {
+ struct svc_export *exp2 = NULL;
+ struct dentry *dp;
+ dchild = dparent->d_covers->d_parent;
+ for (dp=dchild;
+ exp2 == NULL && dp->d_covers->d_parent != dp;
+ dp=dp->d_covers->d_parent)
+ exp2 = exp_get(exp->ex_client, dp->d_inode->i_dev, dp->d_inode->i_ino);
+ if (exp2==NULL || dchild->d_sb != exp2->ex_dentry->d_sb) {
+ dchild = dget(dparent);
+ } else {
+ dget(dchild);
+ exp = exp2;
+ }
+ }
+ } else
+ dchild = dget(dparent->d_parent);
+ } else {
dchild = lookup_dentry(name, dget(dparent), 0);
- if (IS_ERR(dchild))
- goto out_nfserr;
- /*
- * check if we have crossed a mount point ...
- */
- if (dchild->d_sb != dparent->d_sb) {
- struct svc_export *exp2 = NULL;
- exp2 = exp_get(rqstp->rq_client,
- dchild->d_inode->i_dev,
- dchild->d_inode->i_ino);
- if (exp2 && EX_CROSSMNT(exp2))
- /* successfully crossed mount point */
- exp = exp2;
- else if (dchild->d_covers->d_sb == dparent->d_sb) {
- /* stay in the original filesystem */
- struct dentry *tdentry = dget(dchild->d_covers);
- dput(dchild);
- dchild = tdentry;
- } else {
- /* This cannot possibly happen */
- printk("nfsd_lookup: %s/%s impossible mount point!\n", dparent->d_name.name, dchild->d_name.name);
- dput(dchild);
- err = nfserr_acces;
- goto out;
+ if (IS_ERR(dchild))
+ goto out_nfserr;
+ /*
+ * check if we have crossed a mount point ...
+ */
+ if (dchild->d_sb != dparent->d_sb) {
+ struct svc_export *exp2 = NULL;
+ exp2 = exp_get(rqstp->rq_client,
+ dchild->d_inode->i_dev,
+ dchild->d_inode->i_ino);
+ if (exp2 && EX_CROSSMNT(exp2))
+ /* successfully crossed mount point */
+ exp = exp2;
+ else if (dchild->d_covers->d_sb == dparent->d_sb) {
+ /* stay in the original filesystem */
+ struct dentry *tdentry = dget(dchild->d_covers);
+ dput(dchild);
+ dchild = tdentry;
+ } else {
+ /* This cannot possibly happen */
+ printk("nfsd_lookup: %s/%s impossible mount point!\n", dparent->d_name.name, dchild->d_name.name);
+ dput(dchild);
+ err = nfserr_acces;
+ goto out;
+ }
}
}
/*
@@ -216,6 +194,7 @@
int imode;
int err;
kernel_cap_t saved_cap = 0;
+ int size_change = 0;
if (iap->ia_valid & (ATTR_ATIME | ATTR_MTIME | ATTR_SIZE))
accmode |= MAY_WRITE;
@@ -305,14 +284,31 @@
saved_cap = current->cap_effective;
cap_clear(current->cap_effective);
}
+#ifdef CONFIG_QUOTA
+ /* DQUOT_TRANSFER needs both ia_uid and ia_gid defined */
+ if (iap->ia_valid & (ATTR_UID|ATTR_GID)) {
+ if (! (iap->ia_valid & ATTR_UID))
+ iap->ia_uid = inode->i_uid;
+ if (! (iap->ia_valid & ATTR_GID))
+ iap->ia_gid = inode->i_gid;
+ iap->ia_valid |= ATTR_UID|ATTR_GID;
+ }
+#endif /* CONFIG_QUOTA */
+
if (iap->ia_valid & ATTR_SIZE) {
fh_lock(fhp);
+ size_change = 1;
+ }
+#ifdef CONFIG_QUOTA
+ if (iap->ia_valid & (ATTR_UID|ATTR_GID))
+ err = DQUOT_TRANSFER(dentry, iap);
+ else
+#endif
err = notify_change(dentry, iap);
+ if (size_change) {
fh_unlock(fhp);
put_write_access(inode);
}
- else
- err = notify_change(dentry, iap);
if (current->fsuid != 0)
current->cap_effective = saved_cap;
if (err)
@@ -647,11 +643,11 @@
uid_t saved_euid;
#endif
- if (!cnt)
- goto out;
err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file);
if (err)
goto out;
+ if (!cnt)
+ goto out_close;
err = nfserr_perm;
if (!file.f_op->write)
goto out_close;
@@ -812,6 +808,9 @@
err = nfserr_perm;
if (!flen)
goto out;
+ err = nfserr_exist;
+ if (isdotent(fname, flen))
+ goto out;
err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
if (err)
@@ -829,14 +828,11 @@
*/
if (!resfhp->fh_dverified) {
/* called from nfsd_proc_mkdir, or possibly nfsd3_proc_create */
- dchild = lookup_dentry(fname, dget(dentry), 0);
+ fh_lock(fhp);
+ dchild = lookup_one(fname, dget(dentry));
err = PTR_ERR(dchild);
if (IS_ERR(dchild))
goto out_nfserr;
- /* Lock the parent and check for errors ... */
- err = fh_lock_parent(fhp, dchild);
- if (err)
- goto out;
err = fh_compose(resfhp, fhp->fh_export, dchild);
if (err)
goto out;
@@ -934,6 +930,9 @@
err = nfserr_perm;
if (!flen)
goto out;
+ err = nfserr_exist;
+ if (isdotent(fname, flen))
+ goto out;
if (!(iap->ia_valid & ATTR_MODE))
iap->ia_mode = 0;
err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
@@ -948,21 +947,16 @@
err = nfserr_notdir;
if(!dirp->i_op || !dirp->i_op->lookup)
goto out;
+ fh_lock(fhp);
/*
* Compose the response file handle.
*/
- dchild = lookup_dentry(fname, dget(dentry), 0);
+ dchild = lookup_one(fname, dget(dentry));
err = PTR_ERR(dchild);
if(IS_ERR(dchild))
goto out_nfserr;
- /*
- * We must lock the directory before we check for the inode.
- */
- err = fh_lock_parent(fhp, dchild);
- if (err)
- goto out;
err = fh_compose(resfhp, fhp->fh_export, dchild);
if (err)
goto out;
@@ -1096,24 +1090,20 @@
err = nfserr_noent;
if (!flen || !plen)
goto out;
+ err = nfserr_exist;
+ if (isdotent(fname, flen))
+ goto out;
err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE);
if (err)
goto out;
+ fh_lock(fhp);
dentry = fhp->fh_dentry;
-
- dnew = lookup_dentry(fname, dget(dentry), 0);
+ dnew = lookup_one(fname, dget(dentry));
err = PTR_ERR(dnew);
if (IS_ERR(dnew))
goto out_nfserr;
- /*
- * Lock the parent before checking for existence
- */
- err = fh_lock_parent(fhp, dnew);
- if (err)
- goto out_compose;
-
err = vfs_symlink(dentry->d_inode, dnew, path);
if (!err) {
if (EX_ISSYNC(fhp->fh_export))
@@ -1134,7 +1124,6 @@
fh_unlock(fhp);
/* Compose the fh so the dentry will be freed ... */
-out_compose:
cerr = fh_compose(resfhp, fhp->fh_export, dnew);
if (err==0) err = cerr;
out:
@@ -1167,20 +1156,18 @@
err = nfserr_perm;
if (!len)
goto out;
+ err = nfserr_exist;
+ if (isdotent(fname, len))
+ goto out;
+ fh_lock(ffhp);
ddir = ffhp->fh_dentry;
dirp = ddir->d_inode;
- dnew = lookup_dentry(fname, dget(ddir), 0);
+ dnew = lookup_one(fname, dget(ddir));
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;
dold = tfhp->fh_dentry;
dest = dold->d_inode;
@@ -1199,7 +1186,6 @@
}
fh_unlock(ffhp);
-out_dput:
dput(dnew);
out:
return err;
@@ -1210,29 +1196,6 @@
}
/*
- * This follows the model of double_lock() in the VFS.
- */
-static inline void nfsd_double_down(struct semaphore *s1, struct semaphore *s2)
-{
- if (s1 != s2) {
- if ((unsigned long) s1 < (unsigned long) s2) {
- struct semaphore *tmp = s1;
- s1 = s2;
- s2 = tmp;
- }
- down(s1);
- }
- down(s2);
-}
-
-static inline void nfsd_double_up(struct semaphore *s1, struct semaphore *s2)
-{
- up(s1);
- if (s1 != s2)
- up(s2);
-}
-
-/*
* Rename a file
* N.B. After this call _both_ ffhp and tfhp need an fh_put
*/
@@ -1261,15 +1224,12 @@
if (fdir->i_dev != tdir->i_dev)
goto out;
- /* N.B. We shouldn't need this ... dentry layer handles it */
err = nfserr_perm;
- if (!flen || (fname[0] == '.' &&
- (flen == 1 || (flen == 2 && fname[1] == '.'))) ||
- !tlen || (tname[0] == '.' &&
- (tlen == 1 || (tlen == 2 && tname[1] == '.'))))
+ if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen))
goto out;
- odentry = lookup_dentry(fname, dget(fdentry), 0);
+ double_down(&tdir->i_sem, &fdir->i_sem);
+ odentry = lookup_one(fname, dget(fdentry));
err = PTR_ERR(odentry);
if (IS_ERR(odentry))
goto out_nfserr;
@@ -1278,16 +1238,11 @@
if (!odentry->d_inode)
goto out_dput_old;
- ndentry = lookup_dentry(tname, dget(tdentry), 0);
+ ndentry = lookup_one(tname, dget(tdentry));
err = PTR_ERR(ndentry);
if (IS_ERR(ndentry))
goto out_dput_old;
- /*
- * Lock the parent directories.
- */
- nfsd_double_down(&tdir->i_sem, &fdir->i_sem);
-
#ifdef CONFIG_NFSD_V3
/* Fill in the pre-op attr for the wcc data for both
* tdir and fdir
@@ -1296,19 +1251,11 @@
fill_pre_wcc(tfhp);
#endif /* CONFIG_NFSD_V3 */
- err = -ENOENT;
- /* GAM3 check for parent changes after locking. */
- if (nfsd_check_parent(fdentry, odentry) &&
- nfsd_check_parent(tdentry, ndentry)) {
-
- err = vfs_rename(fdir, odentry, tdir, ndentry);
- if (!err && EX_ISSYNC(tfhp->fh_export)) {
- nfsd_sync_dir(tdentry);
- nfsd_sync_dir(fdentry);
- }
- } else
- dprintk("nfsd: Caught race in nfsd_rename");
-
+ err = vfs_rename(fdir, odentry, tdir, ndentry);
+ if (!err && EX_ISSYNC(tfhp->fh_export)) {
+ nfsd_sync_dir(tdentry);
+ nfsd_sync_dir(fdentry);
+ }
#ifdef CONFIG_NFSD_V3
/* Fill in the post-op attr for the wcc data for both
* tdir and fdir
@@ -1316,7 +1263,7 @@
fill_post_wcc(ffhp);
fill_post_wcc(tfhp);
#endif /* CONFIG_NFSD_V3 */
- nfsd_double_up(&tdir->i_sem, &fdir->i_sem);
+ double_up(&tdir->i_sem, &fdir->i_sem);
dput(ndentry);
out_dput_old:
@@ -1343,7 +1290,6 @@
struct inode *dirp;
int err;
- /* N.B. We shouldn't need this test ... handled by dentry layer */
err = nfserr_acces;
if (!flen || isdotent(fname, flen))
goto out;
@@ -1351,10 +1297,11 @@
if (err)
goto out;
+ fh_lock(fhp);
dentry = fhp->fh_dentry;
dirp = dentry->d_inode;
- rdentry = lookup_dentry(fname, dget(dentry), 0);
+ rdentry = lookup_one(fname, dget(dentry));
err = PTR_ERR(rdentry);
if (IS_ERR(rdentry))
goto out_nfserr;
@@ -1365,12 +1312,6 @@
goto out;
}
- err = fh_lock_parent(fhp, rdentry);
- if (err) {
- dput(rdentry);
- goto out;
- }
-
if (type != S_IFDIR) { /* It's UNLINK */
err = vfs_unlink(dirp, rdentry);
} else { /* It's RMDIR */
@@ -1436,6 +1377,7 @@
* may choose to do less.
*/
inode = file.f_dentry->d_inode;
+ down(&inode->i_sem);
while (1) {
oldlen = cd.buflen;
@@ -1444,9 +1386,7 @@
file.f_inode->i_dev, file.f_inode->i_ino,
(int) file.f_pos, (int) oldlen, (int) cd.buflen);
*/
- down(&inode->i_sem);
err = file.f_op->readdir(&file, &cd, (filldir_t) func);
- up(&inode->i_sem);
if (err < 0)
goto out_nfserr;
if (oldlen == cd.buflen)
@@ -1454,6 +1394,7 @@
if (cd.eob)
break;
}
+ up(&inode->i_sem);
/* If we didn't fill the buffer completely, we're at EOF */
eof = !cd.eob;
@@ -1482,6 +1423,7 @@
return err;
out_nfserr:
+ up(&inode->i_sem);
err = nfserrno(err);
goto out_close;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)