NFS: Allow the NFS generic code to pass in a verifier to readdir

If we're ever going to allow support for servers that use the readdir
verifier, then that use needs to be managed by the middle layers as
those need to be able to reject cookies from other verifiers.

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Reviewed-by: Benjamin Coddington <bcodding@redhat.com>
Tested-by: Benjamin Coddington <bcodding@redhat.com>
Tested-by: Dave Wysochanski <dwysocha@redhat.com>
This commit is contained in:
Trond Myklebust 2020-11-02 17:34:23 -05:00
parent 6c981eff23
commit 82e22a5e62
5 changed files with 76 additions and 53 deletions

View File

@ -469,8 +469,20 @@ static int nfs_readdir_xdr_filler(struct nfs_readdir_descriptor *desc,
u64 cookie, struct page **pages, u64 cookie, struct page **pages,
size_t bufsize) size_t bufsize)
{ {
struct file *file = desc->file; struct inode *inode = file_inode(desc->file);
struct inode *inode = file_inode(file); __be32 verf_res[2];
struct nfs_readdir_arg arg = {
.dentry = file_dentry(desc->file),
.cred = desc->file->f_cred,
.verf = NFS_I(inode)->cookieverf,
.cookie = cookie,
.pages = pages,
.page_len = bufsize,
.plus = desc->plus,
};
struct nfs_readdir_res res = {
.verf = verf_res,
};
unsigned long timestamp, gencount; unsigned long timestamp, gencount;
int error; int error;
@ -478,20 +490,21 @@ static int nfs_readdir_xdr_filler(struct nfs_readdir_descriptor *desc,
timestamp = jiffies; timestamp = jiffies;
gencount = nfs_inc_attr_generation_counter(); gencount = nfs_inc_attr_generation_counter();
desc->dir_verifier = nfs_save_change_attribute(inode); desc->dir_verifier = nfs_save_change_attribute(inode);
error = NFS_PROTO(inode)->readdir(file_dentry(file), file->f_cred, error = NFS_PROTO(inode)->readdir(&arg, &res);
cookie, pages, bufsize, desc->plus);
if (error < 0) { if (error < 0) {
/* We requested READDIRPLUS, but the server doesn't grok it */ /* We requested READDIRPLUS, but the server doesn't grok it */
if (error == -ENOTSUPP && desc->plus) { if (error == -ENOTSUPP && desc->plus) {
NFS_SERVER(inode)->caps &= ~NFS_CAP_READDIRPLUS; NFS_SERVER(inode)->caps &= ~NFS_CAP_READDIRPLUS;
clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags); clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
desc->plus = false; desc->plus = arg.plus = false;
goto again; goto again;
} }
goto error; goto error;
} }
desc->timestamp = timestamp; desc->timestamp = timestamp;
desc->gencount = gencount; desc->gencount = gencount;
memcpy(NFS_I(inode)->cookieverf, res.verf,
sizeof(NFS_I(inode)->cookieverf));
error: error:
return error; return error;
} }

View File

