static __be32 compose_entry_fh(struct nfsd3_readdirres *cd, struct svc_fh *fhp, const char *name, int namlen, u64 ino) { struct svc_export *exp; struct dentry *dparent, *dchild; __be32 rv = nfserr_noent; dparent = cd->fh.fh_dentry; exp = cd->fh.fh_export; if (isdotent(name, namlen)) { if (namlen == 2) { dchild = dget_parent(dparent); /* filesystem root - cannot return filehandle for ".." */ if (dchild == dparent) goto out; } else dchild = dget(dparent); } else dchild = lookup_one_len_unlocked(name, dparent, namlen); if (IS_ERR(dchild)) return rv; if (d_mountpoint(dchild)) goto out; if (d_really_is_negative(dchild)) goto out; if (dchild->d_inode->i_ino != ino) goto out; rv = fh_compose(fhp, exp, dchild, &cd->fh); out: dput(dchild); return rv; }
static int nfsd4_build_dentrylist(void *arg, const char *name, int namlen, loff_t offset, u64 ino, unsigned int d_type) { struct dentry_list_arg *dla = arg; struct list_head *dentries = &dla->dentries; struct dentry *parent = dla->parent; struct dentry *dentry; struct dentry_list *child; if (name && isdotent(name, namlen)) return 0; dentry = lookup_one_len(name, parent, namlen); if (IS_ERR(dentry)) return PTR_ERR(dentry); child = kmalloc(sizeof(*child), GFP_KERNEL); if (child == NULL) return -ENOMEM; child->dentry = dentry; list_add(&child->list, dentries); return 0; }
static int encode_entry(struct readdir_cd *cd, const char *name, int namlen, off_t offset, ino_t ino, unsigned int d_type, int plus) { u32 *p = cd->buffer; int buflen, slen, elen; if (cd->offset) xdr_encode_hyper(cd->offset, (u64) offset); /* nfsd_readdir calls us with name == 0 when it wants us to * set the last offset entry. */ if (name == 0) return 0; /* dprintk("encode_entry(%.*s @%ld%s)\n", namlen, name, (long) offset, plus? " plus" : ""); */ /* truncate filename if too long */ if (namlen > NFS3_MAXNAMLEN) namlen = NFS3_MAXNAMLEN; slen = XDR_QUADLEN(namlen); elen = slen + NFS3_ENTRY_BAGGAGE + (plus? NFS3_ENTRYPLUS_BAGGAGE : 0); if ((buflen = cd->buflen - elen) < 0) { cd->eob = 1; return -EINVAL; } *p++ = xdr_one; /* mark entry present */ p = xdr_encode_hyper(p, ino); /* file id */ p = xdr_encode_array(p, name, namlen);/* name length & name */ cd->offset = p; /* remember pointer */ p = xdr_encode_hyper(p, NFS_OFFSET_MAX); /* offset of next entry */ /* throw in readdirplus baggage */ if (plus) { struct svc_fh fh; struct svc_export *exp; struct dentry *dparent, *dchild; dparent = cd->dirfh->fh_dentry; exp = cd->dirfh->fh_export; fh_init(&fh, NFS3_FHSIZE); if (isdotent(name, namlen)) { dchild = dparent; if (namlen == 2) dchild = dchild->d_parent; dchild = dget(dchild); } else dchild = lookup_one_len(name, dparent,namlen); if (IS_ERR(dchild)) goto noexec; if (fh_compose(&fh, exp, dchild, cd->dirfh) != 0 || !dchild->d_inode) goto noexec; p = encode_post_op_attr(cd->rqstp, p, &fh); *p++ = xdr_one; /* yes, a file handle follows */ p = encode_fh(p, &fh); fh_put(&fh); } out: cd->buflen = buflen; cd->buffer = p; return 0; noexec: *p++ = 0; *p++ = 0; goto out; }
__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; }
/* * Unlink a file or directory * N.B. After this call fhp needs an fh_put */ int nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, char *fname, int flen) { struct dentry *dentry, *rdentry; struct inode *dirp; int err; /* N.B. We shouldn't need this test ... handled by dentry layer */ err = nfserr_acces; if (!flen || isdotent(fname, flen)) goto out; err = fh_verify(rqstp, fhp, S_IFDIR, MAY_REMOVE); if (err) goto out; dentry = fhp->fh_dentry; dirp = dentry->d_inode; rdentry = lookup_dentry(fname, dget(dentry), 0); err = PTR_ERR(rdentry); if (IS_ERR(rdentry)) goto out_nfserr; if (!rdentry->d_inode) { dput(rdentry); err = nfserr_noent; goto out; } if (type != S_IFDIR) { /* It's UNLINK */ err = fh_lock_parent(fhp, rdentry); if (err) goto out; err = vfs_unlink(dirp, rdentry); DQUOT_DROP(dirp); fh_unlock(fhp); dput(rdentry); } else { /* It's RMDIR */ /* See comments in fs/namei.c:do_rmdir */ 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; fhp->fh_locked = 1; err = -ENOENT; if (check_parent(dirp, rdentry)) err = vfs_rmdir(dirp, rdentry); rdentry->d_count--; DQUOT_DROP(dirp); if (!fhp->fh_post_version) fhp->fh_post_version = dirp->i_version; fhp->fh_locked = 0; nfsd_double_up(&dirp->i_sem, &rdentry->d_inode->i_sem); dput(rdentry); } if (err) goto out_nfserr; if (EX_ISSYNC(fhp->fh_export)) write_inode_now(dirp); out: return err; out_nfserr: err = nfserrno(-err); goto out; }