summaryrefslogtreecommitdiffstats
path: root/fs/nfsd/nfsproc.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfsd/nfsproc.c')
-rw-r--r--fs/nfsd/nfsproc.c101
1 files changed, 58 insertions, 43 deletions
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index 77a158612..8b115ed3a 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -50,7 +50,7 @@ static int
nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp,
struct nfsd_attrstat *resp)
{
- dprintk("nfsd: GETATTR %d/%ld\n",
+ dprintk("nfsd: GETATTR %d/%d\n",
SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh));
fh_copy(&resp->fh, &argp->fh);
@@ -65,7 +65,7 @@ static int
nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp,
struct nfsd_attrstat *resp)
{
- dprintk("nfsd: SETATTR %d/%ld, valid=%x, size=%ld\n",
+ dprintk("nfsd: SETATTR %d/%d, valid=%x, size=%ld\n",
SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh),
argp->attrs.ia_valid, (long) argp->attrs.ia_size);
@@ -85,7 +85,7 @@ nfsd_proc_lookup(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
{
int nfserr;
- dprintk("nfsd: LOOKUP %d/%ld %s\n",
+ dprintk("nfsd: LOOKUP %d/%d %s\n",
SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh), argp->name);
nfserr = nfsd_lookup(rqstp, &argp->fh, argp->name, argp->len,
@@ -129,7 +129,7 @@ nfsd_proc_read(struct svc_rqst *rqstp, struct nfsd_readargs *argp,
u32 * buffer;
int nfserr, avail;
- dprintk("nfsd: READ %d/%ld %d bytes at %d\n",
+ dprintk("nfsd: READ %d/%d %d bytes at %d\n",
SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh),
argp->count, argp->offset);
@@ -166,7 +166,7 @@ nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp,
{
int nfserr;
- dprintk("nfsd: WRITE %d/%ld %d bytes at %d\n",
+ dprintk("nfsd: WRITE %d/%d %d bytes at %d\n",
SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh),
argp->len, argp->offset);
@@ -180,10 +180,8 @@ nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp,
/*
* CREATE processing is complicated. The keyword here is `overloaded.'
- * There's a small race condition here between the check for existence
- * and the actual create() call, but one could even consider this a
- * feature because this only happens if someone else creates the file
- * at the same time.
+ * The parent directory is kept locked between the check for existence
+ * and the actual create() call in compliance with VFS protocols.
* N.B. After this call _both_ argp->fh and resp->fh need an fh_put
*/
static int
@@ -193,15 +191,14 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
svc_fh *dirfhp = &argp->fh;
svc_fh *newfhp = &resp->fh;
struct iattr *attr = &argp->attrs;
- struct inode *inode = NULL;
- int nfserr, type, mode;
- int rdonly = 0, exists;
+ struct inode *inode;
+ int nfserr, type, mode, rdonly = 0;
dev_t rdev = NODEV;
- dprintk("nfsd: CREATE %d/%ld %s\n",
+ dprintk("nfsd: CREATE %d/%d %s\n",
SVCFH_DEV(dirfhp), SVCFH_INO(dirfhp), argp->name);
- /* Get the directory inode */
+ /* First verify the parent file handle */
nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_EXEC);
if (nfserr)
goto done; /* must fh_put dirfhp even on error */
@@ -214,18 +211,32 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
} else if (nfserr)
goto done;
- /* First, check if the file already exists. */
- exists = !nfsd_lookup(rqstp, dirfhp, argp->name, argp->len, newfhp);
-
- if (newfhp->fh_dverified)
- inode = newfhp->fh_dentry->d_inode;
+ /*
+ * Do a lookup to verify the new file handle.
+ */
+ nfserr = nfsd_lookup(rqstp, dirfhp, argp->name, argp->len, newfhp);
+ if (nfserr) {
+ if (nfserr != nfserr_noent)
+ goto done;
+ /*
+ * If the new file handle wasn't verified, we can't tell
+ * whether the file exists or not. Time to bail ...
+ */
+ nfserr = nfserr_acces;
+ if (!newfhp->fh_dverified) {
+ printk(KERN_WARNING
+ "nfsd_proc_create: file handle not verified\n");
+ goto done;
+ }
+ }
- /* Get rid of this soon... */
- if (exists && !inode) {
- printk("nfsd_proc_create: Wheee... exists but d_inode==NULL\n");
- nfserr = nfserr_rofs;
+ /*
+ * Lock the parent directory and check for existence.
+ */
+ nfserr = fh_lock_parent(dirfhp, newfhp->fh_dentry);
+ if (nfserr)
goto done;
- }
+ inode = newfhp->fh_dentry->d_inode;
/* Unfudge the mode bits */
if (attr->ia_valid & ATTR_MODE) {
@@ -233,7 +244,7 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
mode = attr->ia_mode & ~S_IFMT;
if (!type) /* HP weirdness */
type = S_IFREG;
- } else if (exists) {
+ } else if (inode) {
type = inode->i_mode & S_IFMT;
mode = inode->i_mode & ~S_IFMT;
} else {
@@ -243,8 +254,8 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
/* This is for "echo > /dev/null" a la SunOS. Argh. */
nfserr = nfserr_rofs;
- if (rdonly && (!exists || type == S_IFREG))
- goto done;
+ if (rdonly && (!inode || type == S_IFREG))
+ goto out_unlock;
attr->ia_valid |= ATTR_MODE;
attr->ia_mode = type | mode;
@@ -252,7 +263,6 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
/* Special treatment for non-regular files according to the
* gospel of sun micro
*/
- nfserr = 0;
if (type != S_IFREG) {
int is_borc = 0;
u32 size = attr->ia_size;
@@ -267,21 +277,21 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
} else if (size != rdev) {
/* dev got truncated because of 16bit Linux dev_t */
nfserr = nfserr_io; /* or nfserr_inval? */
- goto done;
+ goto out_unlock;
} else {
/* Okay, char or block special */
is_borc = 1;
}
/* Make sure the type and device matches */
- if (exists && (type != (inode->i_mode & S_IFMT)
- || (is_borc && inode->i_rdev != rdev))) {
- nfserr = nfserr_exist;
- goto done;
- }
+ nfserr = nfserr_exist;
+ if (inode && (type != (inode->i_mode & S_IFMT) ||
+ (is_borc && inode->i_rdev != rdev)))
+ goto out_unlock;
}
- if (!exists) {
+ nfserr = 0;
+ if (!inode) {
/* File doesn't exist. Create it and set attrs */
nfserr = nfsd_create(rqstp, dirfhp, argp->name, argp->len,
attr, type, rdev, newfhp);
@@ -297,6 +307,10 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
nfserr = nfsd_setattr(rqstp, newfhp, attr);
}
+out_unlock:
+ /* We don't really need to unlock, as fh_put does it. */
+ fh_unlock(dirfhp);
+
done:
fh_put(dirfhp);
RETURN(nfserr);
@@ -359,9 +373,9 @@ nfsd_proc_symlink(struct svc_rqst *rqstp, struct nfsd_symlinkargs *argp,
int nfserr;
dprintk("nfsd: SYMLINK %p %s -> %s\n",
- SVCFH_DENTRY(&argp->ffh),
- argp->fname, argp->tname);
+ SVCFH_DENTRY(&argp->ffh), argp->fname, argp->tname);
+ memset(&newfh, 0, sizeof(struct svc_fh));
/*
* Create the link, look up new file and set attrs.
*/
@@ -386,12 +400,13 @@ nfsd_proc_mkdir(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
{
int nfserr;
- dprintk("nfsd: MKDIR %p %s\n",
- SVCFH_DENTRY(&argp->fh),
- argp->name);
+ dprintk("nfsd: MKDIR %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
+
+ if (resp->fh.fh_dverified) {
+ printk(KERN_WARNING
+ "nfsd_proc_mkdir: response already verified??\n");
+ }
- /* N.B. what about the dentry count?? */
- resp->fh.fh_dverified = 0; /* paranoia */
nfserr = nfsd_create(rqstp, &argp->fh, argp->name, argp->len,
&argp->attrs, S_IFDIR, 0, &resp->fh);
fh_put(&argp->fh);
@@ -424,7 +439,7 @@ nfsd_proc_readdir(struct svc_rqst *rqstp, struct nfsd_readdirargs *argp,
u32 * buffer;
int nfserr, count;
- dprintk("nfsd: READDIR %d/%ld %d bytes at %d\n",
+ dprintk("nfsd: READDIR %d/%d %d bytes at %d\n",
SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh),
argp->count, argp->cookie);