summaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfsd')
-rw-r--r--fs/nfsd/.cvsignore1
-rw-r--r--fs/nfsd/export.c48
-rw-r--r--fs/nfsd/nfsctl.c15
-rw-r--r--fs/nfsd/nfsfh.c22
-rw-r--r--fs/nfsd/nfsproc.c68
-rw-r--r--fs/nfsd/nfssvc.c57
-rw-r--r--fs/nfsd/stats.c6
-rw-r--r--fs/nfsd/vfs.c212
8 files changed, 256 insertions, 173 deletions
diff --git a/fs/nfsd/.cvsignore b/fs/nfsd/.cvsignore
index 4671378ae..857dd22e9 100644
--- a/fs/nfsd/.cvsignore
+++ b/fs/nfsd/.cvsignore
@@ -1 +1,2 @@
.depend
+.*.flags
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index 4ea31ed33..9d5037a98 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -433,19 +433,37 @@ exp_readlock(void)
int
exp_writelock(void)
{
+ /* fast track */
+ if (!hash_count && !hash_lock) {
+ lock_it:
+ hash_lock = 1;
+ return 0;
+ }
+
+ current->sigpending = 0;
want_lock++;
- while (hash_count || hash_lock)
+ while (hash_count || hash_lock) {
interruptible_sleep_on(&hash_wait);
+ if (signal_pending(current))
+ break;
+ }
want_lock--;
- if (signal_pending(current))
- return -EINTR;
- hash_lock = 1;
- return 0;
+
+ /* restore the task's signals */
+ spin_lock_irq(&current->sigmask_lock);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
+ if (!hash_count && !hash_lock)
+ goto lock_it;
+ return -EINTR;
}
void
exp_unlock(void)
{
+ if (!hash_count && !hash_lock)
+ printk(KERN_WARNING "exp_unlock: not locked!\n");
if (hash_count)
hash_count--;
else
@@ -598,26 +616,28 @@ exp_delclient(struct nfsctl_client *ncp)
svc_client **clpp, *clp;
int err;
+ err = -EINVAL;
if (!exp_verify_string(ncp->cl_ident, NFSCLNT_IDMAX))
- return -EINVAL;
+ goto out;
/* Lock the hashtable */
if ((err = exp_writelock()) < 0)
- return err;
+ goto out;
+ err = -EINVAL;
for (clpp = &clients; (clp = *clpp); clpp = &(clp->cl_next))
if (!strcmp(ncp->cl_ident, clp->cl_ident))
break;
- if (!clp) {
- exp_unlock();
- return -EINVAL;
+ if (clp) {
+ *clpp = clp->cl_next;
+ exp_freeclient(clp);
+ err = 0;
}
- *clpp = clp->cl_next;
- exp_freeclient(clp);
exp_unlock();
- return 0;
+out:
+ return err;
}
/*
@@ -732,6 +752,8 @@ nfsd_export_shutdown(void)
while (clnt_hash[i])
exp_freeclient(clnt_hash[i]->h_client);
}
+ clients = NULL; /* we may be restarted before the module unloads */
+
exp_unlock();
dprintk("nfsd: export shutdown complete.\n");
}
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c
index d681a92b5..7acaafade 100644
--- a/fs/nfsd/nfsctl.c
+++ b/fs/nfsd/nfsctl.c
@@ -222,6 +222,21 @@ MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>");
extern int (*do_nfsservctl)(int, void *, void *);
/*
+ * This is called as the fill_inode function when an inode
+ * is going into (fill = 1) or out of service (fill = 0).
+ *
+ * We use it here to make sure the module can't be unloaded
+ * while a /proc inode is in use.
+ */
+void nfsd_modcount(struct inode *inode, int fill)
+{
+ if (fill)
+ MOD_INC_USE_COUNT;
+ else
+ MOD_DEC_USE_COUNT;
+}
+
+/*
* Initialize the module
*/
int
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index ff341f012..2cd24e78f 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -999,21 +999,19 @@ out:
u32
fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
{
+ struct knfs_fh *fh = &fhp->fh_handle;
struct svc_export *exp;
struct dentry *dentry;
struct inode *inode;
- struct knfs_fh *fh = &fhp->fh_handle;
u32 error = 0;
- if(fhp->fh_dverified)
- goto out;
-
- dprintk("nfsd: fh_lookup(exp %x/%ld fh %p)\n",
- fh->fh_xdev, fh->fh_xino, fh->fh_dcookie);
+ dprintk("nfsd: fh_verify(exp %x/%ld cookie %p)\n",
+ fh->fh_xdev, fh->fh_xino, fh->fh_dcookie);
+ if(fhp->fh_dverified)
+ goto check_type;
/*
* Look up the export entry.
- * N.B. We need to lock this while in use ...
*/
error = nfserr_stale;
exp = exp_get(rqstp->rq_client, fh->fh_xdev, fh->fh_xino);
@@ -1057,6 +1055,8 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
* spec says this is incorrect (implementation notes for the
* write call).
*/
+check_type:
+ dentry = fhp->fh_dentry;
inode = dentry->d_inode;
if (type > 0 && (inode->i_mode & S_IFMT) != type) {
error = (type == S_IFDIR)? nfserr_notdir : nfserr_isdir;
@@ -1069,9 +1069,11 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
/* Finally, check access permissions. */
error = nfsd_permission(fhp->fh_export, dentry, access);
+#ifdef NFSD_PARANOIA
if (error)
printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n",
dentry->d_parent->d_name.name, dentry->d_name.name, access, error);
+#endif
out:
return error;
@@ -1089,8 +1091,10 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry)
{
struct inode * inode = dentry->d_inode;
- dprintk("nfsd: fh_compose(exp %x/%ld dentry %p)\n",
- exp->ex_dev, exp->ex_ino, dentry);
+ dprintk("nfsd: fh_compose(exp %x/%ld %s/%s, ino=%ld)\n",
+ exp->ex_dev, exp->ex_ino,
+ dentry->d_parent->d_name.name, dentry->d_name.name,
+ (inode ? inode->i_ino : 0));
/*
* N.B. We shouldn't need to init the fh -- the call to fh_compose
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index 6cb65b5ef..77a158612 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -27,12 +27,6 @@ typedef struct svc_buf svc_buf;
#define NFSDDBG_FACILITY NFSDDBG_PROC
-#define sleep(msec) \
- { printk(KERN_NOTICE "nfsd: sleeping %d msecs\n", msec); \
- current->state = TASK_INTERRUPTIBLE; \
- current->timeout = jiffies + msec / 10; \
- schedule(); \
- }
#define RETURN(st) return st
static void
@@ -56,7 +50,8 @@ static int
nfsd_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle *argp,
struct nfsd_attrstat *resp)
{
- dprintk("nfsd: GETATTR %p\n", SVCFH_DENTRY(&argp->fh));
+ dprintk("nfsd: GETATTR %d/%ld\n",
+ SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh));
fh_copy(&resp->fh, &argp->fh);
RETURN(fh_verify(rqstp, &resp->fh, 0, MAY_NOP));
@@ -70,7 +65,9 @@ static int
nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp,
struct nfsd_attrstat *resp)
{
- dprintk("nfsd: SETATTR %p\n", SVCFH_DENTRY(&argp->fh));
+ dprintk("nfsd: SETATTR %d/%ld, valid=%x, size=%ld\n",
+ SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh),
+ argp->attrs.ia_valid, (long) argp->attrs.ia_size);
fh_copy(&resp->fh, &argp->fh);
RETURN(nfsd_setattr(rqstp, &resp->fh, &argp->attrs));
@@ -88,7 +85,8 @@ nfsd_proc_lookup(struct svc_rqst *rqstp, struct nfsd_diropargs *argp,
{
int nfserr;
- dprintk("nfsd: LOOKUP %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
+ dprintk("nfsd: LOOKUP %d/%ld %s\n",
+ SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh), argp->name);
nfserr = nfsd_lookup(rqstp, &argp->fh, argp->name, argp->len,
&resp->fh);
@@ -131,8 +129,8 @@ nfsd_proc_read(struct svc_rqst *rqstp, struct nfsd_readargs *argp,
u32 * buffer;
int nfserr, avail;
- dprintk("nfsd: READ %p %d bytes at %d\n",
- SVCFH_DENTRY(&argp->fh),
+ dprintk("nfsd: READ %d/%ld %d bytes at %d\n",
+ SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh),
argp->count, argp->offset);
/* Obtain buffer pointer for payload. 19 is 1 word for
@@ -168,8 +166,8 @@ nfsd_proc_write(struct svc_rqst *rqstp, struct nfsd_writeargs *argp,
{
int nfserr;
- dprintk("nfsd: WRITE %p %d bytes at %d\n",
- SVCFH_DENTRY(&argp->fh),
+ dprintk("nfsd: WRITE %d/%ld %d bytes at %d\n",
+ SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh),
argp->len, argp->offset);
nfserr = nfsd_write(rqstp, fh_copy(&resp->fh, &argp->fh),
@@ -192,24 +190,21 @@ static int
nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
struct nfsd_diropres *resp)
{
- struct inode *dirp, *inode = NULL;
- struct iattr *attr;
- svc_fh *dirfhp, *newfhp;
+ 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;
dev_t rdev = NODEV;
- dprintk("nfsd: CREATE %p %s\n", SVCFH_DENTRY(&argp->fh), argp->name);
-
- dirfhp = &argp->fh;
- newfhp = &resp->fh;
- attr = &argp->attrs;
+ dprintk("nfsd: CREATE %d/%ld %s\n",
+ SVCFH_DEV(dirfhp), SVCFH_INO(dirfhp), argp->name);
/* Get the directory inode */
nfserr = fh_verify(rqstp, dirfhp, S_IFDIR, MAY_EXEC);
if (nfserr)
goto done; /* must fh_put dirfhp even on error */
- dirp = dirfhp->fh_dentry->d_inode;
/* Check for MAY_WRITE separately. */
nfserr = nfsd_permission(dirfhp->fh_export, dirfhp->fh_dentry,
@@ -247,10 +242,9 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
}
/* This is for "echo > /dev/null" a la SunOS. Argh. */
- if (rdonly && (!exists || type == S_IFREG)) {
- nfserr = nfserr_rofs;
+ nfserr = nfserr_rofs;
+ if (rdonly && (!exists || type == S_IFREG))
goto done;
- }
attr->ia_valid |= ATTR_MODE;
attr->ia_mode = type | mode;
@@ -292,11 +286,14 @@ nfsd_proc_create(struct svc_rqst *rqstp, struct nfsd_createargs *argp,
nfserr = nfsd_create(rqstp, dirfhp, argp->name, argp->len,
attr, type, rdev, newfhp);
} else if (type == S_IFREG) {
+ dprintk("nfsd: existing %s, valid=%x, size=%ld\n",
+ argp->name, attr->ia_valid, (long) attr->ia_size);
/* File already exists. We ignore all attributes except
* size, so that creat() behaves exactly like
* open(..., O_CREAT|O_TRUNC|O_WRONLY).
*/
- if ((attr->ia_valid &= ~(ATTR_SIZE)) != 0)
+ attr->ia_valid &= ATTR_SIZE;
+ if (attr->ia_valid)
nfserr = nfsd_setattr(rqstp, newfhp, attr);
}
@@ -427,22 +424,25 @@ nfsd_proc_readdir(struct svc_rqst *rqstp, struct nfsd_readdirargs *argp,
u32 * buffer;
int nfserr, count;
- dprintk("nfsd: READDIR %p %d bytes at %d\n",
- SVCFH_DENTRY(&argp->fh),
+ dprintk("nfsd: READDIR %d/%ld %d bytes at %d\n",
+ SVCFH_DEV(&argp->fh), SVCFH_INO(&argp->fh),
argp->count, argp->cookie);
/* Reserve buffer space for status */
svcbuf_reserve(&rqstp->rq_resbuf, &buffer, &count, 1);
- /* Make sure we've room for the NULL ptr & eof flag, and shrink to
- * client read size */
- if ((count -= 8) > argp->count)
- count = argp->count;
+ /* Shrink to the client read size */
+ if (count > (argp->count >> 2))
+ count = argp->count >> 2;
+
+ /* Make sure we've room for the NULL ptr & eof flag */
+ count -= 2;
+ if (count < 0)
+ count = 0;
/* Read directory and encode entries on the fly */
nfserr = nfsd_readdir(rqstp, &argp->fh, (loff_t) argp->cookie,
- nfssvc_encode_entry,
- buffer, &count);
+ nfssvc_encode_entry, buffer, &count);
resp->count = count;
fh_put(&argp->fh);
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c
index 051061add..0164b6a7e 100644
--- a/fs/nfsd/nfssvc.c
+++ b/fs/nfsd/nfssvc.c
@@ -36,27 +36,13 @@
#define NFSDDBG_FACILITY NFSDDBG_SVC
#define NFSD_BUFSIZE (1024 + NFSSVC_MAXBLKSIZE)
-#define BLOCKABLE_SIGS (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-#define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM))
+#define ALLOWED_SIGS (sigmask(SIGKILL) | sigmask(SIGSTOP))
+#define SHUTDOWN_SIGS (sigmask(SIGKILL) | sigmask(SIGINT) | sigmask(SIGTERM))
extern struct svc_program nfsd_program;
static void nfsd(struct svc_rqst *rqstp);
struct timeval nfssvc_boot = { 0, 0 };
-
-/*
- * Make a socket for nfsd
- */
-static int
-nfsd_makesock(struct svc_serv *serv, int protocol, unsigned short port)
-{
- struct sockaddr_in sin;
-
- dprintk("nfsd: creating socket proto = %d\n", protocol);
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = INADDR_ANY;
- sin.sin_port = htons(port);
- return svc_create_socket(serv, protocol, &sin);
-}
+static int nfsd_active = 0;
int
nfsd_svc(unsigned short port, int nrservs)
@@ -65,17 +51,19 @@ nfsd_svc(unsigned short port, int nrservs)
int error;
dprintk("nfsd: creating service\n");
+ error = -EINVAL;
if (nrservs < 0)
- return -EINVAL;
+ goto out;
if (nrservs > NFSD_MAXSERVS)
nrservs = NFSD_MAXSERVS;
+ error = -ENOMEM;
serv = svc_create(&nfsd_program, NFSD_BUFSIZE, NFSSVC_XDRSIZE);
if (serv == NULL)
- return -ENOMEM;
+ goto out;
- if ((error = nfsd_makesock(serv, IPPROTO_UDP, port)) < 0
- || (error = nfsd_makesock(serv, IPPROTO_TCP, port)) < 0)
+ if ((error = svc_makesock(serv, IPPROTO_UDP, port)) < 0
+ || (error = svc_makesock(serv, IPPROTO_TCP, port)) < 0)
goto failure;
while (nrservs--) {
@@ -86,6 +74,7 @@ nfsd_svc(unsigned short port, int nrservs)
failure:
svc_destroy(serv); /* Release server */
+out:
return error;
}
@@ -98,24 +87,30 @@ nfsd(struct svc_rqst *rqstp)
struct svc_serv *serv = rqstp->rq_server;
int oldumask, err;
- lock_kernel();
/* Lock module and set up kernel thread */
MOD_INC_USE_COUNT;
+ lock_kernel();
exit_mm(current);
current->session = 1;
current->pgrp = 1;
sprintf(current->comm, "nfsd");
oldumask = current->fs->umask; /* Set umask to 0. */
- siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
current->fs->umask = 0;
- nfssvc_boot = xtime; /* record boot time */
+ if (!nfsd_active++)
+ nfssvc_boot = xtime; /* record boot time */
lockd_up(); /* start lockd */
/*
* The main request loop
*/
for (;;) {
+ /* Block all but the shutdown signals */
+ spin_lock_irq(&current->sigmask_lock);
+ siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
+ recalc_sigpending(current);
+ spin_unlock_irq(&current->sigmask_lock);
+
/*
* Find a socket with data available and call its
* recvfrom routine.
@@ -140,18 +135,13 @@ nfsd(struct svc_rqst *rqstp)
svc_drop(rqstp);
serv->sv_stats->rpcbadclnt++;
} else {
- /* Process request with all signals blocked. */
+ /* Process request with signals blocked. */
spin_lock_irq(&current->sigmask_lock);
- siginitsetinv(&current->blocked, ~BLOCKABLE_SIGS);
+ siginitsetinv(&current->blocked, ALLOWED_SIGS);
recalc_sigpending(current);
spin_unlock_irq(&current->sigmask_lock);
svc_process(serv, rqstp);
-
- spin_lock_irq(&current->sigmask_lock);
- siginitsetinv(&current->blocked, SHUTDOWN_SIGS);
- recalc_sigpending(current);
- spin_unlock_irq(&current->sigmask_lock);
}
/* Unlock export hash tables */
@@ -172,6 +162,11 @@ nfsd(struct svc_rqst *rqstp)
/* Release lockd */
lockd_down();
+ if (!--nfsd_active) {
+ printk("nfsd: last server exiting\n");
+ /* revoke all exports */
+ nfsd_export_shutdown();
+ }
/* Destroy the thread */
svc_exit_thread(rqstp);
diff --git a/fs/nfsd/stats.c b/fs/nfsd/stats.c
index 51a0fbc71..accd0aadc 100644
--- a/fs/nfsd/stats.c
+++ b/fs/nfsd/stats.c
@@ -75,8 +75,12 @@ nfsd_stat_init(void)
{
struct proc_dir_entry *ent;
- if ((ent = svc_proc_register(&nfsd_svcstats)) != 0)
+ if ((ent = svc_proc_register(&nfsd_svcstats)) != 0) {
ent->read_proc = nfsd_proc_read;
+#ifdef MODULE
+ ent->fill_inode = nfsd_modcount;
+#endif
+ }
}
void
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 29a36a554..48ab4cbeb 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -105,7 +105,7 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
int len, struct svc_fh *resfh)
{
struct svc_export *exp;
- struct dentry *dparent;
+ struct dentry *dparent, *dchild;
int err;
dprintk("nfsd: nfsd_lookup(fh %p, %s)\n", SVCFH_DENTRY(fhp), name);
@@ -118,34 +118,48 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
dparent = fhp->fh_dentry;
exp = fhp->fh_export;
- /* Fast path... */
err = nfsd_permission(exp, dparent, MAY_EXEC);
- if ((err == 0) &&
- !fs_off_limits(dparent->d_inode->i_sb) &&
- !nfsd_iscovered(dparent, exp)) {
- struct dentry *dchild;
-
- /* Lookup the name, but don't follow links */
- dchild = lookup_dentry(name, dget(dparent), 0);
- err = PTR_ERR(dchild);
- if (IS_ERR(dchild))
- return nfserrno(-err);
+ if (err)
+ goto out;
+ err = nfserr_noent;
+ if (fs_off_limits(dparent->d_sb))
+ goto out;
+ err = nfserr_acces;
+ if (nfsd_iscovered(dparent, exp))
+ goto out;
- /*
- * Note: we compose the filehandle now, but as the
- * dentry may be negative, it may need to be updated.
- */
- fh_compose(resfh, exp, dchild);
- return (dchild->d_inode ? 0 : nfserr_noent);
+ /* Lookup the name, but don't follow links */
+ dchild = lookup_dentry(name, dget(dparent), 0);
+ if (IS_ERR(dchild))
+ goto out_nfserr;
+ /*
+ * Make sure we haven't crossed a mount point ...
+ */
+ if (dchild->d_sb != dparent->d_sb) {
+#ifdef NFSD_PARANOIA
+printk("nfsd_lookup: %s/%s crossed mount point!\n", dparent->d_name.name, name);
+#endif
+ goto out_dput;
}
- /* Slow path... */
- if (fs_off_limits(dparent->d_inode->i_sb))
- return nfserr_noent;
- if (nfsd_iscovered(dparent, exp))
- return nfserr_acces;
+ /*
+ * Note: we compose the filehandle now, but as the
+ * dentry may be negative, it may need to be updated.
+ */
+ fh_compose(resfh, exp, dchild);
+ err = nfserr_noent;
+ if (dchild->d_inode)
+ err = 0;
out:
return err;
+
+out_nfserr:
+ err = nfserrno(-PTR_ERR(dchild));
+ goto out;
+out_dput:
+ dput(dchild);
+ err = nfserr_acces;
+ goto out;
}
/*
@@ -176,20 +190,24 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
inode = dentry->d_inode;
/* The size case is special... */
- if ((iap->ia_valid & ATTR_SIZE) && S_ISREG(inode->i_mode)) {
+ if (iap->ia_valid & ATTR_SIZE) {
+if (!S_ISREG(inode->i_mode))
+printk("nfsd_setattr: size change??\n");
if (iap->ia_size < inode->i_size) {
err = nfsd_permission(fhp->fh_export, dentry, MAY_TRUNC);
if (err != 0)
goto out;
}
- if ((err = get_write_access(inode)) != 0)
- return nfserrno(-err);
+ err = get_write_access(inode);
+ if (err)
+ goto out_nfserr;
+ /* N.B. Should we update the inode cache here? */
inode->i_size = iap->ia_size;
if (inode->i_op && inode->i_op->truncate)
inode->i_op->truncate(inode);
mark_inode_dirty(inode);
put_write_access(inode);
- iap->ia_valid &= ATTR_SIZE;
+ iap->ia_valid &= ~ATTR_SIZE;
iap->ia_valid |= ATTR_MTIME;
iap->ia_mtime = CURRENT_TIME;
}
@@ -216,15 +234,19 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap)
if (iap->ia_valid) {
iap->ia_valid |= ATTR_CTIME;
iap->ia_ctime = CURRENT_TIME;
- err = notify_change(inode, iap);
+ err = notify_change(dentry, iap);
if (err)
- return nfserrno(-err);
+ goto out_nfserr;
if (EX_ISSYNC(fhp->fh_export))
write_inode_now(inode);
}
err = 0;
out:
return err;
+
+out_nfserr:
+ err = nfserrno(-err);
+ goto out;
}
/*
@@ -461,7 +483,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
ia.ia_valid = ATTR_MODE;
ia.ia_mode = inode->i_mode & ~(S_ISUID | S_ISGID);
- notify_change(inode, &ia);
+ notify_change(dentry, &ia);
}
fh_unlock(fhp); /* unlock inode */
@@ -568,7 +590,8 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
dchild = lookup_dentry(fname, dget(dentry), 0);
err = PTR_ERR(dchild);
if(IS_ERR(dchild))
- return nfserrno(-err);
+ goto out_nfserr;
+ fh_compose(resfhp, fhp->fh_export, dchild);
} else
dchild = resfhp->fh_dentry;
/*
@@ -597,19 +620,14 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
fh_unlock(fhp);
if (err < 0)
- return nfserrno(-err);
+ goto out_nfserr;
if (EX_ISSYNC(fhp->fh_export))
write_inode_now(dirp);
/*
- * Assemble the file handle for the newly created file,
- * or update the filehandle to get the new inode info.
+ * Update the filehandle to get the new inode info.
*/
- if (!resfhp->fh_dverified) {
- fh_compose(resfhp, fhp->fh_export, dchild);
- } else {
- fh_update(resfhp);
- }
+ fh_update(resfhp);
/* Set file attributes. Mode has already been set and
* setting uid/gid works only for root. Irix appears to
@@ -621,6 +639,10 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
err = nfsd_setattr(rqstp, resfhp, iap);
out:
return err;
+
+out_nfserr:
+ err = nfserrno(-err);
+ goto out;
}
/*
@@ -654,7 +676,7 @@ nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size)
fh_lock(fhp);
newattrs.ia_size = size;
newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
- err = notify_change(inode, &newattrs);
+ err = notify_change(dentry, &newattrs);
if (!err) {
vmtruncate(inode, size);
if (inode->i_op && inode->i_op->truncate)
@@ -694,19 +716,21 @@ nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp)
goto out;
UPDATE_ATIME(inode);
+ /* N.B. Why does this call need a get_fs()?? */
oldfs = get_fs(); set_fs(KERNEL_DS);
- err = inode->i_op->readlink(inode, buf, *lenp);
+ err = inode->i_op->readlink(dentry, buf, *lenp);
set_fs(oldfs);
if (err < 0)
- err = nfserrno(-err);
- else {
- *lenp = err;
- err = 0;
- }
-
+ goto out_nfserr;
+ *lenp = err;
+ err = 0;
out:
return err;
+
+out_nfserr:
+ err = nfserrno(-err);
+ goto out;
}
/*
@@ -755,12 +779,14 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
}
}
fh_compose(resfhp, fhp->fh_export, dnew);
-
-out_nfserr:
if (err)
- err = nfserrno(-err);
+ goto out_nfserr;
out:
return err;
+
+out_nfserr:
+ err = nfserrno(-err);
+ goto out;
}
/*
@@ -771,7 +797,7 @@ int
nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
char *fname, int len, struct svc_fh *tfhp)
{
- struct dentry *ddir, *dnew;
+ struct dentry *ddir, *dnew, *dold;
struct inode *dirp, *dest;
int err;
@@ -793,12 +819,14 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
err = -EEXIST;
if (dnew->d_inode)
goto dput_and_out;
- dest = tfhp->fh_dentry->d_inode;
err = -EPERM;
if (!len)
goto dput_and_out;
+ dold = tfhp->fh_dentry;
+ dest = dold->d_inode;
+
err = -EACCES;
if (nfsd_iscovered(ddir, ffhp->fh_export))
goto dput_and_out;
@@ -812,7 +840,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
goto dput_and_out;
fh_lock(ffhp);
- err = dirp->i_op->link(dest, dirp, dnew);
+ err = dirp->i_op->link(dold, dirp, dnew);
fh_unlock(ffhp);
if (!err && EX_ISSYNC(ffhp->fh_export)) {
@@ -821,11 +849,14 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
}
dput_and_out:
dput(dnew);
-out_nfserr:
if (err)
- err = nfserrno(-err);
+ goto out_nfserr;
out:
return err;
+
+out_nfserr:
+ err = nfserrno(-err);
+ goto out;
}
/*
@@ -917,11 +948,14 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
out_dput_old:
dput(odentry);
-out_nfserr:
if (err)
- err = nfserrno(-err);
+ goto out_nfserr;
out:
return err;
+
+out_nfserr:
+ err = nfserrno(-err);
+ goto out;
}
/*
@@ -965,14 +999,16 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
fh_unlock(fhp);
dput(rdentry);
- if (!err && EX_ISSYNC(fhp->fh_export))
- write_inode_now(dirp);
-
-out_nfserr:
if (err)
- err = nfserrno(-err);
+ goto out_nfserr;
+ if (EX_ISSYNC(fhp->fh_export))
+ write_inode_now(dirp);
out:
return err;
+
+out_nfserr:
+ err = nfserrno(-err);
+ goto out;
}
/*
@@ -982,29 +1018,30 @@ int
nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
encode_dent_fn func, u32 *buffer, int *countp)
{
- struct readdir_cd cd;
struct inode *inode;
- struct file file;
u32 *p;
int oldlen, eof, err;
+ struct file file;
+ struct readdir_cd cd;
+ err = 0;
if (offset > ~(u32) 0)
- return 0;
+ goto out;
- if ((err = nfsd_open(rqstp, fhp, S_IFDIR, OPEN_READ, &file)) != 0)
- return err;
+ err = nfsd_open(rqstp, fhp, S_IFDIR, OPEN_READ, &file);
+ if (err)
+ goto out;
- if (!file.f_op->readdir) {
- nfsd_close(&file);
- return nfserr_notdir;
- }
+ err = nfserr_notdir;
+ if (!file.f_op->readdir)
+ goto out_close;
file.f_pos = offset;
/* Set up the readdir context */
memset(&cd, 0, sizeof(cd));
cd.rqstp = rqstp;
cd.buffer = buffer;
- cd.buflen = *countp >> 2;
+ cd.buflen = *countp; /* count of words */
/*
* Read the directory entries. This silly loop is necessary because
@@ -1012,7 +1049,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
* may choose to do less.
*/
inode = file.f_dentry->d_inode;
- do {
+ while (1) {
oldlen = cd.buflen;
/*
@@ -1020,16 +1057,16 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
file.f_inode->i_dev, file.f_inode->i_ino,
(int) file.f_pos, (int) oldlen, (int) cd.buflen);
*/
- err = file.f_op->readdir(&file,
- &cd, (filldir_t) func);
-
- if (err < 0) {
- nfsd_close(&file);
- return nfserrno(-err);
- }
+ 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)
break;
- } while (oldlen != cd.buflen && !cd.eob);
+ if (cd.eob)
+ break;
+ }
/* If we didn't fill the buffer completely, we're at EOF */
eof = !cd.eob;
@@ -1040,9 +1077,6 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
if (cd.offset && !eof)
*cd.offset = htonl(file.f_pos);
- /* Close the file */
- nfsd_close(&file);
-
p = cd.buffer;
*p++ = 0; /* no more entries */
*p++ = htonl(eof); /* end of directory */
@@ -1051,7 +1085,15 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
dprintk("nfsd: readdir result %d bytes, eof %d offset %ld\n",
*countp, eof,
cd.offset? ntohl(*cd.offset) : -1);
- return 0;
+ err = 0;
+out_close:
+ nfsd_close(&file);
+out:
+ return err;
+
+out_nfserr:
+ err = nfserrno(-err);
+ goto out_close;
}
/*