@ -662,37 +662,36 @@ out:
* Also note that this implementation handles both plain readdir and * Also note that this implementation handles both plain readdir and
* readdirplus. * readdirplus.
*/ */
static int static int nfs3_proc_readdir(struct nfs_readdir_arg *nr_arg,
nfs3_proc_readdir(struct dentry *dentry, const struct cred *cred, struct nfs_readdir_res *nr_res)
u64 cookie, struct page **pages, unsigned int count, bool plus)
{ {
struct inode *dir = d_inode(dentry); struct inode *dir = d_inode(nr_arg->dentry);
__be32 *verf = NFS_I(dir)->cookieverf;
struct nfs3_readdirargs arg = { struct nfs3_readdirargs arg = {
.fh = NFS_FH(dir), .fh = NFS_FH(dir),
.cookie = cookie, .cookie = nr_arg->cookie,
.verf = {verf[0], verf[1]}, .plus = nr_arg->plus,
.plus = plus, .count = nr_arg->page_len,
.count = count, .pages = nr_arg->pages
.pages = pages
}; };
struct nfs3_readdirres res = { struct nfs3_readdirres res = {
.verf = verf, .verf = nr_res->verf,
.plus = plus .plus = nr_arg->plus,
}; };
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs3_procedures[NFS3PROC_READDIR], .rpc_proc = &nfs3_procedures[NFS3PROC_READDIR],
.rpc_argp = &arg, .rpc_argp = &arg,
.rpc_resp = &res, .rpc_resp = &res,
.rpc_cred = cred, .rpc_cred = nr_arg->cred,
}; };
int status = -ENOMEM; int status = -ENOMEM;
if (plus) if (nr_arg->plus)
msg.rpc_proc = &nfs3_procedures[NFS3PROC_READDIRPLUS]; msg.rpc_proc = &nfs3_procedures[NFS3PROC_READDIRPLUS];
if (arg.cookie)
memcpy(arg.verf, nr_arg->verf, sizeof(arg.verf));
dprintk("NFS call readdir%s %d\n", dprintk("NFS call readdir%s %llu\n", nr_arg->plus ? "plus" : "",
plus? "plus" : "", (unsigned int) cookie); (unsigned long long)nr_arg->cookie);
res.dir_attr = nfs_alloc_fattr(); res.dir_attr = nfs_alloc_fattr();
if (res.dir_attr == NULL) if (res.dir_attr == NULL)
@ -705,8 +704,8 @@ nfs3_proc_readdir(struct dentry *dentry, const struct cred *cred,
nfs_free_fattr(res.dir_attr); nfs_free_fattr(res.dir_attr);
out: out:
dprintk("NFS reply readdir%s: %d\n", dprintk("NFS reply readdir%s: %d\n", nr_arg->plus ? "plus" : "",
plus? "plus" : "", status); status);
return status; return status;
} }

View File

@ -4961,41 +4961,40 @@ static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
return err; return err;
} }
static int _nfs4_proc_readdir(struct dentry *dentry, const struct cred *cred, static int _nfs4_proc_readdir(struct nfs_readdir_arg *nr_arg,
u64 cookie, struct page **pages, unsigned int count, bool plus) struct nfs_readdir_res *nr_res)
{ {
struct inode *dir = d_inode(dentry); struct inode *dir = d_inode(nr_arg->dentry);
struct nfs_server *server = NFS_SERVER(dir); struct nfs_server *server = NFS_SERVER(dir);
struct nfs4_readdir_arg args = { struct nfs4_readdir_arg args = {
.fh = NFS_FH(dir), .fh = NFS_FH(dir),
.pages = pages, .pages = nr_arg->pages,
.pgbase = 0, .pgbase = 0,
.count = count, .count = nr_arg->page_len,
.plus = plus, .plus = nr_arg->plus,
}; };
struct nfs4_readdir_res res; struct nfs4_readdir_res res;
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READDIR], .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_READDIR],
.rpc_argp = &args, .rpc_argp = &args,
.rpc_resp = &res, .rpc_resp = &res,
.rpc_cred = cred, .rpc_cred = nr_arg->cred,
}; };
int status; int status;
dprintk("%s: dentry = %pd2, cookie = %Lu\n", __func__, dprintk("%s: dentry = %pd2, cookie = %llu\n", __func__,
dentry, nr_arg->dentry, (unsigned long long)nr_arg->cookie);
(unsigned long long)cookie);
if (!(server->caps & NFS_CAP_SECURITY_LABEL)) if (!(server->caps & NFS_CAP_SECURITY_LABEL))
args.bitmask = server->attr_bitmask_nl; args.bitmask = server->attr_bitmask_nl;
else else
args.bitmask = server->attr_bitmask; args.bitmask = server->attr_bitmask;
nfs4_setup_readdir(cookie, NFS_I(dir)->cookieverf, dentry, &args); nfs4_setup_readdir(nr_arg->cookie, nr_arg->verf, nr_arg->dentry, &args);
res.pgbase = args.pgbase; res.pgbase = args.pgbase;
status = nfs4_call_sync(server->client, server, &msg, &args.seq_args, status = nfs4_call_sync(server->client, server, &msg, &args.seq_args,
&res.seq_res, 0); &res.seq_res, 0);
if (status >= 0) { if (status >= 0) {
memcpy(NFS_I(dir)->cookieverf, res.verifier.data, NFS4_VERIFIER_SIZE); memcpy(nr_res->verf, res.verifier.data, NFS4_VERIFIER_SIZE);
status += args.pgbase; status += args.pgbase;
} }
@ -5005,19 +5004,18 @@ static int _nfs4_proc_readdir(struct dentry *dentry, const struct cred *cred,
return status; return status;
} }
static int nfs4_proc_readdir(struct dentry *dentry, const struct cred *cred, static int nfs4_proc_readdir(struct nfs_readdir_arg *arg,
u64 cookie, struct page **pages, unsigned int count, bool plus) struct nfs_readdir_res *res)
{ {
struct nfs4_exception exception = { struct nfs4_exception exception = {
.interruptible = true, .interruptible = true,
}; };
int err; int err;
do { do {
err = _nfs4_proc_readdir(dentry, cred, cookie, err = _nfs4_proc_readdir(arg, res);
pages, count, plus); trace_nfs4_readdir(d_inode(arg->dentry), err);
trace_nfs4_readdir(d_inode(dentry), err); err = nfs4_handle_exception(NFS_SERVER(d_inode(arg->dentry)),
err = nfs4_handle_exception(NFS_SERVER(d_inode(dentry)), err, err, &exception);
&exception);
} while (exception.retry); } while (exception.retry);
return err; return err;
} }

