summaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1998-08-25 09:12:35 +0000
committerRalf Baechle <ralf@linux-mips.org>1998-08-25 09:12:35 +0000
commitc7fc24dc4420057f103afe8fc64524ebc25c5d37 (patch)
tree3682407a599b8f9f03fc096298134cafba1c9b2f /fs/nfsd
parent1d793fade8b063fde3cf275bf1a5c2d381292cd9 (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/Makefile6
-rw-r--r--fs/nfsd/export.c28
-rw-r--r--fs/nfsd/nfsctl.c2
-rw-r--r--fs/nfsd/nfsfh.c194
-rw-r--r--fs/nfsd/nfsproc.c101
-rw-r--r--fs/nfsd/vfs.c221
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;