/* * Convert userspace handle data into a dentry. */ struct dentry * xfs_handle_to_dentry( struct file *parfilp, void __user *uhandle, u32 hlen) { xfs_handle_t handle; struct xfs_fid64 fid; /* * Only allow handle opens under a directory. */ if (!S_ISDIR(file_inode(parfilp)->i_mode)) return ERR_PTR(-ENOTDIR); if (hlen != sizeof(xfs_handle_t)) return ERR_PTR(-EINVAL); if (copy_from_user(&handle, uhandle, hlen)) return ERR_PTR(-EFAULT); if (handle.ha_fid.fid_len != sizeof(handle.ha_fid) - sizeof(handle.ha_fid.fid_len)) return ERR_PTR(-EINVAL); memset(&fid, 0, sizeof(struct fid)); fid.ino = handle.ha_fid.fid_ino; fid.gen = handle.ha_fid.fid_gen; return exportfs_decode_fh(parfilp->f_path.mnt, (struct fid *)&fid, 3, FILEID_INO32_GEN | XFS_FILEID_TYPE_64FLAG, xfs_handle_acceptable, NULL); }
static int do_handle_to_path(int mountdirfd, struct file_handle *handle, struct path *path) { int retval = 0; int handle_dwords; path->mnt = get_vfsmount_from_fd(mountdirfd); if (IS_ERR(path->mnt)) { retval = PTR_ERR(path->mnt); goto out_err; } /* change the handle size to multiple of sizeof(u32) */ handle_dwords = handle->handle_bytes >> 2; path->dentry = exportfs_decode_fh(path->mnt, (struct fid *)handle->f_handle, handle_dwords, handle->handle_type, vfs_dentry_acceptable, NULL); if (IS_ERR(path->dentry)) { retval = PTR_ERR(path->dentry); goto out_mnt; } return 0; out_mnt: mntput(path->mnt); out_err: return retval; }
static struct dentry * au_call_decode_fh(struct vfsmount *h_mnt, __u32 *fh, int fh_len, int fh_type, int (*acceptable)(void *, struct dentry *), void *context) { /* in linux-2.6.24, it takes struct fid * as file handle */ return exportfs_decode_fh(h_mnt, (void *)fh, fh_len, fh_type, acceptable, context); }
static struct dentry *decode_by_path(struct super_block *sb, aufs_bindex_t bindex, ino_t ino, __u32 *fh, int fh_len, struct au_nfsd_si_lock *nsi_lock) { struct dentry *dentry, *h_parent, *root; struct super_block *h_sb; char *pathname, *p; struct vfsmount *h_mnt; struct au_branch *br; int err; struct path path; br = au_sbr(sb, bindex); /* au_br_get(br); */ h_mnt = br->br_mnt; h_sb = h_mnt->mnt_sb; /* todo: call lower fh_to_dentry()? fh_to_parent()? */ h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail), fh_len - Fh_tail, fh[Fh_h_type], h_acceptable, /*context*/NULL); dentry = h_parent; if (unlikely(!h_parent || IS_ERR(h_parent))) { AuWarn1("%s decode_fh failed, %ld\n", au_sbtype(h_sb), PTR_ERR(h_parent)); goto out; } dentry = NULL; if (unlikely(au_test_anon(h_parent))) { AuWarn1("%s decode_fh returned a disconnected dentry\n", au_sbtype(h_sb)); goto out_h_parent; } dentry = ERR_PTR(-ENOMEM); pathname = (void *)__get_free_page(GFP_NOFS); if (unlikely(!pathname)) goto out_h_parent; root = sb->s_root; path.mnt = h_mnt; di_read_lock_parent(root, !AuLock_IR); path.dentry = au_h_dptr(root, bindex); di_read_unlock(root, !AuLock_IR); p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb); dentry = (void *)p; if (IS_ERR(p)) goto out_pathname; si_read_unlock(sb); err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); dentry = ERR_PTR(err); if (unlikely(err)) goto out_relock; dentry = ERR_PTR(-ENOENT); AuDebugOn(au_test_anon(path.dentry)); if (unlikely(!path.dentry->d_inode)) goto out_path; if (ino != path.dentry->d_inode->i_ino) dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL); else dentry = dget(path.dentry); out_path: path_put(&path); out_relock: if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0)) if (!IS_ERR(dentry)) { dput(dentry); dentry = ERR_PTR(-ESTALE); } out_pathname: free_page((unsigned long)pathname); out_h_parent: dput(h_parent); out: /* au_br_put(br); */ AuTraceErrPtr(dentry); return dentry; }
/* * Use the given filehandle to look up the corresponding export and * dentry. On success, the results are used to set fh_export and * fh_dentry. */ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp) { struct knfsd_fh *fh = &fhp->fh_handle; struct fid *fid = NULL, sfid; struct svc_export *exp; struct dentry *dentry; int fileid_type; int data_left = fh->fh_size/4; __be32 error; error = nfserr_stale; if (rqstp->rq_vers > 2) error = nfserr_badhandle; if (rqstp->rq_vers == 4 && fh->fh_size == 0) return nfserr_nofilehandle; if (fh->fh_version == 1) { int len; if (--data_left < 0) return error; if (fh->fh_auth_type != 0) return error; len = key_len(fh->fh_fsid_type) / 4; if (len == 0) return error; if (fh->fh_fsid_type == FSID_MAJOR_MINOR) { /* deprecated, convert to type 3 */ len = key_len(FSID_ENCODE_DEV)/4; fh->fh_fsid_type = FSID_ENCODE_DEV; fh->fh_fsid[0] = new_encode_dev(MKDEV(ntohl(fh->fh_fsid[0]), ntohl(fh->fh_fsid[1]))); fh->fh_fsid[1] = fh->fh_fsid[2]; } data_left -= len; if (data_left < 0) return error; exp = rqst_exp_find(rqstp, fh->fh_fsid_type, fh->fh_auth); fid = (struct fid *)(fh->fh_auth + len); } else { __u32 tfh[2]; dev_t xdev; ino_t xino; if (fh->fh_size != NFS_FHSIZE) return error; /* assume old filehandle format */ xdev = old_decode_dev(fh->ofh_xdev); xino = u32_to_ino_t(fh->ofh_xino); mk_fsid(FSID_DEV, tfh, xdev, xino, 0, NULL); exp = rqst_exp_find(rqstp, FSID_DEV, tfh); } error = nfserr_stale; if (PTR_ERR(exp) == -ENOENT) return error; if (IS_ERR(exp)) return nfserrno(PTR_ERR(exp)); error = nfsd_setuser_and_check_port(rqstp, exp); if (error) goto out; /* * Look up the dentry using the NFS file handle. */ error = nfserr_stale; if (rqstp->rq_vers > 2) error = nfserr_badhandle; if (fh->fh_version != 1) { sfid.i32.ino = fh->ofh_ino; sfid.i32.gen = fh->ofh_generation; sfid.i32.parent_ino = fh->ofh_dirino; fid = &sfid; data_left = 3; if (fh->ofh_dirino == 0) fileid_type = FILEID_INO32_GEN; else fileid_type = FILEID_INO32_GEN_PARENT; } else fileid_type = fh->fh_fileid_type; if (fileid_type == FILEID_ROOT) dentry = dget(exp->ex_path.dentry); else { dentry = exportfs_decode_fh(exp->ex_path.mnt, fid, data_left, fileid_type, nfsd_acceptable, exp); } if (dentry == NULL) goto out; if (IS_ERR(dentry)) { if (PTR_ERR(dentry) != -EINVAL) error = nfserrno(PTR_ERR(dentry)); goto out; } if (S_ISDIR(dentry->d_inode->i_mode) && (dentry->d_flags & DCACHE_DISCONNECTED)) { printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); } fhp->fh_dentry = dentry; fhp->fh_export = exp; nfsd_nr_verified++; return 0; out: exp_put(exp); return error; }
/* * Perform sanity checks on the dentry in a client's file handle. * * Note that the file handle dentry may need to be freed even after * an error return. * * This is only called at the start of an nfsproc call, so fhp points to * a svc_fh which is all 0 except for the over-the-wire file handle. */ __be32 fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) { struct knfsd_fh *fh = &fhp->fh_handle; struct svc_export *exp = NULL; struct dentry *dentry; __be32 error = 0; dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp)); if (!fhp->fh_dentry) { struct fid *fid = NULL, sfid; int fileid_type; int data_left = fh->fh_size/4; error = nfserr_stale; if (rqstp->rq_vers > 2) error = nfserr_badhandle; if (rqstp->rq_vers == 4 && fh->fh_size == 0) return nfserr_nofilehandle; if (fh->fh_version == 1) { int len; if (--data_left<0) goto out; switch (fh->fh_auth_type) { case 0: break; default: goto out; } len = key_len(fh->fh_fsid_type) / 4; if (len == 0) goto out; if (fh->fh_fsid_type == FSID_MAJOR_MINOR) { /* deprecated, convert to type 3 */ len = key_len(FSID_ENCODE_DEV)/4; fh->fh_fsid_type = FSID_ENCODE_DEV; fh->fh_fsid[0] = new_encode_dev(MKDEV(ntohl(fh->fh_fsid[0]), ntohl(fh->fh_fsid[1]))); fh->fh_fsid[1] = fh->fh_fsid[2]; } if ((data_left -= len)<0) goto out; exp = rqst_exp_find(rqstp, fh->fh_fsid_type, fh->fh_auth); fid = (struct fid *)(fh->fh_auth + len); } else { __u32 tfh[2]; dev_t xdev; ino_t xino; if (fh->fh_size != NFS_FHSIZE) goto out; /* assume old filehandle format */ xdev = old_decode_dev(fh->ofh_xdev); xino = u32_to_ino_t(fh->ofh_xino); mk_fsid(FSID_DEV, tfh, xdev, xino, 0, NULL); exp = rqst_exp_find(rqstp, FSID_DEV, tfh); } error = nfserr_stale; if (PTR_ERR(exp) == -ENOENT) goto out; if (IS_ERR(exp)) { error = nfserrno(PTR_ERR(exp)); goto out; } error = nfsd_setuser_and_check_port(rqstp, exp); if (error) goto out; /* * Look up the dentry using the NFS file handle. */ error = nfserr_stale; if (rqstp->rq_vers > 2) error = nfserr_badhandle; if (fh->fh_version != 1) { sfid.i32.ino = fh->ofh_ino; sfid.i32.gen = fh->ofh_generation; sfid.i32.parent_ino = fh->ofh_dirino; fid = &sfid; data_left = 3; if (fh->ofh_dirino == 0) fileid_type = FILEID_INO32_GEN; else fileid_type = FILEID_INO32_GEN_PARENT; } else fileid_type = fh->fh_fileid_type; if (fileid_type == FILEID_ROOT) dentry = dget(exp->ex_dentry); else { dentry = exportfs_decode_fh(exp->ex_mnt, fid, data_left, fileid_type, nfsd_acceptable, exp); } if (dentry == NULL) goto out; if (IS_ERR(dentry)) { if (PTR_ERR(dentry) != -EINVAL) error = nfserrno(PTR_ERR(dentry)); goto out; } if (S_ISDIR(dentry->d_inode->i_mode) && (dentry->d_flags & DCACHE_DISCONNECTED)) { printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n", dentry->d_parent->d_name.name, dentry->d_name.name); } fhp->fh_dentry = dentry; fhp->fh_export = exp; nfsd_nr_verified++; cache_get(&exp->h); } else { /* * just rechecking permissions * (e.g. nfsproc_create calls fh_verify, then nfsd_create * does as well) */ dprintk("nfsd: fh_verify - just checking\n"); dentry = fhp->fh_dentry; exp = fhp->fh_export; cache_get(&exp->h); /* * Set user creds for this exportpoint; necessary even * in the "just checking" case because this may be a * filehandle that was created by fh_compose, and that * is about to be used in another nfsv4 compound * operation. */ error = nfsd_setuser_and_check_port(rqstp, exp); if (error) goto out; } error = nfsd_mode_check(rqstp, dentry->d_inode->i_mode, type); if (error) goto out; if (!(access & MAY_LOCK)) { /* * pseudoflavor restrictions are not enforced on NLM, * which clients virtually always use auth_sys for, * even while using RPCSEC_GSS for NFS. */ error = check_nfsd_access(exp, rqstp); if (error) goto out; } /* Finally, check access permissions. */ error = nfsd_permission(rqstp, exp, dentry, access); if (error) { dprintk("fh_verify: %s/%s permission failure, " "acc=%x, error=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, access, ntohl(error)); } out: if (exp && !IS_ERR(exp)) exp_put(exp); if (error == nfserr_stale) nfsdstats.fh_stale++; return error; }