diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1998-08-25 09:12:35 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1998-08-25 09:12:35 +0000 |
commit | c7fc24dc4420057f103afe8fc64524ebc25c5d37 (patch) | |
tree | 3682407a599b8f9f03fc096298134cafba1c9b2f /fs/nfsd | |
parent | 1d793fade8b063fde3cf275bf1a5c2d381292cd9 (diff) |
o Merge with Linux 2.1.116.
o New Newport console code.
o New G364 console code.
Diffstat (limited to 'fs/nfsd')
-rw-r--r-- | fs/nfsd/Makefile | 6 | ||||
-rw-r--r-- | fs/nfsd/export.c | 28 | ||||
-rw-r--r-- | fs/nfsd/nfsctl.c | 2 | ||||
-rw-r--r-- | fs/nfsd/nfsfh.c | 194 | ||||
-rw-r--r-- | fs/nfsd/nfsproc.c | 101 | ||||
-rw-r--r-- | fs/nfsd/vfs.c | 221 |
6 files changed, 346 insertions, 206 deletions
diff --git a/fs/nfsd/Makefile b/fs/nfsd/Makefile index 111adfbf0..a8d65fc7a 100644 --- a/fs/nfsd/Makefile +++ b/fs/nfsd/Makefile @@ -1,11 +1,11 @@ # -# Makefile for the linux nfs-filesystem routines. +# Makefile for the Linux nfs filesystem routines. # # Note! Dependencies are done automagically by 'make dep', which also # removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). +# unless it's something special (not a .c file). # -# Note 2! The CFLAGS definitions are now in the main makefile... +# Note 2! The CFLAGS definitions are now in the main makefile. O_TARGET := nfsd.o O_OBJS := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \ diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 9d5037a98..0d214eecb 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -32,8 +32,8 @@ typedef struct svc_client svc_client; typedef struct svc_export svc_export; -static svc_export * exp_find(svc_client *clp, dev_t dev); -static svc_export * exp_parent(svc_client *clp, dev_t dev); +static svc_export * exp_find(svc_client *clp, kdev_t dev); +static svc_export * exp_parent(svc_client *clp, kdev_t dev); static void exp_unexport_all(svc_client *clp); static void exp_do_unexport(svc_export *unexp); static svc_client * exp_getclientbyname(char *name); @@ -46,6 +46,7 @@ static int exp_verify_string(char *cp, int max); #define CLIENT_HASHMASK (CLIENT_HASHMAX - 1) #define CLIENT_HASH(a) \ ((((a)>>24) ^ ((a)>>16) ^ ((a)>>8) ^(a)) & CLIENT_HASHMASK) +/* XXX: is this adequate for 32bit kdev_t ? */ #define EXPORT_HASH(dev) ((dev) & (NFSCLNT_EXPMAX - 1)) struct svc_clnthash { @@ -69,7 +70,7 @@ static struct wait_queue * hash_wait = NULL; * Find a client's export for a device. */ static inline svc_export * -exp_find(svc_client *clp, dev_t dev) +exp_find(svc_client *clp, kdev_t dev) { svc_export * exp; @@ -83,7 +84,7 @@ exp_find(svc_client *clp, dev_t dev) * Find the client's export entry matching xdev/xino. */ svc_export * -exp_get(svc_client *clp, dev_t dev, ino_t ino) +exp_get(svc_client *clp, kdev_t dev, ino_t ino) { svc_export * exp; @@ -97,7 +98,7 @@ exp_get(svc_client *clp, dev_t dev, ino_t ino) * Check whether there are any exports for a device. */ static int -exp_device_in_use(dev_t dev) +exp_device_in_use(kdev_t dev) { struct svc_client *clp; @@ -112,7 +113,7 @@ exp_device_in_use(dev_t dev) * Look up the device of the parent fs. */ static inline int -nfsd_parentdev(dev_t *devp) +nfsd_parentdev(kdev_t *devp) { struct super_block *sb; @@ -129,10 +130,10 @@ nfsd_parentdev(dev_t *devp) * only by the export syscall to keep the export tree consistent. */ static svc_export * -exp_parent(svc_client *clp, dev_t dev) +exp_parent(svc_client *clp, kdev_t dev) { svc_export *exp; - dev_t xdev = dev; + kdev_t xdev = dev; do { exp = exp_find(clp, xdev); @@ -155,7 +156,7 @@ exp_export(struct nfsctl_export *nxp) struct dentry *dentry = NULL; struct inode *inode = NULL; int i, err; - dev_t dev; + kdev_t dev; ino_t ino; /* Consistency check */ @@ -166,7 +167,7 @@ exp_export(struct nfsctl_export *nxp) dprintk("exp_export called for %s:%s (%x/%ld fl %x).\n", nxp->ex_client, nxp->ex_path, nxp->ex_dev, nxp->ex_ino, nxp->ex_flags); - dev = nxp->ex_dev; + dev = to_kdev_t(nxp->ex_dev); ino = nxp->ex_ino; /* Try to lock the export table for update */ @@ -205,7 +206,10 @@ exp_export(struct nfsctl_export *nxp) if(!inode) goto finish; err = -EINVAL; - if(inode->i_dev != nxp->ex_dev || inode->i_ino != nxp->ex_ino) { + if(inode->i_dev != dev || inode->i_ino != nxp->ex_ino) { + + printk(KERN_DEBUG "exp_export: i_dev = %x, dev = %x\n", + inode->i_dev, dev); /* I'm just being paranoid... */ goto finish; } @@ -386,7 +390,7 @@ out: * since its harder to fool a kernel module than a user space program. */ int -exp_rootfh(struct svc_client *clp, dev_t dev, ino_t ino, struct knfs_fh *f) +exp_rootfh(struct svc_client *clp, kdev_t dev, ino_t ino, struct knfs_fh *f) { struct svc_export *exp = NULL; struct svc_fh fh; diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 66c4ecd1f..43de72f54 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -124,7 +124,7 @@ nfsctl_getfh(struct nfsctl_fhparm *data, struct knfs_fh *res) if (!(clp = exp_getclient(sin))) err = -EPERM; else - err = exp_rootfh(clp, data->gf_dev, data->gf_ino, res); + err = exp_rootfh(clp, to_kdev_t(data->gf_dev), data->gf_ino, res); exp_unlock(); return err; diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 2cd24e78f..ce89d60bf 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -1,7 +1,7 @@ /* * linux/fs/nfsd/nfsfh.c * - * NFS server filehandle treatment. + * NFS server file handle treatment. * * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> */ @@ -21,7 +21,7 @@ #define NFSD_PARANOIA 1 /* #define NFSD_DEBUG_VERBOSE 1 */ -extern unsigned long num_physpages; +extern unsigned long max_mapnr; #define NFSD_FILE_CACHE 0 #define NFSD_DIR_CACHE 1 @@ -29,7 +29,7 @@ struct fh_entry { struct dentry * dentry; unsigned long reftime; ino_t ino; - dev_t dev; + kdev_t dev; }; #define NFSD_MAXFH PAGE_SIZE/sizeof(struct fh_entry) @@ -42,7 +42,7 @@ static unsigned long nfsd_next_expire = 0; static int add_to_fhcache(struct dentry *, int); static int nfsd_d_validate(struct dentry *); -struct dentry * lookup_inode(dev_t, ino_t, ino_t); +struct dentry * lookup_inode(kdev_t, ino_t, ino_t); static LIST_HEAD(fixup_head); static LIST_HEAD(path_inuse); @@ -55,7 +55,7 @@ struct nfsd_fixup { struct list_head lru; ino_t dir; ino_t ino; - dev_t dev; + kdev_t dev; struct dentry *dentry; unsigned long reftime; }; @@ -65,11 +65,11 @@ struct nfsd_path { unsigned long reftime; int users; ino_t ino; - dev_t dev; + kdev_t dev; char name[1]; }; -static struct nfsd_fixup * find_cached_lookup(dev_t dev, ino_t dir, ino_t ino) +static struct nfsd_fixup * find_cached_lookup(kdev_t dev, ino_t dir, ino_t ino) { struct list_head *tmp = fixup_head.next; @@ -98,7 +98,9 @@ static void add_to_lookup_cache(struct dentry *dentry, struct knfs_fh *fh) { struct nfsd_fixup *fp; - fp = find_cached_lookup(fh->fh_dev, fh->fh_dirino, fh->fh_ino); + fp = find_cached_lookup(u32_to_kdev_t(fh->fh_dev), + u32_to_ino_t(fh->fh_dirino), + u32_to_ino_t(fh->fh_ino)); if (fp) { fp->dentry = dentry; return; @@ -111,9 +113,9 @@ static void add_to_lookup_cache(struct dentry *dentry, struct knfs_fh *fh) */ fp = kmalloc(sizeof(struct nfsd_fixup), GFP_KERNEL); if (fp) { - fp->dir = fh->fh_dirino; - fp->ino = fh->fh_ino; - fp->dev = fh->fh_dev; + fp->dir = u32_to_kdev_t(fh->fh_dirino); + fp->ino = u32_to_ino_t(fh->fh_ino); + fp->dev = u32_to_ino_t(fh->fh_dev); fp->dentry = dentry; fp->reftime = jiffies; list_add(&fp->lru, &fixup_head); @@ -182,7 +184,7 @@ static int add_to_path_cache(struct dentry *dentry) int len, result = 0; #ifdef NFSD_DEBUG_VERBOSE -printk("add_to_path_cache: cacheing %s/%s\n", +printk("add_to_path_cache: caching %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); #endif /* @@ -222,18 +224,18 @@ printk("add_to_path_cache: added %s, paths=%d\n", new->name, nfsd_nr_paths); return result; /* - * If the dentry's path length changed, just try again ... + * If the dentry's path length changed, just try again. */ retry: kfree(new); - printk("add_to_path_cache: path length changed, retrying\n"); + printk(KERN_DEBUG "add_to_path_cache: path length changed, retrying\n"); goto restart; } /* * Search for a path entry for the specified (dev, inode). */ -struct nfsd_path *get_path_entry(dev_t dev, ino_t ino) +struct nfsd_path *get_path_entry(kdev_t dev, ino_t ino) { struct nfsd_path *pe; struct list_head *tmp; @@ -264,7 +266,7 @@ static void put_path(struct nfsd_path *pe) static void free_path_entry(struct nfsd_path *pe) { if (pe->users) - printk("free_path_entry: %s in use, users=%d\n", + printk(KERN_DEBUG "free_path_entry: %s in use, users=%d\n", pe->name, pe->users); list_del(&pe->lru); kfree(pe); @@ -374,14 +376,14 @@ out: /* * Look up a dentry given inode and parent inode numbers. * - * This relies on the ability of a unix-like filesystem to return + * This relies on the ability of a Unix-like filesystem to return * the parent inode of a directory as the ".." (second) entry. * * This could be further optimized if we had an efficient way of * searching for a dentry given the inode: as we walk up the tree, * it's likely that a dentry exists before we reach the root. */ -struct dentry * lookup_inode(dev_t dev, ino_t dirino, ino_t ino) +struct dentry * lookup_inode(kdev_t dev, ino_t dirino, ino_t ino) { struct super_block *sb; struct dentry *root, *dentry, *result; @@ -457,7 +459,8 @@ struct dentry * lookup_inode(dev_t dev, ino_t dirino, ino_t ino) * Make sure we can't get caught in a loop ... */ if (dirino == dirent.ino && dirino != root_ino) { - printk("lookup_inode: looping?? (ino=%ld, path=%s)\n", + printk(KERN_DEBUG + "lookup_inode: looping?? (ino=%ld, path=%s)\n", dirino, name); goto out_root; } @@ -609,7 +612,7 @@ printk("expire_old: expiring %s older than %d\n", /* * Add a dentry to the file or dir cache. * - * Note: As NFS filehandles must have an inode, we don't accept + * Note: As NFS file handles must have an inode, we don't accept * negative dentries. */ static int add_to_fhcache(struct dentry *dentry, int cache) @@ -649,7 +652,7 @@ repeat: /* * Find an entry in the dir cache for the specified inode number. */ -static struct fh_entry *find_fhe_by_ino(dev_t dev, ino_t ino) +static struct fh_entry *find_fhe_by_ino(kdev_t dev, ino_t ino) { struct fh_entry * fhe = &dirstable[0]; int i; @@ -667,7 +670,7 @@ static struct fh_entry *find_fhe_by_ino(dev_t dev, ino_t ino) * Find the (directory) dentry with the specified (dev, inode) number. * Note: this leaves the dentry in the cache. */ -static struct dentry *find_dentry_by_ino(dev_t dev, ino_t ino) +static struct dentry *find_dentry_by_ino(kdev_t dev, ino_t ino) { struct fh_entry *fhe; struct nfsd_path *pe; @@ -762,7 +765,9 @@ dentry->d_parent->d_name.name, dentry->d_name.name); #endif goto out; } - if (inode->i_ino != fh->fh_ino || inode->i_dev != fh->fh_dev) + if (inode->i_ino != u32_to_ino_t(fh->fh_ino)) + goto out; + if (inode->i_dev != u32_to_kdev_t(fh->fh_dev)) goto out; fhe->dentry = NULL; @@ -838,25 +843,27 @@ static struct dentry *nfsd_cached_lookup(struct knfs_fh *fh) { struct nfsd_fixup *fp; - fp = find_cached_lookup(fh->fh_dev, fh->fh_dirino, fh->fh_ino); + fp = find_cached_lookup(u32_to_kdev_t(fh->fh_dev), + u32_to_ino_t(fh->fh_dirino), + u32_to_ino_t(fh->fh_ino)); if (fp) return fp->dentry; return NULL; } /* - * The is the basic lookup mechanism for turning an NFS filehandle + * The is the basic lookup mechanism for turning an NFS file handle * into a dentry. There are several levels to the search: * (1) Look for the dentry pointer the short-term fhcache, * and verify that it has the correct inode number. * - * (2) Try to validate the dentry pointer in the filehandle, + * (2) Try to validate the dentry pointer in the file handle, * and verify that it has the correct inode number. If this * fails, check for a cached lookup in the fix-up list and * repeat step (2) using the new dentry pointer. * * (3) Look up the dentry by using the inode and parent inode numbers - * to build the name string. This should succeed for any unix-like + * to build the name string. This should succeed for any Unix-like * filesystem. * * (4) Search for the parent dentry in the dir cache, and then @@ -885,20 +892,21 @@ find_fh_dentry(struct knfs_fh *fh) } /* - * Stage 2: Attempt to validate the dentry in the filehandle. + * Stage 2: Attempt to validate the dentry in the file handle. */ dentry = fh->fh_dcookie; recheck: if (nfsd_d_validate(dentry)) { struct inode * dir = dentry->d_parent->d_inode; - if (dir->i_ino == fh->fh_dirino && dir->i_dev == fh->fh_dev) { + if (dir->i_ino == u32_to_ino_t(fh->fh_dirino) && + dir->i_dev == u32_to_kdev_t(fh->fh_dev)) { struct inode * inode = dentry->d_inode; /* - * NFS filehandles must always have an inode, + * NFS file handles must always have an inode, * so we won't accept a negative dentry. */ - if (inode && inode->i_ino == fh->fh_ino) { + if (inode && inode->i_ino == u32_to_ino_t(fh->fh_ino)) { dget(dentry); #ifdef NFSD_DEBUG_VERBOSE printk("find_fh_dentry: validated %s/%s, ino=%ld\n", @@ -928,17 +936,19 @@ printk("find_fh_dentry: retried validation successful\n"); /* * Stage 3: Look up the dentry based on the inode and parent inode - * numbers. This should work for all unix-like filesystems ... + * numbers. This should work for all Unix-like filesystems. */ looked_up = 1; - dentry = lookup_inode(fh->fh_dev, fh->fh_dirino, fh->fh_ino); + dentry = lookup_inode(u32_to_kdev_t(fh->fh_dev), + u32_to_ino_t(fh->fh_dirino), + u32_to_ino_t(fh->fh_ino)); if (!IS_ERR(dentry)) { struct inode * inode = dentry->d_inode; #ifdef NFSD_DEBUG_VERBOSE printk("find_fh_dentry: looked up %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); #endif - if (inode && inode->i_ino == fh->fh_ino) { + if (inode && inode->i_ino == u32_to_ino_t(fh->fh_ino)) { nfsdstats.fh_lookup++; goto out; } @@ -952,12 +962,13 @@ dentry->d_parent->d_name.name, dentry->d_name.name); /* * Stage 4: Look for the parent dentry in the fhcache ... */ - parent = find_dentry_by_ino(fh->fh_dev, fh->fh_dirino); + parent = find_dentry_by_ino(u32_to_kdev_t(fh->fh_dev), + u32_to_ino_t(fh->fh_dirino)); if (parent) { /* * ... then search for the inode in the parent directory. */ - dentry = lookup_by_inode(parent, fh->fh_ino); + dentry = lookup_by_inode(parent, u32_to_ino_t(fh->fh_ino)); dput(parent); if (dentry) goto out; @@ -967,8 +978,8 @@ dentry->d_parent->d_name.name, dentry->d_name.name); * Stage 5: Search the whole volume. */ #ifdef NFSD_PARANOIA -printk("find_fh_dentry: %s, %ld/%ld not found -- need full search!\n", -kdevname(fh->fh_dev), fh->fh_dirino, fh->fh_ino); +printk("find_fh_dentry: %s, %u/%u not found -- need full search!\n", +kdevname(u32_to_kdev_t(fh->fh_dev)), fh->fh_dirino, fh->fh_ino); #endif dentry = NULL; nfsdstats.fh_stale++; @@ -993,7 +1004,7 @@ out: /* * Perform sanity checks on the dentry in a client's file handle. * - * Note that the filehandle dentry may need to be freed even after + * Note that the file handle dentry may need to be freed even after * an error return. */ u32 @@ -1005,7 +1016,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) struct inode *inode; u32 error = 0; - dprintk("nfsd: fh_verify(exp %x/%ld cookie %p)\n", + dprintk("nfsd: fh_verify(exp %x/%u cookie %p)\n", fh->fh_xdev, fh->fh_xino, fh->fh_dcookie); if(fhp->fh_dverified) @@ -1014,7 +1025,9 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) * Look up the export entry. */ error = nfserr_stale; - exp = exp_get(rqstp->rq_client, fh->fh_xdev, fh->fh_xino); + exp = exp_get(rqstp->rq_client, + u32_to_kdev_t(fh->fh_xdev), + u32_to_ino_t(fh->fh_xino)); if (!exp) /* export entry revoked */ goto out; @@ -1032,17 +1045,17 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) nfsd_setuser(rqstp, exp); /* - * Look up the dentry using the NFS fh. + * Look up the dentry using the NFS file handle. */ error = nfserr_stale; dentry = find_fh_dentry(fh); if (!dentry) goto out; /* - * Note: it's possible that the returned dentry won't be the - * one in the filehandle. We can correct the FH for our use, - * but unfortunately the client will keep sending the broken - * one. Hopefully the lookup will keep patching things up.. + * Note: it's possible the returned dentry won't be the one in the + * file handle. We can correct the file handle for our use, but + * unfortunately the client will keep sending the broken one. Let's + * hope the lookup will keep patching things up. */ fhp->fh_dentry = dentry; fhp->fh_export = exp; @@ -1080,7 +1093,7 @@ out: } /* - * Compose a filehandle for an NFS reply. + * Compose a file handle for an NFS reply. * * Note that when first composed, the dentry may not yet have * an inode. In this case a call to fh_update should be made @@ -1090,6 +1103,7 @@ void fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry) { struct inode * inode = dentry->d_inode; + struct dentry *parent = dentry->d_parent; dprintk("nfsd: fh_compose(exp %x/%ld %s/%s, ino=%ld)\n", exp->ex_dev, exp->ex_ino, @@ -1104,12 +1118,12 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry) fh_init(fhp); fhp->fh_handle.fh_dcookie = dentry; if (inode) { - fhp->fh_handle.fh_ino = inode->i_ino; + fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino); } - fhp->fh_handle.fh_dirino = dentry->d_parent->d_inode->i_ino; - fhp->fh_handle.fh_dev = dentry->d_parent->d_inode->i_dev; - fhp->fh_handle.fh_xdev = exp->ex_dev; - fhp->fh_handle.fh_xino = exp->ex_ino; + fhp->fh_handle.fh_dirino = ino_t_to_u32(parent->d_inode->i_ino); + fhp->fh_handle.fh_dev = kdev_t_to_u32(parent->d_inode->i_dev); + fhp->fh_handle.fh_xdev = kdev_t_to_u32(exp->ex_dev); + fhp->fh_handle.fh_xino = ino_t_to_u32(exp->ex_ino); fhp->fh_dentry = dentry; /* our internal copy */ fhp->fh_export = exp; @@ -1120,7 +1134,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry) } /* - * Update filehandle information after changing a dentry. + * Update file handle information after changing a dentry. */ void fh_update(struct svc_fh *fhp) @@ -1128,44 +1142,50 @@ fh_update(struct svc_fh *fhp) struct dentry *dentry; struct inode *inode; - if (!fhp->fh_dverified) { - printk("fh_update: fh not verified!\n"); - goto out; - } + if (!fhp->fh_dverified) + goto out_bad; dentry = fhp->fh_dentry; inode = dentry->d_inode; - if (!inode) { - printk("fh_update: %s/%s still negative!\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - goto out; - } - fhp->fh_handle.fh_ino = inode->i_ino; + if (!inode) + goto out_negative; + fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino); out: return; + +out_bad: + printk(KERN_ERR "fh_update: fh not verified!\n"); + goto out; +out_negative: + printk(KERN_ERR "fh_update: %s/%s still negative!\n", + dentry->d_parent->d_name.name, dentry->d_name.name); + goto out; } /* - * Release a filehandle. If the filehandle carries a dentry count, + * Release a file handle. If the file handle carries a dentry count, * we add the dentry to the short-term cache rather than release it. */ void fh_put(struct svc_fh *fhp) { + struct dentry * dentry = fhp->fh_dentry; if (fhp->fh_dverified) { - struct dentry * dentry = fhp->fh_dentry; fh_unlock(fhp); fhp->fh_dverified = 0; - if (!dentry->d_count) { - printk("fh_put: %s/%s has d_count 0!\n", - dentry->d_parent->d_name.name, dentry->d_name.name); - return; - } + if (!dentry->d_count) + goto out_bad; if (!dentry->d_inode || !add_to_fhcache(dentry, 0)) { dput(dentry); nfsd_nr_put++; } } + return; + +out_bad: + printk(KERN_ERR "fh_put: %s/%s has d_count 0!\n", + dentry->d_parent->d_name.name, dentry->d_name.name); + return; } /* @@ -1177,7 +1197,7 @@ static int nfsd_d_validate(struct dentry *dentry) { unsigned long dent_addr = (unsigned long) dentry; unsigned long min_addr = PAGE_OFFSET; - unsigned long max_addr = min_addr + (num_physpages << PAGE_SHIFT); + unsigned long max_addr = min_addr + (max_mapnr << PAGE_SHIFT); unsigned long align_mask = 0x0F; unsigned int len; int valid = 0; @@ -1188,6 +1208,11 @@ static int nfsd_d_validate(struct dentry *dentry) goto bad_addr; if ((dent_addr & ~align_mask) != dent_addr) goto bad_align; + /* XXX: Should test here, whether the address doesn't belong to + a physical memory hole on sparc32/sparc64. Then it is not + safe to dereference it. On the other side, the previous + use of num_physpages instead of max_mapnr caused the same + to happen, plus some valid addresses could get rejected. -jj */ /* * Looks safe enough to dereference ... */ @@ -1204,10 +1229,10 @@ out: return valid; bad_addr: - printk("nfsd_d_validate: invalid address %lx\n", dent_addr); + printk(KERN_DEBUG "nfsd_d_validate: invalid address %lx\n", dent_addr); goto out; bad_align: - printk("nfsd_d_validate: unaligned address %lx\n", dent_addr); + printk(KERN_DEBUG "nfsd_d_validate: unaligned address %lx\n", dent_addr); goto out; } @@ -1218,7 +1243,7 @@ bad_align: * This is called when revoking the last export for a * device, so that it can be unmounted cleanly. */ -void nfsd_fh_flush(dev_t dev) +void nfsd_fh_flush(kdev_t dev) { struct fh_entry *fhe; int i, pass = 2; @@ -1260,7 +1285,7 @@ void nfsd_fh_free(void) free_fixup_entry(fp); i++; } - printk("nfsd_fh_free: %d fixups freed\n", i); + printk(KERN_DEBUG "nfsd_fh_free: %d fixups freed\n", i); i = 0; while ((tmp = path_inuse.next) != &path_inuse) { @@ -1269,18 +1294,31 @@ void nfsd_fh_free(void) free_path_entry(pe); i++; } - printk("nfsd_fh_free: %d paths freed\n", i); + printk(KERN_DEBUG "nfsd_fh_free: %d paths freed\n", i); - printk("nfsd_fh_free: verified %d, put %d\n", + printk(KERN_DEBUG "nfsd_fh_free: verified %d, put %d\n", nfsd_nr_verified, nfsd_nr_put); } void nfsd_fh_init(void) { + /* Sanity check */ + extern void __my_nfsfh_is_too_big(void); + if (sizeof(struct nfs_fhbase) > 32) + __my_nfsfh_is_too_big(); + memset(filetable, 0, NFSD_MAXFH*sizeof(struct fh_entry)); memset(dirstable, 0, NFSD_MAXFH*sizeof(struct fh_entry)); INIT_LIST_HEAD(&path_inuse); INIT_LIST_HEAD(&fixup_head); - printk("nfsd_init: initialized fhcache, entries=%lu\n", NFSD_MAXFH); + printk(KERN_DEBUG + "nfsd_init: initialized fhcache, entries=%lu\n", NFSD_MAXFH); + /* + * Display a warning if the ino_t is larger than 32 bits. + */ + if (sizeof(ino_t) > sizeof(__u32)) + printk(KERN_INFO + "NFSD: ino_t is %d bytes, using lower 4 bytes\n", + sizeof(ino_t)); } 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); diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 48ab4cbeb..8b398112d 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -31,13 +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 */ @@ -76,6 +76,41 @@ static struct raparms raparms[FILECACHE_MAX]; 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 @@ -86,7 +121,7 @@ fs_off_limits(struct super_block *sb) } /* - * Check whether directory is a mount point, but it is alright if + * Check whether directory is a mount point, but it is all right if * this is precisely the local mount point being exported. */ static inline int @@ -143,7 +178,7 @@ printk("nfsd_lookup: %s/%s crossed mount point!\n", dparent->d_name.name, name); } /* - * Note: we compose the filehandle now, but as the + * Note: we compose the file handle now, but as the * dentry may be negative, it may need to be updated. */ fh_compose(resfh, exp, dchild); @@ -289,6 +324,9 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, filp->f_mode = wflag? FMODE_WRITE : FMODE_READ; filp->f_dentry = dentry; + if (wflag) + DQUOT_INIT(inode); + err = 0; if (filp->f_op && filp->f_op->open) { err = filp->f_op->open(inode, filp); @@ -325,8 +363,10 @@ nfsd_close(struct file *filp) dentry->d_parent->d_name.name, dentry->d_name.name); if (filp->f_op && filp->f_op->release) filp->f_op->release(inode, filp); - if (filp->f_mode & FMODE_WRITE) + if (filp->f_mode & FMODE_WRITE) { put_write_access(inode); + DQUOT_DROP(inode); + } } /* @@ -511,7 +551,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, interruptible_sleep_on(&inode->i_wait); #else dprintk("nfsd: write defer %d\n", current->pid); - need_resched = 1; + current->need_resched = 1; current->timeout = jiffies + HZ / 100; schedule(); dprintk("nfsd: write resume %d\n", current->pid); @@ -540,8 +580,11 @@ out: } /* - * 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 @@ -551,13 +594,12 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, { 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; @@ -565,67 +607,79 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, 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 file handle 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); + /* + * 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; } - 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); /* - * Update the filehandle to get the new inode info. + * Update the file handle to get the new inode info. */ fh_update(resfhp); @@ -674,6 +728,7 @@ nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size) /* Things look sane, lock and do it. */ fh_lock(fhp); + DQUOT_INIT(inode); newattrs.ia_size = size; newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; err = notify_change(dentry, &newattrs); @@ -683,6 +738,7 @@ nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size) inode->i_op->truncate(inode); } put_write_access(inode); + DQUOT_DROP(inode); fh_unlock(fhp); out_nfserr: if (err) @@ -768,19 +824,29 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, 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); - fh_unlock(fhp); + DQUOT_DROP(dirp); 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; @@ -808,6 +874,10 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, if (err) goto out; + err = nfserr_perm; + if (!len) + goto out; + ddir = ffhp->fh_dentry; dirp = ddir->d_inode; @@ -815,42 +885,48 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, 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); - fh_unlock(ffhp); + DQUOT_DROP(dirp); + 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; @@ -938,11 +1014,16 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, nfsd_double_down(&tdir->i_sem, &fdir->i_sem); /* N.B. check for parent changes after locking?? */ + DQUOT_INIT(fdir); + DQUOT_INIT(tdir); err = fdir->i_op->rename(fdir, odentry, tdir, ndentry); if (!err && EX_ISSYNC(tfhp->fh_export)) { write_inode_now(fdir); write_inode_now(tdir); } + DQUOT_DROP(fdir); + DQUOT_DROP(tdir); + nfsd_double_up(&tdir->i_sem, &fdir->i_sem); dput(ndentry); @@ -986,7 +1067,11 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, 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; if (dirp->i_op && dirp->i_op->rmdir) @@ -996,6 +1081,7 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, if (dirp->i_op && dirp->i_op->unlink) err = dirp->i_op->unlink(dirp, rdentry); } + DQUOT_DROP(dirp); fh_unlock(fhp); dput(rdentry); @@ -1071,10 +1157,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, /* If we didn't fill the buffer completely, we're at EOF */ eof = !cd.eob; - /* Hewlett Packard ignores the eof flag on READDIR. Some - * fs-specific readdir implementations seem to reset f_pos to 0 - * at EOF however, causing an endless loop. */ - if (cd.offset && !eof) + if (cd.offset) *cd.offset = htonl(file.f_pos); p = cd.buffer; |