/* * Set the Access and/or Default ACL of a file. */ static __be32 nfsacld_proc_setacl(struct svc_rqst * rqstp, struct nfsd3_setaclargs *argp, struct nfsd_attrstat *resp) { svc_fh *fh; __be32 nfserr = 0; dprintk("nfsd: SETACL(2acl) %s\n", SVCFH_fmt(&argp->fh)); fh = fh_copy(&resp->fh, &argp->fh); nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR); if (!nfserr) { nfserr = nfserrno( nfsd_set_posix_acl( fh, ACL_TYPE_ACCESS, argp->acl_access) ); } if (!nfserr) { nfserr = nfserrno( nfsd_set_posix_acl( fh, ACL_TYPE_DEFAULT, argp->acl_default) ); } /* argp->acl_{access,default} may have been allocated in nfssvc_decode_setaclargs. */ posix_acl_release(argp->acl_access); posix_acl_release(argp->acl_default); return nfserr; }
/* * Check file attributes */ static __be32 nfsacld_proc_getattr(struct svc_rqst * rqstp, struct nfsd_fhandle *argp, struct nfsd_attrstat *resp) { dprintk("nfsd: GETATTR %s\n", SVCFH_fmt(&argp->fh)); fh_copy(&resp->fh, &argp->fh); return fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); }
/* * Get the Access and/or Default ACL of a file. */ static __be32 nfsacld_proc_getacl(struct svc_rqst * rqstp, struct nfsd3_getaclargs *argp, struct nfsd3_getaclres *resp) { struct posix_acl *acl; struct inode *inode; svc_fh *fh; __be32 nfserr = 0; dprintk("nfsd: GETACL(2acl) %s\n", SVCFH_fmt(&argp->fh)); fh = fh_copy(&resp->fh, &argp->fh); nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP); if (nfserr) RETURN_STATUS(nfserr); inode = fh->fh_dentry->d_inode; if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT)) RETURN_STATUS(nfserr_inval); resp->mask = argp->mask; nfserr = fh_getattr(fh, &resp->stat); if (nfserr) goto fail; if (resp->mask & (NFS_ACL|NFS_ACLCNT)) { acl = get_acl(inode, ACL_TYPE_ACCESS); if (IS_ERR(acl)) { nfserr = nfserrno(PTR_ERR(acl)); goto fail; } if (acl == NULL) { /* Solaris returns the inode's minimum ACL. */ acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL); } resp->acl_access = acl; } if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) { /* Check how Solaris handles requests for the Default ACL of a non-directory! */ acl = get_acl(inode, ACL_TYPE_DEFAULT); if (IS_ERR(acl)) { nfserr = nfserrno(PTR_ERR(acl)); goto fail; } resp->acl_default = acl; } /* resp->acl_{access,default} are released in nfssvc_release_getacl. */ RETURN_STATUS(0); fail: posix_acl_release(resp->acl_access); posix_acl_release(resp->acl_default); RETURN_STATUS(nfserr); }
/* * Check file access */ static __be32 nfsacld_proc_access(struct svc_rqst *rqstp, struct nfsd3_accessargs *argp, struct nfsd3_accessres *resp) { __be32 nfserr; dprintk("nfsd: ACCESS(2acl) %s 0x%x\n", SVCFH_fmt(&argp->fh), argp->access); fh_copy(&resp->fh, &argp->fh); resp->access = argp->access; nfserr = nfsd_access(rqstp, &resp->fh, &resp->access, NULL); return nfserr; }
/* * Set the Access and/or Default ACL of a file. */ static __be32 nfsacld_proc_setacl(struct svc_rqst * rqstp, struct nfsd3_setaclargs *argp, struct nfsd_attrstat *resp) { struct inode *inode; svc_fh *fh; __be32 nfserr = 0; int error; dprintk("nfsd: SETACL(2acl) %s\n", SVCFH_fmt(&argp->fh)); fh = fh_copy(&resp->fh, &argp->fh); nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR); if (nfserr) goto out; inode = fh->fh_dentry->d_inode; if (!IS_POSIXACL(inode) || !inode->i_op->set_acl) { error = -EOPNOTSUPP; goto out_errno; } error = fh_want_write(fh); if (error) goto out_errno; error = inode->i_op->set_acl(inode, argp->acl_access, ACL_TYPE_ACCESS); if (error) goto out_drop_write; error = inode->i_op->set_acl(inode, argp->acl_default, ACL_TYPE_DEFAULT); if (error) goto out_drop_write; fh_drop_write(fh); nfserr = fh_getattr(fh, &resp->stat); out: /* argp->acl_{access,default} may have been allocated in nfssvc_decode_setaclargs. */ posix_acl_release(argp->acl_access); posix_acl_release(argp->acl_default); return nfserr; out_drop_write: fh_drop_write(fh); out_errno: nfserr = nfserrno(error); goto out; }
static void test_nfsfh(void){ dev_t dev=0; u32 unfs=0, u32ptr[2]; ino_t ino=0; struct svc_fh A1; int i=20; u32_to_dev_t((__u32)unfs); ino_t_to_u32(ino); u32_to_ino_t((__u32)unfs); mk_fsid_v0(u32ptr, dev, ino); mk_fsid_v1(u32ptr, unfs); SVCFH_fmt(&A1); fh_init(&A1,i); fh_lock(&A1); fh_unlock(&A1); printk("finished nfsfh test\n"); }
__be32 nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, unsigned int len, struct svc_export **exp_ret, struct dentry **dentry_ret) { struct svc_export *exp; struct dentry *dparent; struct dentry *dentry; __be32 err; int host_err; dprintk("nfsd: nfsd_lookup(fh %s, %.*s)\n", SVCFH_fmt(fhp), len,name); /* Obtain dentry and export. */ err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_EXEC); if (err) return err; dparent = fhp->fh_dentry; exp = fhp->fh_export; exp_get(exp); /* Lookup the name, but don't follow links */ if (isdotent(name, len)) { if (len==1) dentry = dget(dparent); else if (dparent != exp->ex_path.dentry) dentry = dget_parent(dparent); else if (!EX_NOHIDE(exp)) dentry = dget(dparent); /* .. == . just like at / */ else { /* checking mountpoint crossing is very different when stepping up */ struct svc_export *exp2 = NULL; struct dentry *dp; struct vfsmount *mnt = mntget(exp->ex_path.mnt); dentry = dget(dparent); while(dentry == mnt->mnt_root && follow_up(&mnt, &dentry)) ; dp = dget_parent(dentry); dput(dentry); dentry = dp; exp2 = rqst_exp_parent(rqstp, mnt, dentry); if (PTR_ERR(exp2) == -ENOENT) { dput(dentry); dentry = dget(dparent); } else if (IS_ERR(exp2)) { host_err = PTR_ERR(exp2); dput(dentry); mntput(mnt); goto out_nfserr; } else { exp_put(exp); exp = exp2; } mntput(mnt); } } else { fh_lock(fhp); dentry = lookup_one_len(name, dparent, len); host_err = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_nfserr; /* * check if we have crossed a mount point ... */ if (d_mountpoint(dentry)) { if ((host_err = nfsd_cross_mnt(rqstp, &dentry, &exp))) { dput(dentry); goto out_nfserr; } } } *dentry_ret = dentry; *exp_ret = exp; return 0; out_nfserr: exp_put(exp); return nfserrno(host_err); }
/* * Look up one component of a pathname. * N.B. After this call _both_ fhp and resfh need an fh_put * * If the lookup would cross a mountpoint, and the mounted filesystem * is exported to the client with NFSEXP_CROSSMNT, then the lookup is * accepted as it stands and the mounted directory is * returned. Otherwise the covered directory is returned. * NOTE: this mountpoint crossing is not supported properly by all * clients and is explicitly disallowed for NFSv3 * NeilBrown <*****@*****.**> */ int nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, int len, struct svc_fh *resfh) { struct svc_export *exp; struct dentry *dparent; struct dentry *dentry; int err; dprintk("nfsd: nfsd_lookup(fh %s, %.*s)\n", SVCFH_fmt(fhp), len,name); /* Obtain dentry and export. */ err = fh_verify(rqstp, fhp, S_IFDIR, MAY_EXEC); if (err) goto out; dparent = fhp->fh_dentry; exp = fhp->fh_export; err = nfserr_acces; /* Lookup the name, but don't follow links */ if (isdotent(name, len)) { if (len==1) dentry = dget(dparent); else { /* must be ".." */ /* checking mountpoint crossing is very different when stepping up */ if (dparent == exp->ex_dentry) { if (!EX_CROSSMNT(exp)) dentry = dget(dparent); /* .. == . just like at / */ else { struct svc_export *exp2 = NULL; struct dentry *dp; struct vfsmount *mnt = mntget(exp->ex_mnt); dentry = dget(dparent); while(follow_up(&mnt, &dentry)) ; dp = dget(dentry->d_parent); dput(dentry); dentry = dp; for ( ; exp2 == NULL && dp->d_parent != dp; dp=dp->d_parent) exp2 = exp_get(exp->ex_client, dp->d_inode->i_dev, dp->d_inode->i_ino); if (exp2==NULL) { dput(dentry); dentry = dget(dparent); } else { exp = exp2; } mntput(mnt); } } else dentry = dget(dparent->d_parent); } } else { fh_lock(fhp); dentry = lookup_one_len(name, dparent, len); err = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_nfserr; /* * check if we have crossed a mount point ... */ if (d_mountpoint(dentry)) { struct svc_export *exp2 = NULL; struct vfsmount *mnt = mntget(exp->ex_mnt); struct dentry *mounts = dget(dentry); while (follow_down(&mnt,&mounts)&&d_mountpoint(mounts)) ; exp2 = exp_get(rqstp->rq_client, mounts->d_inode->i_dev, mounts->d_inode->i_ino); if (exp2 && EX_CROSSMNT(exp2)) { /* successfully crossed mount point */ exp = exp2; dput(dentry); dentry = mounts; } else dput(mounts); mntput(mnt); } } /* * Note: we compose the file handle now, but as the * dentry may be negative, it may need to be updated. */ err = fh_compose(resfh, exp, dentry, fhp); if (!err && !dentry->d_inode) err = nfserr_noent; out: return err; out_nfserr: err = nfserrno(err); goto out; }
/* * Look up one component of a pathname. * N.B. After this call _both_ fhp and resfh need an fh_put * * If the lookup would cross a mountpoint, and the mounted filesystem * is exported to the client with NFSEXP_NOHIDE, then the lookup is * accepted as it stands and the mounted directory is * returned. Otherwise the covered directory is returned. * NOTE: this mountpoint crossing is not supported properly by all * clients and is explicitly disallowed for NFSv3 * NeilBrown <*****@*****.**> */ int nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name, int len, struct svc_fh *resfh) { struct svc_export *exp; struct dentry *dparent; struct dentry *dentry; int err; dprintk("nfsd: nfsd_lookup(fh %s, %.*s)\n", SVCFH_fmt(fhp), len,name); /* Obtain dentry and export. */ err = fh_verify(rqstp, fhp, S_IFDIR, MAY_EXEC); if (err) return err; dparent = fhp->fh_dentry; exp = fhp->fh_export; exp_get(exp); err = nfserr_acces; /* Lookup the name, but don't follow links */ if (isdotent(name, len)) { if (len==1) dentry = dget(dparent); else if (dparent != exp->ex_dentry) { dentry = dget_parent(dparent); } else if (!EX_NOHIDE(exp)) dentry = dget(dparent); /* .. == . just like at / */ else { /* checking mountpoint crossing is very different when stepping up */ struct svc_export *exp2 = NULL; struct dentry *dp; struct vfsmount *mnt = mntget(exp->ex_mnt); dentry = dget(dparent); while(dentry == mnt->mnt_root && follow_up(&mnt, &dentry)) ; dp = dget_parent(dentry); dput(dentry); dentry = dp; exp2 = exp_parent(exp->ex_client, mnt, dentry, &rqstp->rq_chandle); if (IS_ERR(exp2)) { err = PTR_ERR(exp2); dput(dentry); mntput(mnt); goto out_nfserr; } if (!exp2) { dput(dentry); dentry = dget(dparent); } else { exp_put(exp); exp = exp2; } mntput(mnt); } } else { fh_lock(fhp); dentry = lookup_one_len(name, dparent, len); err = PTR_ERR(dentry); if (IS_ERR(dentry)) goto out_nfserr; /* * check if we have crossed a mount point ... */ if (d_mountpoint(dentry)) { if ((err = nfsd_cross_mnt(rqstp, &dentry, &exp))) { dput(dentry); goto out_nfserr; } } } /* * Note: we compose the file handle now, but as the * dentry may be negative, it may need to be updated. */ err = fh_compose(resfh, exp, dentry, fhp); if (!err && !dentry->d_inode) err = nfserr_noent; dput(dentry); out: exp_put(exp); return err; out_nfserr: err = nfserrno(err); goto out; }
/* * 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) { __u32 *datap=NULL; __u32 tfh[3]; /* filehandle fragment for oldstyle filehandles */ int fileid_type; int data_left = fh->fh_size/4; error = nfserr_stale; if (rqstp->rq_client == NULL) goto out; 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; datap = fh->fh_auth; 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 = exp_find(rqstp->rq_client, fh->fh_fsid_type, datap, &rqstp->rq_chandle); datap += len; } else { 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 = exp_find(rqstp->rq_client, FSID_DEV, tfh, &rqstp->rq_chandle); } if (IS_ERR(exp) && (PTR_ERR(exp) == -EAGAIN || PTR_ERR(exp) == -ETIMEDOUT)) { error = nfserrno(PTR_ERR(exp)); goto out; } error = nfserr_stale; if (!exp || IS_ERR(exp)) goto out; /* Check if the request originated from a secure port. */ error = nfserr_perm; if (!rqstp->rq_secure && EX_SECURE(exp)) { char buf[RPC_MAX_ADDRBUFLEN]; printk(KERN_WARNING "nfsd: request from insecure port %s!\n", svc_print_addr(rqstp, buf, sizeof(buf))); goto out; } /* Set user creds for this exportpoint */ error = nfserrno(nfsd_setuser(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) { tfh[0] = fh->ofh_ino; tfh[1] = fh->ofh_generation; tfh[2] = fh->ofh_dirino; datap = tfh; data_left = 3; if (fh->ofh_dirino == 0) fileid_type = 1; else fileid_type = 2; } else fileid_type = fh->fh_fileid_type; if (fileid_type == 0) dentry = dget(exp->ex_dentry); else { struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op; dentry = CALL(nop,decode_fh)(exp->ex_mnt->mnt_sb, datap, 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++; } 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; /* 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 = nfserrno(nfsd_setuser(rqstp, exp)); if (error) goto out; } cache_get(&exp->h); error = nfsd_mode_check(rqstp, dentry->d_inode->i_mode, type); if (error) goto out; /* Finally, check access permissions. */ error = nfsd_permission(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; }
/* * 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 svc_export *exp; struct dentry *dentry; __be32 error; dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp)); if (!fhp->fh_dentry) { error = nfsd_set_fh_dentry(rqstp, fhp); if (error) goto out; dentry = fhp->fh_dentry; exp = fhp->fh_export; } 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; /* * 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 (error == nfserr_stale) nfsdstats.fh_stale++; 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; }
/* * 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. */ u32 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; struct inode *inode; u32 error = 0; dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp)); /* keep this filehandle for possible reference when encoding attributes */ rqstp->rq_reffh = fh; if (!fhp->fh_dentry) { __u32 *datap=NULL; __u32 tfh[3]; /* filehandle fragment for oldstyle filehandles */ int fileid_type; int data_left = fh->fh_size/4; error = nfserr_stale; if (rqstp->rq_client == NULL) goto out; 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; datap = fh->fh_auth; 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 == 2) { /* deprecated, convert to type 3 */ len = 3; fh->fh_fsid_type = 3; 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 = exp_find(rqstp->rq_client, fh->fh_fsid_type, datap, &rqstp->rq_chandle); datap += len; } else { 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_v0(tfh, xdev, xino); exp = exp_find(rqstp->rq_client, 0, tfh, &rqstp->rq_chandle); } error = nfserr_dropit; if (IS_ERR(exp) && PTR_ERR(exp) == -EAGAIN) goto out; error = nfserr_stale; if (!exp || IS_ERR(exp)) goto out; /* Check if the request originated from a secure port. */ error = nfserr_perm; if (!rqstp->rq_secure && EX_SECURE(exp)) { printk(KERN_WARNING "nfsd: request from insecure port (%08x:%d)!\n", ntohl(rqstp->rq_addr.sin_addr.s_addr), ntohs(rqstp->rq_addr.sin_port)); goto out; } /* Set user creds for this exportpoint */ error = nfsd_setuser(rqstp, exp); if (error) { error = nfserrno(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) { tfh[0] = fh->ofh_ino; tfh[1] = fh->ofh_generation; tfh[2] = fh->ofh_dirino; datap = tfh; data_left = 3; if (fh->ofh_dirino == 0) fileid_type = 1; else fileid_type = 2; } else fileid_type = fh->fh_fileid_type; if (fileid_type == 0) dentry = dget(exp->ex_dentry); else { struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op; dentry = CALL(nop,decode_fh)(exp->ex_mnt->mnt_sb, datap, 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; } #ifdef NFSD_PARANOIA 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); } #endif fhp->fh_dentry = dentry; fhp->fh_export = exp; nfsd_nr_verified++; } 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); inode = dentry->d_inode; /* Type check. The correct error return for type mismatches * does not seem to be generally agreed upon. SunOS seems to * use EISDIR if file isn't S_IFREG; a comment in the NFSv3 * spec says this is incorrect (implementation notes for the * write call). */ /* Type can be negative when creating hardlinks - not to a dir */ if (type > 0 && (inode->i_mode & S_IFMT) != type) { if (rqstp->rq_vers == 4 && (inode->i_mode & S_IFMT) == S_IFLNK) error = nfserr_symlink; else if (type == S_IFDIR) error = nfserr_notdir; else if ((inode->i_mode & S_IFMT) == S_IFDIR) error = nfserr_isdir; else error = nfserr_inval; goto out; } if (type < 0 && (inode->i_mode & S_IFMT) == -type) { if (rqstp->rq_vers == 4 && (inode->i_mode & S_IFMT) == S_IFLNK) error = nfserr_symlink; else if (type == -S_IFDIR) error = nfserr_isdir; else error = nfserr_notdir; goto out; } /* Finally, check access permissions. */ error = nfsd_permission(exp, dentry, access); #ifdef NFSD_PARANOIA_EXTREME if (error) { printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, access, (error >> 24)); } #endif out: if (exp && !IS_ERR(exp)) exp_put(exp); if (error == nfserr_stale) nfsdstats.fh_stale++; 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. */ u32 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; u32 error = 0; dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp)); /* keep this filehandle for possible reference when encoding attributes */ rqstp->rq_reffh = fh; if (!fhp->fh_dentry) { __u32 *datap=NULL; __u32 tfh[3]; /* filehandle fragment for oldstyle filehandles */ 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; datap = fh->fh_auth; 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 == 2) { /* deprecated, convert to type 3 */ len = 3; fh->fh_fsid_type = 3; 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, datap); datap += len; } else { 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_v0(tfh, xdev, xino); 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; } /* Check if the request originated from a secure port. */ error = nfserr_perm; if (!rqstp->rq_secure && EX_SECURE(exp)) { printk(KERN_WARNING "nfsd: request from insecure port (%u.%u.%u.%u:%d)!\n", NIPQUAD(rqstp->rq_addr.sin_addr.s_addr), ntohs(rqstp->rq_addr.sin_port)); goto out; } /* Set user creds for this exportpoint */ error = nfserrno(nfsd_setuser(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) { tfh[0] = fh->ofh_ino; tfh[1] = fh->ofh_generation; tfh[2] = fh->ofh_dirino; datap = tfh; data_left = 3; if (fh->ofh_dirino == 0) fileid_type = 1; else fileid_type = 2; } else fileid_type = fh->fh_fileid_type; if (fileid_type == 0) dentry = dget(exp->ex_dentry); else { struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op; dentry = CALL(nop,decode_fh)(exp->ex_mnt->mnt_sb, datap, 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; } #ifdef NFSD_PARANOIA 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); } #endif fhp->fh_dentry = dentry; fhp->fh_export = exp; nfsd_nr_verified++; } 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; /* 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 = nfserrno(nfsd_setuser(rqstp, exp)); if (error) goto out; } cache_get(&exp->h); error = nfsd_mode_check(rqstp, dentry->d_inode->i_mode, type); if (error) goto out; /* * pseudoflavor restrictions are not enforced on NLM, * which clients virtually always use auth_sys for, * even while using RPCSEC_GSS for NFS. */ if (access & MAY_LOCK) goto skip_pseudoflavor_check; /* * Clients may expect to be able to use auth_sys during mount, * even if they use gss for everything else; see section 2.3.2 * of rfc 2623. */ if (access & MAY_BYPASS_GSS_ON_ROOT && exp->ex_dentry == dentry) goto skip_pseudoflavor_check; error = check_nfsd_access(exp, rqstp); if (error) goto out; skip_pseudoflavor_check: /* Finally, check access permissions. */ error = nfsd_permission(rqstp, exp, dentry, access); #ifdef NFSD_PARANOIA_EXTREME if (error) { printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n", dentry->d_parent->d_name.name, dentry->d_name.name, access, (error >> 24)); } #endif out: if (exp && !IS_ERR(exp)) exp_put(exp); if (error == nfserr_stale) nfsdstats.fh_stale++; return error; }