diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2000-02-04 07:40:19 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2000-02-04 07:40:19 +0000 |
commit | 33263fc5f9ac8e8cb2b22d06af3ce5ac1dd815e4 (patch) | |
tree | 2d1b86a40bef0958a68cf1a2eafbeb0667a70543 /fs/nfsd/vfs.c | |
parent | 216f5f51aa02f8b113aa620ebc14a9631a217a00 (diff) |
Merge with Linux 2.3.32.
Diffstat (limited to 'fs/nfsd/vfs.c')
-rw-r--r-- | fs/nfsd/vfs.c | 520 |
1 files changed, 412 insertions, 108 deletions
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 398d2ebae..2876b8455 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -32,6 +32,10 @@ #include <linux/sunrpc/svc.h> #include <linux/nfsd/nfsd.h> +#ifdef CONFIG_NFSD_V3 +#include <linux/nfs3.h> +#include <linux/nfsd/xdr3.h> +#endif /* CONFIG_NFSD_V3 */ #include <linux/nfsd/nfsfh.h> #include <linux/quotaops.h> @@ -48,7 +52,12 @@ /* Hack until we have a macro check for mandatory locks. */ #ifndef IS_ISMNDLK -#define IS_ISMNDLK(i) (((i)->i_mode & (S_ISGID|S_IXGRP)) == S_ISGID) +/* We must ignore files (but only file) which might have mandatory + * locks on them because there is no way to know if the accesser has + * the lock. + */ +#define IS_ISMNDLK(i) (((i)->i_mode & (S_ISGID|S_IXGRP|S_IFMT)) \ + == (S_ISGID|S_IFREG)) #endif /* Check for dir entries '.' and '..' */ @@ -78,61 +87,46 @@ int nfsd_nservers = 0; static struct raparms * raparml = NULL; static struct raparms * raparm_cache = NULL; + +/* + * We need to do a check-parent every time + * after we have locked the parent - to verify + * that the parent is still our parent and + * that we are still hashed onto it.. + * + * This is required in case two processes race + * on removing (or moving) the same entry: the + * parent lock will serialize them, but the + * other process will be too late.. + * + * Note that this nfsd_check_parent is identical + * the check_parent in linux/fs/namei.c. + */ +#define nfsd_check_parent(dir, dentry) \ + ((dir) == (dentry)->d_parent && !list_empty(&dentry->d_hash)) + /* * 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; + if (nfsd_check_parent(parent_fh->fh_dentry, dchild)) + return 0; -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", + "fh_lock_parent: %s/%s parent changed or child 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 -fs_off_limits(struct super_block *sb) -{ - return !sb || sb->s_magic == NFS_SUPER_MAGIC - || sb->s_magic == PROC_SUPER_MAGIC; + fh_unlock(parent_fh); + return nfserr_noent; } -/* - * 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 -nfsd_iscovered(struct dentry *dentry, struct svc_export *exp) -{ - return (dentry != dentry->d_covers && - dentry != exp->ex_dentry); -} /* * Look up one component of a pathname. @@ -169,12 +163,7 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, if (err) goto out; #endif - err = nfserr_noent; - if (fs_off_limits(dparent->d_sb)) - goto out; err = nfserr_acces; - if (nfsd_iscovered(dparent, exp)) - goto out; /* Lookup the name, but don't follow links */ dchild = lookup_dentry(name, dget(dparent), 0); @@ -250,6 +239,32 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap) inode = dentry->d_inode; err = inode_change_ok(inode, iap); + /* could be a "touch" (utimes) request where the user is not the owner but does + * have write permission. In this case the user should be allowed to set + * both times to the current time. We could just assume any such SETATTR + * is intended to set the times to "now", but we do a couple of simple tests + * to increase our confidence. + */ +#define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET) +#define MAX_TOUCH_TIME_ERROR (30*60) + if (err + && (iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET + && iap->ia_mtime == iap->ia_ctime + ) { + /* looks good. now just make sure time is in the right ballpark. + * solaris, at least, doesn't seem to care what the time request is + */ + time_t delta = iap->ia_atime - CURRENT_TIME; + if (delta<0) delta = -delta; + if (delta < MAX_TOUCH_TIME_ERROR) { + /* turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME + * this will cause notify_change to setthese times to "now" + */ + iap->ia_valid &= ~BOTH_TIME_SET; + err = inode_change_ok(inode, iap); + } + } + if (err) goto out_nfserr; @@ -321,6 +336,9 @@ printk("nfsd_setattr: size change??\n"); if (EX_ISSYNC(fhp->fh_export)) write_inode_now(inode); err = 0; + + /* Don't unlock inode; the nfssvc_release functions are supposed + * to do this. */ out: return err; @@ -329,6 +347,84 @@ out_nfserr: goto out; } +#ifdef CONFIG_NFSD_V3 +/* + * Check server access rights to a file system object + */ +struct accessmap { + u32 access; + int how; +}; +static struct accessmap nfs3_regaccess[] = { + { NFS3_ACCESS_READ, MAY_READ }, + { NFS3_ACCESS_EXECUTE, MAY_EXEC }, + { NFS3_ACCESS_MODIFY, MAY_WRITE|MAY_TRUNC }, + { NFS3_ACCESS_EXTEND, MAY_WRITE }, + + { 0, 0 } +}; + +static struct accessmap nfs3_diraccess[] = { + { NFS3_ACCESS_READ, MAY_READ }, + { NFS3_ACCESS_LOOKUP, MAY_EXEC }, + { NFS3_ACCESS_MODIFY, MAY_EXEC|MAY_WRITE|MAY_TRUNC }, + { NFS3_ACCESS_EXTEND, MAY_EXEC|MAY_WRITE }, + { NFS3_ACCESS_DELETE, MAY_REMOVE }, + + { 0, 0 } +}; + +static struct accessmap nfs3_anyaccess[] = { + /* XXX: should we try to cover read/write here for clients that + * rely on us to do their access checking for special files? */ + + { 0, 0 } +}; + +int +nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access) +{ + struct accessmap *map; + struct svc_export *export; + struct dentry *dentry; + u32 query, result = 0; + int error; + + error = fh_verify(rqstp, fhp, 0, MAY_NOP); + if (error < 0) + goto out; + + export = fhp->fh_export; + dentry = fhp->fh_dentry; + + if (S_ISREG(dentry->d_inode->i_mode)) { + map = nfs3_regaccess; + } else if (S_ISDIR(dentry->d_inode->i_mode)) { + map = nfs3_diraccess; + } else { + map = nfs3_anyaccess; + } + + query = *access; + while (map->access) { + if (map->access & query) { + error = nfsd_permission(export, dentry, map->how); + if (error == 0) + result |= map->access; + else if (error != -EPERM) + goto out; + } + map++; + } + *access = result; + +out: + return error; +} +#endif + + + /* * Open an existing file or directory. * The wflag argument indicates write access. @@ -415,11 +511,28 @@ nfsd_close(struct file *filp) /* * Sync a file + * As this calls fsync (not fdatasync) there is no need for a write_inode + * after it. */ void -nfsd_sync(struct inode *inode, struct file *filp) +nfsd_sync(struct file *filp) { + dprintk("nfsd: sync file %s\n", filp->f_dentry->d_name.name); + down(&filp->f_dentry->d_inode->i_sem); filp->f_op->fsync(filp, filp->f_dentry); + up(&filp->f_dentry->d_inode->i_sem); +} + +void +nfsd_sync_dir(struct dentry *dp) +{ + struct inode *inode = dp->d_inode; + int (*fsync) (struct file *, struct dentry *); + + if (inode->i_op->default_file_ops + && (fsync = inode->i_op->default_file_ops->fsync)) { + fsync(NULL, dp); + } } /* @@ -550,11 +663,21 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, * Request sync writes if * - the sync export option has been set, or * - the client requested O_SYNC behavior (NFSv3 feature). + * - The file system doesn't support fsync(). * When gathered writes have been configured for this volume, * flushing the data to disk is handled separately below. */ +#ifdef CONFIG_NFSD_V3 + if (rqstp->rq_vers == 2) + stable = EX_ISSYNC(exp); + else if (file.f_op->fsync == 0) + stable = 1; + if (stable && !EX_WGATHER(exp)) + file.f_flags |= O_SYNC; +#else if ((stable || (stable = EX_ISSYNC(exp))) && !EX_WGATHER(exp)) file.f_flags |= O_SYNC; +#endif /* CONFIG_NFSD_V3 */ file.f_pos = offset; /* set write offset */ @@ -574,7 +697,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, /* clear setuid/setgid flag after write */ if (err >= 0 && (inode->i_mode & (S_ISUID | S_ISGID))) { struct iattr ia; - kernel_cap_t saved_cap; + kernel_cap_t saved_cap = 0; ia.ia_valid = ATTR_MODE; ia.ia_mode = inode->i_mode & ~(S_ISUID | S_ISGID); @@ -609,6 +732,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, interruptible_sleep_on_timeout(&inode->i_wait, 10 * HZ / 1000); #else dprintk("nfsd: write defer %d\n", current->pid); +/* FIXME: Olaf commented this out [gam3] */ schedule_timeout((HZ+99)/100); dprintk("nfsd: write resume %d\n", current->pid); #endif @@ -616,15 +740,14 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, if (inode->i_state & I_DIRTY) { dprintk("nfsd: write sync %d\n", current->pid); - nfsd_sync(inode, &file); - write_inode_now(inode); + nfsd_sync(&file); } wake_up(&inode->i_wait); last_ino = inode->i_ino; last_dev = inode->i_dev; } - dprintk("nfsd: write complete\n"); + dprintk("nfsd: write complete err=%d\n", err); if (err >= 0) err = 0; else @@ -635,6 +758,36 @@ out: return err; } + +#ifdef CONFIG_NFSD_V3 +/* + * Commit all pendig writes to stable storage. + * Strictly speaking, we could sync just indicated the file region here, + * but there's currently no way we can ask the VFS to do so. + * + * We lock the file to make sure we return full WCC data to the client. + */ +int +nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, + off_t offset, unsigned long count) +{ + struct file file; + int err; + + if ((err = nfsd_open(rqstp, fhp, S_IFREG, OPEN_WRITE, &file)) != 0) + return err; + + if (file.f_op && file.f_op->fsync) { + nfsd_sync(&file); + } else { + err = nfserr_notsupp; + } + + nfsd_close(&file); + return err; +} +#endif /* CONFIG_NFSD_V3 */ + /* * Create a file (regular, directory, device, fifo); UNIX sockets * not yet implemented. @@ -672,6 +825,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, * If it has, the parent directory should already be locked. */ if (!resfhp->fh_dverified) { + /* called from nfsd_proc_mkdir, or possibly nfsd3_proc_create */ dchild = lookup_dentry(fname, dget(dentry), 0); err = PTR_ERR(dchild); if (IS_ERR(dchild)) @@ -682,20 +836,24 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, if (err) goto out; } else { + /* called from nfsd_proc_create */ dchild = resfhp->fh_dentry; - if (!fhp->fh_locked) + if (!fhp->fh_locked) { + /* not actually possible */ printk(KERN_ERR "nfsd_create: parent %s/%s not locked!\n", dentry->d_parent->d_name.name, dentry->d_name.name); + err = -EIO; + goto out; + } } /* * Make sure the child dentry is still negative ... */ err = nfserr_exist; if (dchild->d_inode) { - printk(KERN_WARNING - "nfsd_create: dentry %s/%s not negative!\n", + dprintk("nfsd_create: dentry %s/%s not negative!\n", dentry->d_name.name, dchild->d_name.name); goto out; } @@ -710,8 +868,6 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, break; case S_IFDIR: opfunc = (nfsd_dirop_t) dirp->i_op->mkdir; - /* Odd, indeed, but filesystems did it anyway */ - iap->ia_mode &= (S_IRWXUGO|S_ISVTX) & ~current->fs->umask; break; case S_IFCHR: case S_IFBLK: @@ -725,12 +881,16 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, case S_IFSOCK: opfunc = dirp->i_op->mknod; break; + default: + printk("nfsd: bad file type %o in nfsd_create\n", type); + err = nfserr_inval; } if (!opfunc) goto out; if (!(iap->ia_valid & ATTR_MODE)) iap->ia_mode = 0; + iap->ia_mode = (iap->ia_mode & S_IALLUGO) | type; /* * Call the dir op function to create the object. @@ -740,8 +900,10 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, if (err < 0) goto out_nfserr; - if (EX_ISSYNC(fhp->fh_export)) - write_inode_now(dirp); + if (EX_ISSYNC(fhp->fh_export)) { + nfsd_sync_dir(dentry); + write_inode_now(dchild->d_inode); + } /* * Update the file handle to get the new inode info. @@ -764,6 +926,126 @@ out_nfserr: goto out; } +#ifdef CONFIG_NFSD_V3 +/* + * NFSv3 version of nfsd_create + */ +int +nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp, + char *fname, int flen, struct iattr *iap, + struct svc_fh *resfhp, int createmode, u32 *verifier) +{ + struct dentry *dentry, *dchild; + struct inode *dirp; + 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; + + 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(!dirp->i_op->create) + goto out; + + /* + * Compose the response file handle. + */ + dchild = lookup_dentry(fname, dget(dentry), 0); + err = PTR_ERR(dchild); + if(IS_ERR(dchild)) + goto out_nfserr; + fh_compose(resfhp, fhp->fh_export, dchild); + + /* + * We must lock the directory before we check for the inode. + */ + err = fh_lock_parent(fhp, dchild); + if (err) + goto out; + + if (dchild->d_inode) { + err = 0; + + if (resfhp->fh_handle.fh_ino == 0) + /* inode might have been instantiated while we slept */ + fh_update(resfhp); + + switch (createmode) { + case NFS3_CREATE_UNCHECKED: + if (! S_ISREG(dchild->d_inode->i_mode)) + err = nfserr_exist; + else { + iap->ia_valid &= ATTR_SIZE; + goto set_attr; + } + break; + case NFS3_CREATE_EXCLUSIVE: + if ( dchild->d_inode->i_mtime == verifier[0] + && dchild->d_inode->i_atime == verifier[1] + && dchild->d_inode->i_mode == S_IFREG + && dchild->d_inode->i_size == 0 ) + break; + /* fallthru */ + case NFS3_CREATE_GUARDED: + err = nfserr_exist; + } + goto out; + } + + err = dirp->i_op->create(dirp, dchild, iap->ia_mode); + if (err < 0) + goto out_nfserr; + + if (EX_ISSYNC(fhp->fh_export)) { + nfsd_sync_dir(dentry); + /* setattr will sync the child (or not) */ + } + + /* + * Update the filehandle to get the new inode info. + */ + fh_update(resfhp); + err = 0; + + if (createmode == NFS3_CREATE_EXCLUSIVE) { + /* Cram the verifier into atime/mtime */ + iap->ia_valid = ATTR_MTIME|ATTR_ATIME|ATTR_MTIME_SET|ATTR_ATIME_SET; + iap->ia_mtime = verifier[0]; + iap->ia_atime = verifier[1]; + } + + /* Set file attributes. Mode has already been set and + * setting uid/gid works only for root. Irix appears to + * send along the gid when it tries to implement setgid + * directories via NFS. Clear out all that cruft. + */ + set_attr: + if ((iap->ia_valid &= ~(ATTR_UID|ATTR_GID|ATTR_MODE)) != 0) + err = nfsd_setattr(rqstp, resfhp, iap); + + out: + fh_unlock(fhp); + return err; + + out_nfserr: + err = nfserrno(-err); + goto out; +} +#endif /* CONFIG_NFSD_V3 */ + /* * Truncate a file. * The calling routines must make sure to update the ctime @@ -779,7 +1061,7 @@ nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size) struct inode *inode; struct iattr newattrs; int err; - kernel_cap_t saved_cap; + kernel_cap_t saved_cap = 0; err = fh_verify(rqstp, fhp, S_IFREG, MAY_WRITE | MAY_TRUNC); if (err) @@ -815,6 +1097,8 @@ nfsd_truncate(struct svc_rqst *rqstp, struct svc_fh *fhp, unsigned long size) if (current->fsuid != 0) current->cap_effective = saved_cap; put_write_access(inode); + if (EX_ISSYNC(fhp->fh_export)) + nfsd_sync_dir(dentry); fh_unlock(fhp); out_nfserr: if (err) @@ -848,7 +1132,10 @@ 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()?? */ + /* N.B. Why does this call need a get_fs()?? + * Remove the set_fs and watch the fireworks:-) --okir + */ + oldfs = get_fs(); set_fs(KERNEL_DS); err = inode->i_op->readlink(dentry, buf, *lenp); set_fs(oldfs); @@ -873,7 +1160,8 @@ int nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *fname, int flen, char *path, int plen, - struct svc_fh *resfhp) + struct svc_fh *resfhp, + struct iattr *iap) { struct dentry *dentry, *dnew; struct inode *dirp; @@ -889,8 +1177,6 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, dentry = fhp->fh_dentry; err = nfserr_perm; - if (nfsd_iscovered(dentry, fhp->fh_export)) - goto out; dirp = dentry->d_inode; if (!dirp->i_op || !dirp->i_op->symlink) goto out; @@ -913,7 +1199,16 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, err = dirp->i_op->symlink(dirp, dnew, path); if (!err) { if (EX_ISSYNC(fhp->fh_export)) - write_inode_now(dirp); + nfsd_sync_dir(dentry); + if (iap) { + iap->ia_valid &= ATTR_MODE /* ~(ATTR_MODE|ATTR_UID|ATTR_GID)*/; + if (iap->ia_valid) { + iap->ia_valid |= ATTR_CTIME; + err = notify_change(dnew, iap); + if (!err && EX_ISSYNC(fhp->fh_export)) + write_inode_now(dentry->d_inode); + } + } } else err = nfserrno(-err); } @@ -945,7 +1240,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_CREATE); if (err) goto out; - err = fh_verify(rqstp, tfhp, S_IFREG, MAY_NOP); + err = fh_verify(rqstp, tfhp, -S_IFDIR, MAY_NOP); if (err) goto out; @@ -974,10 +1269,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, dold = tfhp->fh_dentry; dest = dold->d_inode; - err = nfserr_acces; - if (nfsd_iscovered(ddir, ffhp->fh_export)) - goto out_unlock; - /* FIXME: nxdev for NFSv3 */ + err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev; if (dirp->i_dev != dest->i_dev) goto out_unlock; @@ -991,7 +1283,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, err = dirp->i_op->link(dold, dirp, dnew); if (!err) { if (EX_ISSYNC(ffhp->fh_export)) { - write_inode_now(dirp); + nfsd_sync_dir(ddir); write_inode_now(dest); } } else @@ -1010,29 +1302,12 @@ out_nfserr: } /* - * We need to do a check-parent every time - * after we have locked the parent - to verify - * that the parent is still our parent and - * that we are still hashed onto it.. - * - * This is required in case two processes race - * on removing (or moving) the same entry: the - * parent lock will serialize them, but the - * other process will be too late.. - * - * Note that this nfsd_check_parent is different - * than the one in linux/include/dcache_func.h. - */ -#define nfsd_check_parent(dir, dentry) \ - ((dir) == (dentry)->d_parent->d_inode && !list_empty(&dentry->d_hash)) - -/* * This follows the model of double_lock() in the VFS. */ static inline void nfsd_double_down(struct semaphore *s1, struct semaphore *s2) { if (s1 != s2) { - if ((unsigned long) s1 > (unsigned long) s2) { + if ((unsigned long) s1 < (unsigned long) s2) { struct semaphore *tmp = s1; s1 = s2; s2 = tmp; @@ -1074,6 +1349,10 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, tdentry = tfhp->fh_dentry; tdir = tdentry->d_inode; + err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev; + if (fdir->i_dev != tdir->i_dev) + goto out; + /* N.B. We shouldn't need this ... dentry layer handles it */ err = nfserr_perm; if (!flen || (fname[0] == '.' && @@ -1100,19 +1379,35 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, * Lock the parent directories. */ nfsd_double_down(&tdir->i_sem, &fdir->i_sem); + +#ifdef CONFIG_NFSD_V3 + /* Fill in the pre-op attr for the wcc data for both + * tdir and fdir + */ + fill_pre_wcc(ffhp); + fill_pre_wcc(tfhp); +#endif /* CONFIG_NFSD_V3 */ + err = -ENOENT; /* GAM3 check for parent changes after locking. */ - if (nfsd_check_parent(fdir, odentry) && - nfsd_check_parent(tdir, ndentry)) { + if (nfsd_check_parent(fdentry, odentry) && + nfsd_check_parent(tdentry, ndentry)) { err = vfs_rename(fdir, odentry, tdir, ndentry); if (!err && EX_ISSYNC(tfhp->fh_export)) { - write_inode_now(fdir); - write_inode_now(tdir); + nfsd_sync_dir(tdentry); + nfsd_sync_dir(fdentry); } } else dprintk("nfsd: Caught race in nfsd_rename"); +#ifdef CONFIG_NFSD_V3 + /* Fill in the post-op attr for the wcc data for both + * tdir and fdir + */ + fill_post_wcc(ffhp); + fill_post_wcc(tfhp); +#endif /* CONFIG_NFSD_V3 */ nfsd_double_up(&tdir->i_sem, &fdir->i_sem); dput(ndentry); @@ -1182,18 +1477,23 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, rdentry->d_count++; nfsd_double_down(&dirp->i_sem, &rdentry->d_inode->i_sem); - if (!fhp->fh_pre_mtime) - fhp->fh_pre_mtime = dirp->i_mtime; + +#ifdef CONFIG_NFSD_V3 + fill_pre_wcc(fhp); +#else fhp->fh_locked = 1; +#endif /* CONFIG_NFSD_V3 */ err = -ENOENT; - if (nfsd_check_parent(dirp, rdentry)) + if (nfsd_check_parent(dentry, rdentry)) err = vfs_rmdir(dirp, rdentry); rdentry->d_count--; - if (!fhp->fh_post_version) - fhp->fh_post_version = dirp->i_version; +#ifdef CONFIG_NFSD_V3 + fill_post_wcc(fhp); +#else fhp->fh_locked = 0; +#endif /* CONFIG_NFSD_V3 */ nfsd_double_up(&dirp->i_sem, &rdentry->d_inode->i_sem); dput(rdentry); @@ -1201,8 +1501,11 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, if (err) goto out_nfserr; - if (EX_ISSYNC(fhp->fh_export)) - write_inode_now(dirp); + if (EX_ISSYNC(fhp->fh_export)) { + down(&dentry->d_inode->i_sem); + nfsd_sync_dir(dentry); + up(&dentry->d_inode->i_sem); + } out: return err; @@ -1213,10 +1516,11 @@ out_nfserr: /* * Read entries from a directory. + * The verifier is an NFSv3 thing we ignore for now. */ int nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, - encode_dent_fn func, u32 *buffer, int *countp) + encode_dent_fn func, u32 *buffer, int *countp, u32 *verf) { struct inode *inode; u32 *p; @@ -1242,6 +1546,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, cd.rqstp = rqstp; cd.buffer = buffer; cd.buflen = *countp; /* count of words */ + cd.dirfh = fhp; /* * Read the directory entries. This silly loop is necessary because @@ -1271,8 +1576,14 @@ 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; - if (cd.offset) - *cd.offset = htonl(file.f_pos); + if (cd.offset) { +#ifdef CONFIG_NFSD_V3 + if (rqstp->rq_vers == 3) + (void)enc64(cd.offset, file.f_pos); + else +#endif /* CONFIG_NFSD_V3 */ + *cd.offset = htonl(file.f_pos); + } p = cd.buffer; *p++ = 0; /* no more entries */ @@ -1334,7 +1645,7 @@ nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) { struct inode *inode = dentry->d_inode; int err; - kernel_cap_t saved_cap; + kernel_cap_t saved_cap = 0; if (acc == MAY_NOP) return 0; @@ -1353,17 +1664,10 @@ nfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc) dprintk(" owner %d/%d user %d/%d\n", inode->i_uid, inode->i_gid, current->fsuid, current->fsgid); #endif -#ifndef CONFIG_NFSD_SUN - if (dentry->d_mounts != dentry) { - return nfserr_perm; - } -#endif if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) { if (EX_RDONLY(exp) || IS_RDONLY(inode)) return nfserr_rofs; - if (S_ISDIR(inode->i_mode) && nfsd_iscovered(dentry, exp)) - return nfserr_perm; if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode)) return nfserr_perm; } |