diff options
Diffstat (limited to 'fs/nfsd')
-rw-r--r-- | fs/nfsd/.cvsignore | 1 | ||||
-rw-r--r-- | fs/nfsd/export.c | 48 | ||||
-rw-r--r-- | fs/nfsd/nfsctl.c | 15 | ||||
-rw-r--r-- | fs/nfsd/nfsfh.c | 22 | ||||
-rw-r--r-- | fs/nfsd/nfsproc.c | 68 | ||||
-rw-r--r-- | fs/nfsd/nfssvc.c | 57 | ||||
-rw-r--r-- | fs/nfsd/stats.c | 6 | ||||
-rw-r--r-- | fs/nfsd/vfs.c | 212 |
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(¤t->sigmask_lock); + recalc_sigpending(current); + spin_unlock_irq(¤t->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(¤t->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(¤t->sigmask_lock); + siginitsetinv(¤t->blocked, SHUTDOWN_SIGS); + recalc_sigpending(current); + spin_unlock_irq(¤t->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(¤t->sigmask_lock); - siginitsetinv(¤t->blocked, ~BLOCKABLE_SIGS); + siginitsetinv(¤t->blocked, ALLOWED_SIGS); recalc_sigpending(current); spin_unlock_irq(¤t->sigmask_lock); svc_process(serv, rqstp); - - spin_lock_irq(¤t->sigmask_lock); - siginitsetinv(¤t->blocked, SHUTDOWN_SIGS); - recalc_sigpending(current); - spin_unlock_irq(¤t->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; } /* |