summaryrefslogtreecommitdiffstats
path: root/fs/nfsd/nfsfh.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfsd/nfsfh.c')
-rw-r--r--fs/nfsd/nfsfh.c201
1 files changed, 140 insertions, 61 deletions
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index 084a17d9b..4a7e63dcb 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -328,7 +328,7 @@ static struct dentry *splice(struct dentry *child, struct dentry *parent)
* connection if made.
*/
static struct dentry *
-find_fh_dentry(struct super_block *sb, struct knfs_fh *fh, int needpath)
+find_fh_dentry(struct super_block *sb, ino_t ino, int generation, ino_t dirino, int needpath)
{
struct dentry *dentry, *result = NULL;
struct dentry *tmp;
@@ -347,7 +347,7 @@ find_fh_dentry(struct super_block *sb, struct knfs_fh *fh, int needpath)
* Attempt to find the inode.
*/
retry:
- result = nfsd_iget(sb, fh->fh_ino, fh->fh_generation);
+ result = nfsd_iget(sb, ino, generation);
err = PTR_ERR(result);
if (IS_ERR(result))
goto err_out;
@@ -370,10 +370,10 @@ find_fh_dentry(struct super_block *sb, struct knfs_fh *fh, int needpath)
/* It's a directory, or we are required to confirm the file's
* location in the tree.
*/
- dprintk("nfs_fh: need to look harder for %d/%d\n",sb->s_dev,fh->fh_ino);
+ dprintk("nfs_fh: need to look harder for %d/%ld\n",sb->s_dev,ino);
down(&sb->s_nfsd_free_path_sem);
- /* claiming the semaphore might have allow things to get fixed up */
+ /* claiming the semaphore might have allowed things to get fixed up */
if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {
up(&sb->s_nfsd_free_path_sem);
return result;
@@ -383,11 +383,11 @@ find_fh_dentry(struct super_block *sb, struct knfs_fh *fh, int needpath)
found = 0;
if (!S_ISDIR(result->d_inode->i_mode)) {
nfsdstats.fh_nocache_nondir++;
- if (fh->fh_dirino == 0)
+ if (dirino == 0)
goto err_result; /* don't know how to find parent */
else {
- /* need to iget fh->fh_dirino and make sure this inode is in that directory */
- dentry = nfsd_iget(sb, fh->fh_dirino, 0);
+ /* need to iget dirino and make sure this inode is in that directory */
+ dentry = nfsd_iget(sb, dirino, 0);
err = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto err_result;
@@ -490,38 +490,58 @@ err_out:
u32
fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
{
- struct knfs_fh *fh = &fhp->fh_handle;
+ struct knfsd_fh *fh = &fhp->fh_handle;
struct svc_export *exp;
struct dentry *dentry;
struct inode *inode;
u32 error = 0;
- dprintk("nfsd: fh_verify(exp %s/%u file (%s/%u dir %u)\n",
- kdevname(fh->fh_xdev),
- fh->fh_xino,
- kdevname(fh->fh_dev),
- fh->fh_ino,
- fh->fh_dirino);
+ dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp));
if (!fhp->fh_dverified) {
- /*
- * Security: Check that the fh is internally consistant (from <gam3@acm.org>)
- */
- if (fh->fh_dev != fh->fh_xdev) {
- printk("fh_verify: Security: export on other device (%s, %s).\n",
- kdevname(fh->fh_dev), kdevname(fh->fh_xdev));
- error = nfserr_stale;
- nfsdstats.fh_stale++;
- goto out;
+ kdev_t xdev;
+ ino_t xino;
+ __u32 *datap=NULL;
+ int data_left = fh->fh_size/4;
+ int nfsdev;
+ error = nfserr_stale;
+#if CONFIG_NFSD_V3
+ if (rqstp->rq_vers == 3)
+ error = nfserr_badhandle;
+#endif
+ if (fh->fh_version == 1) {
+
+ datap = fh->fh_auth;
+ if (--data_left<0) goto out;
+ switch (fh->fh_auth_type) {
+ case 0: break;
+ default: goto out;
+ }
+
+ switch (fh->fh_fsid_type) {
+ case 0:
+ if ((data_left-=2)<0) goto out;
+ nfsdev = ntohl(*datap++);
+ xdev = MKDEV(nfsdev>>16, nfsdev&0xFFFF);
+ xino = *datap++;
+ break;
+ default:
+ goto out;
+ }
+ } else {
+ if (fh->fh_size != NFS_FHSIZE)
+ goto out;
+ /* assume old filehandle format */
+ xdev = u32_to_kdev_t(fh->ofh_xdev);
+ xino = u32_to_ino_t(fh->ofh_xino);
}
/*
* Look up the export entry.
*/
error = nfserr_stale;
- exp = exp_get(rqstp->rq_client,
- u32_to_kdev_t(fh->fh_xdev),
- u32_to_ino_t(fh->fh_xino));
+ exp = exp_get(rqstp->rq_client, xdev, xino);
+
if (!exp) {
/* export entry revoked */
nfsdstats.fh_stale++;
@@ -544,13 +564,44 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
/*
* Look up the dentry using the NFS file handle.
*/
+ error = nfserr_stale;
+#if CONFIG_NFSD_V3
+ if (rqstp->rq_vers == 3)
+ error = nfserr_badhandle;
+#endif
- dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb,
- fh,
- !(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
+ if (fh->fh_version == 1) {
+ /* if fileid_type != 0, and super_operations provide fh_to_dentry lookup,
+ * then should use that */
+ switch (fh->fh_fileid_type) {
+ case 0:
+ dentry = dget(exp->ex_dentry);
+ break;
+ case 1:
+ if ((data_left-=2)<0) goto out;
+ dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb,
+ datap[0], datap[1],
+ 0,
+ !(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
+ break;
+ case 2:
+ if ((data_left-=3)<0) goto out;
+ dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb,
+ datap[0], datap[1],
+ datap[2],
+ !(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
+ break;
+ default: goto out;
+ }
+ } else {
+ dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb,
+ fh->ofh_ino, fh->ofh_generation,
+ fh->ofh_dirino,
+ !(exp->ex_flags & NFSEXP_NOSUBTREECHECK));
+ }
if (IS_ERR(dentry)) {
- error = nfserrno(-PTR_ERR(dentry));
+ error = nfserrno(PTR_ERR(dentry));
goto out;
}
#ifdef NFSD_PARANOIA
@@ -652,72 +703,96 @@ out:
* an inode. In this case a call to fh_update should be made
* before the fh goes out on the wire ...
*/
-void
+inline int _fh_update(struct dentry *dentry, struct svc_export *exp,
+ __u32 **datapp, int maxsize)
+{
+ __u32 *datap= *datapp;
+ if (dentry == exp->ex_dentry)
+ return 0;
+ /* if super_operations provides dentry_to_fh lookup, should use that */
+
+ *datap++ = ino_t_to_u32(dentry->d_inode->i_ino);
+ *datap++ = dentry->d_inode->i_generation;
+ if (S_ISDIR(dentry->d_inode->i_mode) || (exp->ex_flags & NFSEXP_NOSUBTREECHECK)){
+ *datapp = datap;
+ return 1;
+ }
+ *datap++ = ino_t_to_u32(dentry->d_parent->d_inode->i_ino);
+ *datapp = datap;
+ return 2;
+}
+
+int
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;
+ __u32 *datap;
dprintk("nfsd: fh_compose(exp %x/%ld %s/%s, ino=%ld)\n",
exp->ex_dev, (long) exp->ex_ino,
parent->d_name.name, dentry->d_name.name,
(inode ? inode->i_ino : 0));
- /*
- * N.B. We shouldn't need to init the fh -- the call to fh_compose
- * may not be done on error paths, but the cleanup must call fh_put.
- * Fix this soon!
- */
if (fhp->fh_dverified || fhp->fh_locked || fhp->fh_dentry) {
printk(KERN_ERR "fh_compose: fh %s/%s not initialized!\n",
parent->d_name.name, dentry->d_name.name);
}
- fh_init(fhp);
-
- 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_handle.fh_dcookie = (struct dentry *)0xfeebbaca;
- if (inode) {
- fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino);
- fhp->fh_handle.fh_generation = inode->i_generation;
- if (S_ISDIR(inode->i_mode) || (exp->ex_flags & NFSEXP_NOSUBTREECHECK))
- fhp->fh_handle.fh_dirino = 0;
- }
+ if (fhp->fh_maxsize < NFS_FHSIZE)
+ printk(KERN_ERR "fh_compose: called with maxsize %d! %s/%s\n",
+ fhp->fh_maxsize, parent->d_name.name, dentry->d_name.name);
fhp->fh_dentry = dentry; /* our internal copy */
fhp->fh_export = exp;
+ fhp->fh_handle.fh_version = 1;
+ fhp->fh_handle.fh_auth_type = 0;
+ fhp->fh_handle.fh_fsid_type = 0;
+ datap = fhp->fh_handle.fh_auth+0;
+ /* fsid_type 0 == 2byte major, 2byte minor, 4byte inode */
+ *datap++ = htonl((MAJOR(exp->ex_dev)<<16)| MINOR(exp->ex_dev));
+ *datap++ = ino_t_to_u32(exp->ex_ino);
+
+ if (inode)
+ fhp->fh_handle.fh_fileid_type =
+ _fh_update(dentry, exp, &datap, fhp->fh_maxsize-3);
+
+ fhp->fh_handle.fh_size = (datap-fhp->fh_handle.fh_auth+1)*4;
+
+
/* We stuck it there, we know it's good. */
fhp->fh_dverified = 1;
nfsd_nr_verified++;
+ if (fhp->fh_handle.fh_fileid_type == 255)
+ return nfserr_opnotsupp;
+ return 0;
}
/*
* Update file handle information after changing a dentry.
- * This is only called by nfsd_create
+ * This is only called by nfsd_create, nfsd_create_v3 and nfsd_proc_create
*/
-void
+int
fh_update(struct svc_fh *fhp)
{
struct dentry *dentry;
- struct inode *inode;
-
+ __u32 *datap;
+
if (!fhp->fh_dverified)
goto out_bad;
dentry = fhp->fh_dentry;
- inode = dentry->d_inode;
- if (!inode)
+ if (!dentry->d_inode)
goto out_negative;
- fhp->fh_handle.fh_ino = ino_t_to_u32(inode->i_ino);
- fhp->fh_handle.fh_generation = inode->i_generation;
- if (S_ISDIR(inode->i_mode) || (fhp->fh_export->ex_flags & NFSEXP_NOSUBTREECHECK))
- fhp->fh_handle.fh_dirino = 0;
-
+ if (fhp->fh_handle.fh_fileid_type != 0)
+ goto out_uptodate;
+ datap = fhp->fh_handle.fh_auth+
+ fhp->fh_handle.fh_size/4 -1;
+ fhp->fh_handle.fh_fileid_type =
+ _fh_update(dentry, fhp->fh_export, &datap, fhp->fh_maxsize-fhp->fh_handle.fh_size);
+ fhp->fh_handle.fh_size = (datap-fhp->fh_handle.fh_auth+1)*4;
out:
- return;
+ return 0;
out_bad:
printk(KERN_ERR "fh_update: fh not verified!\n");
@@ -726,6 +801,10 @@ out_negative:
printk(KERN_ERR "fh_update: %s/%s still negative!\n",
dentry->d_parent->d_name.name, dentry->d_name.name);
goto out;
+out_uptodate:
+ printk(KERN_ERR "fh_update: %s/%s already up-to-date!\n",
+ dentry->d_parent->d_name.name, dentry->d_name.name);
+ goto out;
}
/*