View File

@ -499,26 +499,26 @@ nfs_proc_rmdir(struct inode *dir, const struct qstr *name)
* sure it is syntactically correct; the entries itself are decoded * sure it is syntactically correct; the entries itself are decoded
* from nfs_readdir by calling the decode_entry function directly. * from nfs_readdir by calling the decode_entry function directly.
*/ */
static int static int nfs_proc_readdir(struct nfs_readdir_arg *nr_arg,
nfs_proc_readdir(struct dentry *dentry, const struct cred *cred, struct nfs_readdir_res *nr_res)
u64 cookie, struct page **pages, unsigned int count, bool plus)
{ {
struct inode *dir = d_inode(dentry); struct inode *dir = d_inode(nr_arg->dentry);
struct nfs_readdirargs arg = { struct nfs_readdirargs arg = {
.fh = NFS_FH(dir), .fh = NFS_FH(dir),
.cookie = cookie, .cookie = nr_arg->cookie,
.count = count, .count = nr_arg->page_len,
.pages = pages, .pages = nr_arg->pages,
}; };
struct rpc_message msg = { struct rpc_message msg = {
.rpc_proc = &nfs_procedures[NFSPROC_READDIR], .rpc_proc = &nfs_procedures[NFSPROC_READDIR],
.rpc_argp = &arg, .rpc_argp = &arg,
.rpc_cred = cred, .rpc_cred = nr_arg->cred,
}; };
int status; int status;
dprintk("NFS call readdir %d\n", (unsigned int)cookie); dprintk("NFS call readdir %llu\n", (unsigned long long)nr_arg->cookie);
status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0);
nr_res->verf[0] = nr_res->verf[1] = 0;
nfs_invalidate_atime(dir); nfs_invalidate_atime(dir);

View File

@ -750,6 +750,20 @@ struct nfs_entry {
struct nfs_server * server; struct nfs_server * server;
}; };
struct nfs_readdir_arg {
struct dentry *dentry;
const struct cred *cred;
__be32 *verf;
u64 cookie;
struct page **pages;
unsigned int page_len;
bool plus;
};
struct nfs_readdir_res {
__be32 *verf;
};
/* /*
* The following types are for NFSv2 only. * The following types are for NFSv2 only.
*/ */
@ -1744,8 +1758,7 @@ struct nfs_rpc_ops {
unsigned int, struct iattr *); unsigned int, struct iattr *);
int (*mkdir) (struct inode *, struct dentry *, struct iattr *); int (*mkdir) (struct inode *, struct dentry *, struct iattr *);
int (*rmdir) (struct inode *, const struct qstr *); int (*rmdir) (struct inode *, const struct qstr *);
int (*readdir) (struct dentry *, const struct cred *, int (*readdir) (struct nfs_readdir_arg *, struct nfs_readdir_res *);
u64, struct page **, unsigned int, bool);
int (*mknod) (struct inode *, struct dentry *, struct iattr *, int (*mknod) (struct inode *, struct dentry *, struct iattr *,
dev_t); dev_t);
int (*statfs) (struct nfs_server *, struct nfs_fh *, int (*statfs) (struct nfs_server *, struct nfs_fh *,