X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=fs%2Fnfsd%2Fnfsfh.c;h=c59d6fbb7a6bedb71446f33808bcb90ebf667670;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=8e8c2342b184fda56a8ae6855d404d8cb44b92de;hpb=9213980e6a70d8473e0ffd4b39ab5b6caaba9ff5;p=linux-2.6.git diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 8e8c2342b..c59d6fbb7 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -24,8 +24,6 @@ #include #define NFSDDBG_FACILITY NFSDDBG_FH -#define NFSD_PARANOIA 1 -/* #define NFSD_DEBUG_VERBOSE 1 */ static int nfsd_nr_verified; @@ -41,7 +39,7 @@ extern struct export_operations export_op_default; * if not, require that we can walk up to exp->ex_dentry * doing some checks on the 'x' bits */ -int nfsd_acceptable(void *expv, struct dentry *dentry) +static int nfsd_acceptable(void *expv, struct dentry *dentry) { struct svc_export *exp = expv; int rv; @@ -71,6 +69,35 @@ int nfsd_acceptable(void *expv, struct dentry *dentry) return rv; } +/* 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). + */ +static inline __be32 +nfsd_mode_check(struct svc_rqst *rqstp, umode_t mode, int type) +{ + /* Type can be negative when creating hardlinks - not to a dir */ + if (type > 0 && (mode & S_IFMT) != type) { + if (rqstp->rq_vers == 4 && (mode & S_IFMT) == S_IFLNK) + return nfserr_symlink; + else if (type == S_IFDIR) + return nfserr_notdir; + else if ((mode & S_IFMT) == S_IFDIR) + return nfserr_isdir; + else + return nfserr_inval; + } + if (type < 0 && (mode & S_IFMT) == -type) { + if (rqstp->rq_vers == 4 && (mode & S_IFMT) == S_IFLNK) + return nfserr_symlink; + else if (type == -S_IFDIR) + return nfserr_isdir; + else + return nfserr_notdir; + } + return 0; +} /* * Perform sanity checks on the dentry in a client's file handle. @@ -81,14 +108,13 @@ int nfsd_acceptable(void *expv, struct dentry *dentry) * 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 +__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; - struct inode *inode; - u32 error = 0; + __be32 error = 0; dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp)); @@ -141,9 +167,11 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) exp = exp_find(rqstp->rq_client, 0, tfh, &rqstp->rq_chandle); } - error = nfserr_dropit; - if (IS_ERR(exp) && PTR_ERR(exp) == -EAGAIN) + 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)) @@ -153,18 +181,16 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) 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), + "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 = nfsd_setuser(rqstp, exp); - if (error) { - error = nfserrno(error); + error = nfserrno(nfsd_setuser(rqstp, exp)); + if (error) goto out; - } /* * Look up the dentry using the NFS file handle. @@ -190,10 +216,10 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) 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); + dentry = CALL(nop,decode_fh)(exp->ex_mnt->mnt_sb, + datap, data_left, + fileid_type, + nfsd_acceptable, exp); } if (dentry == NULL) goto out; @@ -202,13 +228,12 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) 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; @@ -220,50 +245,32 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) 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); - 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; + 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); -#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)); + 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)); } -#endif out: if (exp && !IS_ERR(exp)) exp_put(exp); @@ -280,8 +287,8 @@ out: * an inode. In this case a call to fh_update should be made * before the fh goes out on the wire ... */ -inline int _fh_update(struct dentry *dentry, struct svc_export *exp, - __u32 *datap, int *maxsize) +static inline int _fh_update(struct dentry *dentry, struct svc_export *exp, + __u32 *datap, int *maxsize) { struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op; @@ -297,8 +304,9 @@ inline int _fh_update(struct dentry *dentry, struct svc_export *exp, /* * for composing old style file handles */ -inline void _fh_update_old(struct dentry *dentry, struct svc_export *exp, - struct knfsd_fh *fh) +static inline void _fh_update_old(struct dentry *dentry, + struct svc_export *exp, + struct knfsd_fh *fh) { fh->ofh_ino = ino_t_to_u32(dentry->d_inode->i_ino); fh->ofh_generation = dentry->d_inode->i_generation; @@ -307,12 +315,12 @@ inline void _fh_update_old(struct dentry *dentry, struct svc_export *exp, fh->ofh_dirino = 0; } -int +__be32 fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, struct svc_fh *ref_fh) { /* ref_fh is a reference file handle. - * if it is non-null, then we should compose a filehandle which is - * of the same version, where possible. + * if it is non-null and for the same filesystem, then we should compose + * a filehandle which is of the same version, where possible. * Currently, that means that if ref_fh->fh_handle.fh_version == 0xca * Then create a 32byte filehandle using nfs_fhbase_old * @@ -331,7 +339,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, st parent->d_name.name, dentry->d_name.name, (inode ? inode->i_ino : 0)); - if (ref_fh) { + if (ref_fh && ref_fh->fh_export == exp) { ref_fh_version = ref_fh->fh_handle.fh_version; if (ref_fh_version == 0xca) ref_fh_fsid_type = 0; @@ -339,13 +347,16 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, st ref_fh_fsid_type = ref_fh->fh_handle.fh_fsid_type; if (ref_fh_fsid_type > 3) ref_fh_fsid_type = 0; - } - /* make sure ref_fh type works for given export */ - if (ref_fh_fsid_type == 1 && - !(exp->ex_flags & NFSEXP_FSID)) { - /* if we don't have an fsid, we cannot provide one... */ - ref_fh_fsid_type = 0; - } + + /* make sure ref_fh type works for given export */ + if (ref_fh_fsid_type == 1 && + !(exp->ex_flags & NFSEXP_FSID)) { + /* if we don't have an fsid, we cannot provide one... */ + ref_fh_fsid_type = 0; + } + } else if (exp->ex_flags & NFSEXP_FSID) + ref_fh_fsid_type = 1; + if (!old_valid_dev(ex_dev) && ref_fh_fsid_type == 0) { /* for newer device numbers, we must use a newer fsid format */ ref_fh_version = 1; @@ -440,7 +451,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, st * Update file handle information after changing a dentry. * This is only called by nfsd_create, nfsd_create_v3 and nfsd_proc_create */ -int +__be32 fh_update(struct svc_fh *fhp) { struct dentry *dentry; @@ -457,7 +468,7 @@ fh_update(struct svc_fh *fhp) } else { int size; if (fhp->fh_handle.fh_fileid_type != 0) - goto out_uptodate; + goto out; datap = fhp->fh_handle.fh_auth+ fhp->fh_handle.fh_size/4 -1; size = (fhp->fh_maxsize - fhp->fh_handle.fh_size)/4; @@ -477,10 +488,6 @@ 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; } /* @@ -502,7 +509,7 @@ fh_put(struct svc_fh *fhp) nfsd_nr_put++; } if (exp) { - svc_export_put(&exp->h, &svc_export_cache); + cache_put(&exp->h, &svc_export_cache); fhp->fh_export = NULL; } return;