From cd9a1c0e5ac681871d64804f82291649e2a0accb Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 17 Sep 2010 10:56:50 -0400 Subject: NFSv4: Clean up nfs4_atomic_open Start moving the 'struct nameidata' dependent code out of the lower level NFS code in preparation for the removal of open intents. Instead of the struct nameidata, we pass down a partially initialised struct nfs_open_context that will be fully initialised by the atomic open upon success. Signed-off-by: Trond Myklebust --- fs/nfs/nfs4_fs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/nfs/nfs4_fs.h') diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 311e15cc8af..c5cc2a6aceb 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -242,7 +242,7 @@ extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *); extern int nfs4_init_clientid(struct nfs_client *, struct rpc_cred *); extern int nfs41_init_clientid(struct nfs_client *, struct rpc_cred *); extern int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, int wait); -extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); +extern struct dentry *nfs4_atomic_open(struct inode *, struct nfs_open_context *, int, struct iattr *); extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *); extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, -- cgit v1.2.3 From f46e0bd34ec002d0727761da52b8fd47f06d4440 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 17 Sep 2010 10:56:50 -0400 Subject: NFSv4: Further minor cleanups for nfs4_atomic_open() Signed-off-by: Trond Myklebust --- fs/nfs/dir.c | 21 +++++++++++++++------ fs/nfs/nfs4_fs.h | 2 +- fs/nfs/nfs4proc.c | 28 ++++------------------------ 3 files changed, 20 insertions(+), 31 deletions(-) (limited to 'fs/nfs/nfs4_fs.h') diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 17529b5bc55..194676cc5b0 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1084,8 +1084,8 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry struct nfs_open_context *ctx; struct iattr attr; struct dentry *res = NULL; + struct inode *inode; int open_flags; - int error; dfprintk(VFS, "NFS: atomic_lookup(%s/%ld), %s\n", dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); @@ -1125,13 +1125,15 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry } /* Open the file on the server */ - res = nfs4_atomic_open(dir, ctx, open_flags, &attr); - if (IS_ERR(res)) { + nfs_block_sillyrename(dentry->d_parent); + inode = nfs4_atomic_open(dir, ctx, open_flags, &attr); + if (IS_ERR(inode)) { + nfs_unblock_sillyrename(dentry->d_parent); put_nfs_open_context(ctx); - error = PTR_ERR(res); - switch (error) { + switch (PTR_ERR(inode)) { /* Make a negative dentry */ case -ENOENT: + d_add(dentry, NULL); res = NULL; goto out; /* This turned out not to be a regular file */ @@ -1143,13 +1145,20 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry goto no_open; /* case -EINVAL: */ default: + res = ERR_CAST(inode); goto out; } } - if (res != NULL) + res = d_add_unique(dentry, inode); + if (res != NULL) { + dput(ctx->path.dentry); + ctx->path.dentry = dget(res); dentry = res; + } nfs_intent_set_file(nd, ctx); + nfs_unblock_sillyrename(dentry->d_parent); out: + nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); return res; no_open: return nfs_lookup(dir, dentry, nd); diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index c5cc2a6aceb..8e1f6396d3d 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -242,7 +242,7 @@ extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *); extern int nfs4_init_clientid(struct nfs_client *, struct rpc_cred *); extern int nfs41_init_clientid(struct nfs_client *, struct rpc_cred *); extern int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, int wait); -extern struct dentry *nfs4_atomic_open(struct inode *, struct nfs_open_context *, int, struct iattr *); +extern struct inode *nfs4_atomic_open(struct inode *, struct nfs_open_context *, int, struct iattr *); extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *); extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 38c3bed2240..f8d41eed9a6 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2024,37 +2024,17 @@ out_close: return ret; } -struct dentry * +struct inode * nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags, struct iattr *attr) { - struct dentry *dentry = ctx->path.dentry; - struct dentry *parent; struct nfs4_state *state; - struct dentry *res; - parent = dentry->d_parent; /* Protect against concurrent sillydeletes */ - nfs_block_sillyrename(parent); state = nfs4_do_open(dir, &ctx->path, ctx->mode, open_flags, attr, ctx->cred); - if (IS_ERR(state)) { - if (PTR_ERR(state) == -ENOENT) { - d_add(dentry, NULL); - nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); - } - nfs_unblock_sillyrename(parent); - return (struct dentry *)state; - } - res = d_add_unique(dentry, igrab(state->inode)); - if (res != NULL) { - struct dentry *dummy = ctx->path.dentry; - - ctx->path.dentry = dget(res); - dput(dummy); - } + if (IS_ERR(state)) + return ERR_CAST(state); ctx->state = state; - nfs_set_verifier(ctx->path.dentry, nfs_save_change_attribute(dir)); - nfs_unblock_sillyrename(parent); - return res; + return igrab(state->inode); } int -- cgit v1.2.3 From b8d4caddd871758ffa156be51b4c8be82fea470d Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 17 Sep 2010 10:56:51 -0400 Subject: NFSv4: Clean up nfs4_open_revalidate Remove references to 'struct nameidata' from the low-level open_revalidate code, and replace them with a struct nfs_open_context which will be correctly initialised upon success. Signed-off-by: Trond Myklebust --- fs/nfs/dir.c | 11 ++++++++++- fs/nfs/nfs4_fs.h | 2 +- fs/nfs/nfs4proc.c | 23 ++++++----------------- 3 files changed, 17 insertions(+), 19 deletions(-) (limited to 'fs/nfs/nfs4_fs.h') diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 194676cc5b0..196775c7094 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1169,6 +1169,7 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) struct dentry *parent = NULL; struct inode *inode = dentry->d_inode; struct inode *dir; + struct nfs_open_context *ctx; int openflags, ret = 0; if (!is_atomic_open(nd) || d_mountpoint(dentry)) @@ -1194,12 +1195,20 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) /* We can't create new files, or truncate existing ones here */ openflags &= ~(O_CREAT|O_EXCL|O_TRUNC); + ctx = nameidata_to_nfs_open_context(dentry, nd); + ret = PTR_ERR(ctx); + if (IS_ERR(ctx)) + goto out; /* * Note: we're not holding inode->i_mutex and so may be racing with * operations that change the directory. We therefore save the * change attribute *before* we do the RPC call. */ - ret = nfs4_open_revalidate(dir, dentry, openflags, nd); + ret = nfs4_open_revalidate(dir, ctx, openflags); + if (ret == 1) + nfs_intent_set_file(nd, ctx); + else + put_nfs_open_context(ctx); out: dput(parent); if (!ret) diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 8e1f6396d3d..76ec1c6d92a 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -243,7 +243,7 @@ extern int nfs4_init_clientid(struct nfs_client *, struct rpc_cred *); extern int nfs41_init_clientid(struct nfs_client *, struct rpc_cred *); extern int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, int wait); extern struct inode *nfs4_atomic_open(struct inode *, struct nfs_open_context *, int, struct iattr *); -extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *); +extern int nfs4_open_revalidate(struct inode *, struct nfs_open_context *, int); extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, struct nfs4_fs_locations *fs_locations, struct page *page); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f8d41eed9a6..b4762463b19 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2038,21 +2038,11 @@ nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags } int -nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, struct nameidata *nd) +nfs4_open_revalidate(struct inode *dir, struct nfs_open_context *ctx, int openflags) { - struct path path = { - .mnt = nd->path.mnt, - .dentry = dentry, - }; - struct rpc_cred *cred; struct nfs4_state *state; - fmode_t fmode = openflags & (FMODE_READ | FMODE_WRITE); - cred = rpc_lookup_cred(); - if (IS_ERR(cred)) - return PTR_ERR(cred); - state = nfs4_do_open(dir, &path, fmode, openflags, NULL, cred); - put_rpccred(cred); + state = nfs4_do_open(dir, &ctx->path, ctx->mode, openflags, NULL, ctx->cred); if (IS_ERR(state)) { switch (PTR_ERR(state)) { case -EPERM: @@ -2065,14 +2055,13 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st goto out_drop; } } - if (state->inode == dentry->d_inode) { - nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); - nfs4_intent_set_file(nd, &path, state, fmode); + ctx->state = state; + if (state->inode == ctx->path.dentry->d_inode) { + nfs_set_verifier(ctx->path.dentry, nfs_save_change_attribute(dir)); return 1; } - nfs4_close_sync(&path, state, fmode); out_drop: - d_drop(dentry); + d_drop(ctx->path.dentry); return 0; } -- cgit v1.2.3 From 535918f14176396646b5547b7d1353c932f24f5e Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 17 Sep 2010 10:56:51 -0400 Subject: NFSv4: Further cleanups for nfs4_open_revalidate() Signed-off-by: Trond Myklebust --- fs/nfs/dir.c | 32 ++++++++++++++++++++++++++------ fs/nfs/nfs4_fs.h | 1 - fs/nfs/nfs4proc.c | 28 ---------------------------- 3 files changed, 26 insertions(+), 35 deletions(-) (limited to 'fs/nfs/nfs4_fs.h') diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 196775c7094..dc93d356341 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1204,16 +1204,36 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) * operations that change the directory. We therefore save the * change attribute *before* we do the RPC call. */ - ret = nfs4_open_revalidate(dir, ctx, openflags); - if (ret == 1) + inode = nfs4_atomic_open(dir, ctx, openflags, NULL); + if (IS_ERR(inode)) { + ret = PTR_ERR(inode); + switch (ret) { + case -EPERM: + case -EACCES: + case -EDQUOT: + case -ENOSPC: + case -EROFS: + goto out_put_ctx; + default: + goto out_drop; + } + } + iput(inode); + if (inode == dentry->d_inode) { + nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); nfs_intent_set_file(nd, ctx); - else - put_nfs_open_context(ctx); + } else + goto out_drop; out: dput(parent); - if (!ret) - d_drop(dentry); return ret; +out_drop: + d_drop(dentry); + ret = 0; +out_put_ctx: + put_nfs_open_context(ctx); + goto out; + no_open_dput: dput(parent); no_open: diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 76ec1c6d92a..6cf12ba9f4e 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -243,7 +243,6 @@ extern int nfs4_init_clientid(struct nfs_client *, struct rpc_cred *); extern int nfs41_init_clientid(struct nfs_client *, struct rpc_cred *); extern int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, int wait); extern struct inode *nfs4_atomic_open(struct inode *, struct nfs_open_context *, int, struct iattr *); -extern int nfs4_open_revalidate(struct inode *, struct nfs_open_context *, int); extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, struct nfs4_fs_locations *fs_locations, struct page *page); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index b4762463b19..83c5ef6e7ce 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2037,34 +2037,6 @@ nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags return igrab(state->inode); } -int -nfs4_open_revalidate(struct inode *dir, struct nfs_open_context *ctx, int openflags) -{ - struct nfs4_state *state; - - state = nfs4_do_open(dir, &ctx->path, ctx->mode, openflags, NULL, ctx->cred); - if (IS_ERR(state)) { - switch (PTR_ERR(state)) { - case -EPERM: - case -EACCES: - case -EDQUOT: - case -ENOSPC: - case -EROFS: - return PTR_ERR(state); - default: - goto out_drop; - } - } - ctx->state = state; - if (state->inode == ctx->path.dentry->d_inode) { - nfs_set_verifier(ctx->path.dentry, nfs_save_change_attribute(dir)); - return 1; - } -out_drop: - d_drop(ctx->path.dentry); - return 0; -} - static void nfs4_close_context(struct nfs_open_context *ctx, int is_sync) { if (ctx->state == NULL) -- cgit v1.2.3 From 2b484297e48c3fbb1846fc6ea10036d9465273e7 Mon Sep 17 00:00:00 2001 From: Trond Myklebust Date: Fri, 17 Sep 2010 10:56:51 -0400 Subject: NFS: Add an 'open_context' element to struct nfs_rpc_ops Signed-off-by: Trond Myklebust --- fs/nfs/dir.c | 7 ++++--- fs/nfs/nfs4_fs.h | 1 - fs/nfs/nfs4proc.c | 3 ++- include/linux/nfs_xdr.h | 4 ++++ 4 files changed, 10 insertions(+), 5 deletions(-) (limited to 'fs/nfs/nfs4_fs.h') diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index e37ffddd79c..1e9e18813ad 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -34,7 +34,6 @@ #include #include -#include "nfs4_fs.h" #include "delegation.h" #include "iostat.h" #include "internal.h" @@ -1127,7 +1126,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry /* Open the file on the server */ nfs_block_sillyrename(dentry->d_parent); - inode = nfs4_atomic_open(dir, ctx, open_flags, &attr); + inode = NFS_PROTO(dir)->open_context(dir, ctx, open_flags, &attr); if (IS_ERR(inode)) { nfs_unblock_sillyrename(dentry->d_parent); put_nfs_open_context(ctx); @@ -1175,8 +1174,10 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) if (!is_atomic_open(nd) || d_mountpoint(dentry)) goto no_open; + parent = dget_parent(dentry); dir = parent->d_inode; + /* We can't create new files in nfs_open_revalidate(), so we * optimize away revalidation of negative dentries. */ @@ -1205,7 +1206,7 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd) * operations that change the directory. We therefore save the * change attribute *before* we do the RPC call. */ - inode = nfs4_atomic_open(dir, ctx, openflags, NULL); + inode = NFS_PROTO(dir)->open_context(dir, ctx, openflags, NULL); if (IS_ERR(inode)) { ret = PTR_ERR(inode); switch (ret) { diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index 6cf12ba9f4e..d24a8e07b5e 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -242,7 +242,6 @@ extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *); extern int nfs4_init_clientid(struct nfs_client *, struct rpc_cred *); extern int nfs41_init_clientid(struct nfs_client *, struct rpc_cred *); extern int nfs4_do_close(struct path *path, struct nfs4_state *state, gfp_t gfp_mask, int wait); -extern struct inode *nfs4_atomic_open(struct inode *, struct nfs_open_context *, int, struct iattr *); extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name, struct nfs4_fs_locations *fs_locations, struct page *page); diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 617b149ee16..643abd26f2d 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -1998,7 +1998,7 @@ out: return status; } -struct inode * +static struct inode * nfs4_atomic_open(struct inode *dir, struct nfs_open_context *ctx, int open_flags, struct iattr *attr) { struct nfs4_state *state; @@ -5358,6 +5358,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = { .lock = nfs4_proc_lock, .clear_acl_cache = nfs4_zap_acl_attr, .close_context = nfs4_close_context, + .open_context = nfs4_atomic_open, }; /* diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index b1484dad7be..6f345f8af4a 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1065,6 +1065,10 @@ struct nfs_rpc_ops { int (*lock_check_bounds)(const struct file_lock *); void (*clear_acl_cache)(struct inode *); void (*close_context)(struct nfs_open_context *ctx, int); + struct inode * (*open_context) (struct inode *dir, + struct nfs_open_context *ctx, + int open_flags, + struct iattr *iattr); }; /* -- cgit v1.2.3 From babddc72a9468884ce1a23db3c3d54b0afa299f0 Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Wed, 20 Oct 2010 15:44:29 -0400 Subject: NFS: decode_dirent should use an xdr_stream Convert nfs*xdr.c to use an xdr stream in decode_dirent. This will prevent a kernel oops that has been occuring when reading a vmapped page. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/dir.c | 29 ++++++++++----- fs/nfs/internal.h | 6 ++-- fs/nfs/nfs2xdr.c | 39 ++++++++++++++++++--- fs/nfs/nfs3xdr.c | 93 +++++++++++++++++++++++++++++++++++++++++++++---- fs/nfs/nfs4_fs.h | 2 +- fs/nfs/nfs4xdr.c | 71 ++++++++++++++++++++++++++++--------- include/linux/nfs_xdr.h | 2 +- 7 files changed, 202 insertions(+), 40 deletions(-) (limited to 'fs/nfs/nfs4_fs.h') diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index fd30f185ec0..88cbcda7685 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -171,7 +171,7 @@ struct nfs_cache_array { #define MAX_READDIR_ARRAY ((PAGE_SIZE - sizeof(struct nfs_cache_array)) / sizeof(struct nfs_cache_array_entry)) -typedef __be32 * (*decode_dirent_t)(__be32 *, struct nfs_entry *, int); +typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, int); typedef struct { struct file *file; struct page *page; @@ -357,13 +357,11 @@ error: /* Fill in an entry based on the xdr code stored in desc->page */ static -int xdr_decode(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, __be32 **ptr) +int xdr_decode(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, struct xdr_stream *stream) { - __be32 *p = *ptr; - p = desc->decode(p, entry, desc->plus); + __be32 *p = desc->decode(stream, entry, desc->plus); if (IS_ERR(p)) return PTR_ERR(p); - *ptr = p; entry->fattr->time_start = desc->timestamp; entry->fattr->gencount = desc->gencount; @@ -438,10 +436,23 @@ out: /* Perform conversion from xdr to cache array */ static void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, - struct page *xdr_page, struct page *page) + struct page *xdr_page, struct page *page, unsigned int buflen) { + struct xdr_stream stream; + struct xdr_buf buf; __be32 *ptr = kmap(xdr_page); - while (xdr_decode(desc, entry, &ptr) == 0) { + + buf.head->iov_base = xdr_page; + buf.head->iov_len = buflen; + buf.tail->iov_len = 0; + buf.page_base = 0; + buf.page_len = 0; + buf.buflen = buf.head->iov_len; + buf.len = buf.head->iov_len; + + xdr_init_decode(&stream, &buf, ptr); + + while (xdr_decode(desc, entry, &stream) == 0) { if (nfs_readdir_add_to_array(entry, page) == -1) break; if (desc->plus == 1) @@ -458,6 +469,7 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, struct file *file = desc->file; struct nfs_cache_array *array; int status = 0; + unsigned int array_size = 1; entry.prev_cookie = 0; entry.cookie = *desc->dir_cookie; @@ -476,9 +488,10 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, goto out_release_array; do { status = nfs_readdir_xdr_filler(xdr_page, desc, &entry, file, inode); + if (status < 0) break; - nfs_readdir_page_filler(desc, &entry, xdr_page, page); + nfs_readdir_page_filler(desc, &entry, xdr_page, page, array_size * PAGE_SIZE); } while (array->eof_index < 0 && array->size < MAX_READDIR_ARRAY); put_page(xdr_page); diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index c961bc92c10..74b015598a4 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -181,15 +181,15 @@ extern void nfs_destroy_directcache(void); /* nfs2xdr.c */ extern int nfs_stat_to_errno(int); extern struct rpc_procinfo nfs_procedures[]; -extern __be32 * nfs_decode_dirent(__be32 *, struct nfs_entry *, int); +extern __be32 *nfs_decode_dirent(struct xdr_stream *, struct nfs_entry *, int); /* nfs3xdr.c */ extern struct rpc_procinfo nfs3_procedures[]; -extern __be32 *nfs3_decode_dirent(__be32 *, struct nfs_entry *, int); +extern __be32 *nfs3_decode_dirent(struct xdr_stream *, struct nfs_entry *, int); /* nfs4xdr.c */ #ifdef CONFIG_NFS_V4 -extern __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus); +extern __be32 *nfs4_decode_dirent(struct xdr_stream *, struct nfs_entry *entry, int plus); #endif #ifdef CONFIG_NFS_V4_1 extern const u32 nfs41_maxread_overhead; diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 79c74387a2f..0210c752e74 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -500,25 +500,56 @@ err_unmap: goto out; } +static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) +{ + dprintk("nfs: %s: prematurely hit end of receive buffer. " + "Remaining buffer length is %tu words.\n", + func, xdr->end - xdr->p); +} + __be32 * -nfs_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) +nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, int plus) { - if (!*p++) { - if (!*p) + __be32 *p; + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto out_overflow; + if (!ntohl(*p++)) { + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto out_overflow; + if (!ntohl(*p++)) return ERR_PTR(-EAGAIN); entry->eof = 1; return ERR_PTR(-EBADCOOKIE); } + p = xdr_inline_decode(xdr, 8); + if (unlikely(!p)) + goto out_overflow; + entry->ino = ntohl(*p++); entry->len = ntohl(*p++); + + p = xdr_inline_decode(xdr, entry->len + 4); + if (unlikely(!p)) + goto out_overflow; entry->name = (const char *) p; p += XDR_QUADLEN(entry->len); entry->prev_cookie = entry->cookie; entry->cookie = ntohl(*p++); - entry->eof = !p[0] && p[1]; + + p = xdr_inline_peek(xdr, 8); + if (p != NULL) + entry->eof = !p[0] && p[1]; + else + entry->eof = 0; return p; + +out_overflow: + print_overflow_msg(__func__, xdr); + return ERR_PTR(-EIO); } /* diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index 52b2fda66e6..d562c8d9d56 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -100,6 +100,13 @@ static const umode_t nfs_type2fmt[] = { [NF3FIFO] = S_IFIFO, }; +static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) +{ + dprintk("nfs: %s: prematurely hit end of receive buffer. " + "Remaining buffer length is %tu words.\n", + func, xdr->end - xdr->p); +} + /* * Common NFS XDR functions as inlines */ @@ -119,6 +126,29 @@ xdr_decode_fhandle(__be32 *p, struct nfs_fh *fh) return NULL; } +static inline __be32 * +xdr_decode_fhandle_stream(struct xdr_stream *xdr, struct nfs_fh *fh) +{ + __be32 *p; + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto out_overflow; + fh->size = ntohl(*p++); + + if (fh->size <= NFS3_FHSIZE) { + p = xdr_inline_decode(xdr, fh->size); + if (unlikely(!p)) + goto out_overflow; + memcpy(fh->data, p, fh->size); + return p + XDR_QUADLEN(fh->size); + } + return NULL; + +out_overflow: + print_overflow_msg(__func__, xdr); + return ERR_PTR(-EIO); +} + /* * Encode/decode time. */ @@ -240,6 +270,26 @@ xdr_decode_post_op_attr(__be32 *p, struct nfs_fattr *fattr) return p; } +static inline __be32 * +xdr_decode_post_op_attr_stream(struct xdr_stream *xdr, struct nfs_fattr *fattr) +{ + __be32 *p; + + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto out_overflow; + if (ntohl(*p++)) { + p = xdr_inline_decode(xdr, 84); + if (unlikely(!p)) + goto out_overflow; + p = xdr_decode_fattr(p, fattr); + } + return p; +out_overflow: + print_overflow_msg(__func__, xdr); + return ERR_PTR(-EIO); +} + static inline __be32 * xdr_decode_pre_op_attr(__be32 *p, struct nfs_fattr *fattr) { @@ -616,19 +666,33 @@ err_unmap: } __be32 * -nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) +nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, int plus) { + __be32 *p; struct nfs_entry old = *entry; - if (!*p++) { - if (!*p) + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto out_overflow; + if (!ntohl(*p++)) { + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto out_overflow; + if (!ntohl(*p++)) return ERR_PTR(-EAGAIN); entry->eof = 1; return ERR_PTR(-EBADCOOKIE); } + p = xdr_inline_decode(xdr, 12); + if (unlikely(!p)) + goto out_overflow; p = xdr_decode_hyper(p, &entry->ino); entry->len = ntohl(*p++); + + p = xdr_inline_decode(xdr, entry->len + 8); + if (unlikely(!p)) + goto out_overflow; entry->name = (const char *) p; p += XDR_QUADLEN(entry->len); entry->prev_cookie = entry->cookie; @@ -636,10 +700,17 @@ nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) if (plus) { entry->fattr->valid = 0; - p = xdr_decode_post_op_attr(p, entry->fattr); + p = xdr_decode_post_op_attr_stream(xdr, entry->fattr); + if (IS_ERR(p)) + goto out_overflow_exit; /* In fact, a post_op_fh3: */ + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto out_overflow; if (*p++) { - p = xdr_decode_fhandle(p, entry->fh); + p = xdr_decode_fhandle_stream(xdr, entry->fh); + if (IS_ERR(p)) + goto out_overflow_exit; /* Ugh -- server reply was truncated */ if (p == NULL) { dprintk("NFS: FH truncated\n"); @@ -650,8 +721,18 @@ nfs3_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) memset((u8*)(entry->fh), 0, sizeof(*entry->fh)); } - entry->eof = !p[0] && p[1]; + p = xdr_inline_peek(xdr, 8); + if (p != NULL) + entry->eof = !p[0] && p[1]; + else + entry->eof = 0; + return p; + +out_overflow: + print_overflow_msg(__func__, xdr); +out_overflow_exit: + return ERR_PTR(-EIO); } /* diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index d24a8e07b5e..c58ea637750 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -331,7 +331,7 @@ extern void nfs_free_seqid(struct nfs_seqid *seqid); extern const nfs4_stateid zero_stateid; /* nfs4xdr.c */ -extern __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus); +extern __be32 *nfs4_decode_dirent(struct xdr_stream *, struct nfs_entry *entry, int plus); extern struct rpc_procinfo nfs4_procedures[]; struct nfs4_mount_data; diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 6ea5c9392fe..a4919e99935 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -3950,13 +3950,13 @@ static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl) __be32 *p; uint32_t namelen, type; - p = xdr_inline_decode(xdr, 32); + p = xdr_inline_decode(xdr, 32); /* read 32 bytes */ if (unlikely(!p)) goto out_overflow; - p = xdr_decode_hyper(p, &offset); + p = xdr_decode_hyper(p, &offset); /* read 2 8-byte long words */ p = xdr_decode_hyper(p, &length); - type = be32_to_cpup(p++); - if (fl != NULL) { + type = be32_to_cpup(p++); /* 4 byte read */ + if (fl != NULL) { /* manipulate file lock */ fl->fl_start = (loff_t)offset; fl->fl_end = fl->fl_start + (loff_t)length - 1; if (length == ~(uint64_t)0) @@ -3966,9 +3966,9 @@ static int decode_lock_denied (struct xdr_stream *xdr, struct file_lock *fl) fl->fl_type = F_RDLCK; fl->fl_pid = 0; } - p = xdr_decode_hyper(p, &clientid); - namelen = be32_to_cpup(p); - p = xdr_inline_decode(xdr, namelen); + p = xdr_decode_hyper(p, &clientid); /* read 8 bytes */ + namelen = be32_to_cpup(p); /* read 4 bytes */ /* have read all 32 bytes now */ + p = xdr_inline_decode(xdr, namelen); /* variable size field */ if (likely(p)) return -NFS4ERR_DENIED; out_overflow: @@ -5755,21 +5755,33 @@ static int nfs4_xdr_dec_reclaim_complete(struct rpc_rqst *rqstp, uint32_t *p, } #endif /* CONFIG_NFS_V4_1 */ -__be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) +__be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, int plus) { uint32_t bitmap[2] = {0}; uint32_t len; - - if (!*p++) { - if (!*p) + __be32 *p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto out_overflow; + if (!ntohl(*p++)) { + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto out_overflow; + if (!ntohl(*p++)) return ERR_PTR(-EAGAIN); entry->eof = 1; return ERR_PTR(-EBADCOOKIE); } + p = xdr_inline_decode(xdr, 12); + if (unlikely(!p)) + goto out_overflow; entry->prev_cookie = entry->cookie; p = xdr_decode_hyper(p, &entry->cookie); entry->len = ntohl(*p++); + + p = xdr_inline_decode(xdr, entry->len + 4); + if (unlikely(!p)) + goto out_overflow; entry->name = (const char *) p; p += XDR_QUADLEN(entry->len); @@ -5782,29 +5794,54 @@ __be32 *nfs4_decode_dirent(__be32 *p, struct nfs_entry *entry, int plus) len = ntohl(*p++); /* bitmap length */ if (len-- > 0) { + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto out_overflow; bitmap[0] = ntohl(*p++); if (len-- > 0) { + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto out_overflow; bitmap[1] = ntohl(*p++); p += len; } } + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto out_overflow; len = XDR_QUADLEN(ntohl(*p++)); /* attribute buffer length */ if (len > 0) { if (bitmap[0] & FATTR4_WORD0_RDATTR_ERROR) { bitmap[0] &= ~FATTR4_WORD0_RDATTR_ERROR; /* Ignore the return value of rdattr_error for now */ - p++; - len--; + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto out_overflow; } - if (bitmap[0] == 0 && bitmap[1] == FATTR4_WORD1_MOUNTED_ON_FILEID) + if (bitmap[0] == 0 && bitmap[1] == FATTR4_WORD1_MOUNTED_ON_FILEID) { + p = xdr_inline_decode(xdr, 8); + if (unlikely(!p)) + goto out_overflow; xdr_decode_hyper(p, &entry->ino); - else if (bitmap[0] == FATTR4_WORD0_FILEID) + } else if (bitmap[0] == FATTR4_WORD0_FILEID) { + p = xdr_inline_decode(xdr, 8); + if (unlikely(!p)) + goto out_overflow; xdr_decode_hyper(p, &entry->ino); - p += len; + } } - entry->eof = !p[0] && p[1]; + p = xdr_inline_peek(xdr, 8); + if (p != NULL) + entry->eof = !p[0] && p[1]; + else + entry->eof = 0; + return p; + +out_overflow: + print_overflow_msg(__func__, xdr); + return ERR_PTR(-EIO); } /* diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 5772b2c2f06..ca0e8fd7fee 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -1036,7 +1036,7 @@ struct nfs_rpc_ops { int (*pathconf) (struct nfs_server *, struct nfs_fh *, struct nfs_pathconf *); int (*set_capabilities)(struct nfs_server *, struct nfs_fh *); - __be32 *(*decode_dirent)(__be32 *, struct nfs_entry *, int plus); + __be32 *(*decode_dirent)(struct xdr_stream *, struct nfs_entry *, int plus); void (*read_setup) (struct nfs_read_data *, struct rpc_message *); int (*read_done) (struct rpc_task *, struct nfs_read_data *); void (*write_setup) (struct nfs_write_data *, struct rpc_message *); -- cgit v1.2.3 From 82f2e5472e2304e531c2fa85e457f4a71070044e Mon Sep 17 00:00:00 2001 From: Bryan Schumaker Date: Thu, 21 Oct 2010 16:33:18 -0400 Subject: NFS: Readdir plus in v4 By requsting more attributes during a readdir, we can mimic the readdir plus operation that was in NFSv3. To test, I ran the command `ls -lU --color=none` on directories with various numbers of files. Without readdir plus, I see this: n files | 100 | 1,000 | 10,000 | 100,000 | 1,000,000 --------+-----------+-----------+-----------+-----------+---------- real | 0m00.153s | 0m00.589s | 0m05.601s | 0m56.691s | 9m59.128s user | 0m00.007s | 0m00.007s | 0m00.077s | 0m00.703s | 0m06.800s sys | 0m00.010s | 0m00.070s | 0m00.633s | 0m06.423s | 1m10.005s access | 3 | 1 | 1 | 4 | 31 getattr | 2 | 1 | 1 | 1 | 1 lookup | 104 | 1,003 | 10,003 | 100,003 | 1,000,003 readdir | 2 | 16 | 158 | 1,575 | 15,749 total | 111 | 1,021 | 10,163 | 101,583 | 1,015,784 With readdir plus enabled, I see this: n files | 100 | 1,000 | 10,000 | 100,000 | 1,000,000 --------+-----------+-----------+-----------+-----------+---------- real | 0m00.115s | 0m00.206s | 0m01.079s | 0m12.521s | 2m07.528s user | 0m00.003s | 0m00.003s | 0m00.040s | 0m00.290s | 0m03.296s sys | 0m00.007s | 0m00.020s | 0m00.120s | 0m01.357s | 0m17.556s access | 3 | 1 | 1 | 1 | 7 getattr | 2 | 1 | 1 | 1 | 1 lookup | 4 | 3 | 3 | 3 | 3 readdir | 6 | 62 | 630 | 6,300 | 62,993 total | 15 | 67 | 635 | 6,305 | 63,004 Readdir plus disabled has about a 16x increase in the number of rpc calls and is 4 - 5 times slower on large directories. Signed-off-by: Bryan Schumaker Signed-off-by: Trond Myklebust --- fs/nfs/client.c | 5 +++-- fs/nfs/dir.c | 4 ++-- fs/nfs/internal.h | 6 +++--- fs/nfs/nfs2xdr.c | 2 +- fs/nfs/nfs3xdr.c | 2 +- fs/nfs/nfs4_fs.h | 2 +- fs/nfs/nfs4proc.c | 1 + fs/nfs/nfs4xdr.c | 55 ++++++++++++++++++++++++------------------------- include/linux/nfs_xdr.h | 3 ++- 9 files changed, 41 insertions(+), 39 deletions(-) (limited to 'fs/nfs/nfs4_fs.h') diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 876ba859270..da2f2f024a4 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -1358,8 +1358,9 @@ static int nfs4_init_server(struct nfs_server *server, /* Initialise the client representation from the mount data */ server->flags = data->flags; - server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR| - NFS_CAP_POSIX_LOCK; + server->caps |= NFS_CAP_ATOMIC_OPEN|NFS_CAP_CHANGE_ATTR|NFS_CAP_POSIX_LOCK; + if (!(data->flags & NFS_MOUNT_NORDIRPLUS)) + server->caps |= NFS_CAP_READDIRPLUS; server->options = data->options; /* Get a client record */ diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 0cbb714a09d..2a768d05a53 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -172,7 +172,7 @@ struct nfs_cache_array { #define MAX_READDIR_ARRAY ((PAGE_SIZE - sizeof(struct nfs_cache_array)) / sizeof(struct nfs_cache_array_entry)) -typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, int); +typedef __be32 * (*decode_dirent_t)(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int); typedef struct { struct file *file; struct page *page; @@ -360,7 +360,7 @@ error: static int xdr_decode(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, struct xdr_stream *stream) { - __be32 *p = desc->decode(stream, entry, desc->plus); + __be32 *p = desc->decode(stream, entry, NFS_SERVER(desc->file->f_path.dentry->d_inode), desc->plus); if (IS_ERR(p)) return PTR_ERR(p); diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 7b0e894d00c..db08ff3ff45 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -187,15 +187,15 @@ extern void nfs_destroy_directcache(void); /* nfs2xdr.c */ extern int nfs_stat_to_errno(int); extern struct rpc_procinfo nfs_procedures[]; -extern __be32 *nfs_decode_dirent(struct xdr_stream *, struct nfs_entry *, int); +extern __be32 *nfs_decode_dirent(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int); /* nfs3xdr.c */ extern struct rpc_procinfo nfs3_procedures[]; -extern __be32 *nfs3_decode_dirent(struct xdr_stream *, struct nfs_entry *, int); +extern __be32 *nfs3_decode_dirent(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int); /* nfs4xdr.c */ #ifdef CONFIG_NFS_V4 -extern __be32 *nfs4_decode_dirent(struct xdr_stream *, struct nfs_entry *entry, int plus); +extern __be32 *nfs4_decode_dirent(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int); #endif #ifdef CONFIG_NFS_V4_1 extern const u32 nfs41_maxread_overhead; diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index 82f02642242..e6bf45710cc 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c @@ -454,7 +454,7 @@ static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) } __be32 * -nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, int plus) +nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus) { __be32 *p; p = xdr_inline_decode(xdr, 4); diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index dc98eb7976c..31a44df40ae 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c @@ -590,7 +590,7 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, __be32 *p, struct nfs3_readdirres *res } __be32 * -nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, int plus) +nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_server *server, int plus) { __be32 *p; struct nfs_entry old = *entry; diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index c58ea637750..9fa496387fd 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -331,7 +331,7 @@ extern void nfs_free_seqid(struct nfs_seqid *seqid); extern const nfs4_stateid zero_stateid; /* nfs4xdr.c */ -extern __be32 *nfs4_decode_dirent(struct xdr_stream *, struct nfs_entry *entry, int plus); +extern __be32 *nfs4_decode_dirent(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int); extern struct rpc_procinfo nfs4_procedures[]; struct nfs4_mount_data; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index cb33c73206e..f5ab216e887 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -2832,6 +2832,7 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, .pgbase = 0, .count = count, .bitmask = NFS_SERVER(dentry->d_inode)->attr_bitmask, + .plus = plus, }; struct nfs4_readdir_res res; struct rpc_message msg = { diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index b7eff205d3d..ccfb1c92b26 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -1385,12 +1385,20 @@ static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args, static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req, struct compound_hdr *hdr) { - uint32_t attrs[2] = { - FATTR4_WORD0_RDATTR_ERROR|FATTR4_WORD0_FILEID, - FATTR4_WORD1_MOUNTED_ON_FILEID, - }; + uint32_t attrs[2] = {0, 0}; __be32 *p; + if (readdir->plus) { + attrs[0] |= FATTR4_WORD0_TYPE|FATTR4_WORD0_CHANGE|FATTR4_WORD0_SIZE| + FATTR4_WORD0_FSID|FATTR4_WORD0_FILEHANDLE; + attrs[1] |= FATTR4_WORD1_MODE|FATTR4_WORD1_NUMLINKS|FATTR4_WORD1_OWNER| + FATTR4_WORD1_OWNER_GROUP|FATTR4_WORD1_RAWDEV| + FATTR4_WORD1_SPACE_USED|FATTR4_WORD1_TIME_ACCESS| + FATTR4_WORD1_TIME_METADATA|FATTR4_WORD1_TIME_MODIFY; + } + attrs[0] |= FATTR4_WORD0_RDATTR_ERROR|FATTR4_WORD0_FILEID; + attrs[1] |= FATTR4_WORD1_MOUNTED_ON_FILEID; + p = reserve_space(xdr, 12+NFS4_VERIFIER_SIZE+20); *p++ = cpu_to_be32(OP_READDIR); p = xdr_encode_hyper(p, readdir->cookie); @@ -1398,11 +1406,15 @@ static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *p++ = cpu_to_be32(readdir->count >> 1); /* We're not doing readdirplus */ *p++ = cpu_to_be32(readdir->count); *p++ = cpu_to_be32(2); - /* Switch to mounted_on_fileid if the server supports it */ - if (readdir->bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID) - attrs[0] &= ~FATTR4_WORD0_FILEID; - else - attrs[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID; + + if (!readdir->plus) { + /* Switch to mounted_on_fileid if the server supports it */ + if (readdir->bitmask[1] & FATTR4_WORD1_MOUNTED_ON_FILEID) + attrs[0] &= ~FATTR4_WORD0_FILEID; + else + attrs[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID; + } + *p++ = cpu_to_be32(attrs[0] & readdir->bitmask[0]); *p = cpu_to_be32(attrs[1] & readdir->bitmask[1]); hdr->nops++; @@ -5768,7 +5780,8 @@ static int nfs4_xdr_dec_reclaim_complete(struct rpc_rqst *rqstp, uint32_t *p, } #endif /* CONFIG_NFS_V4_1 */ -__be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, int plus) +__be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, + struct nfs_server *server, int plus) { uint32_t bitmap[2] = {0}; uint32_t len; @@ -5824,24 +5837,10 @@ __be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, int goto out_overflow; len = XDR_QUADLEN(ntohl(*p++)); /* attribute buffer length */ if (len > 0) { - if (bitmap[0] & FATTR4_WORD0_RDATTR_ERROR) { - bitmap[0] &= ~FATTR4_WORD0_RDATTR_ERROR; - /* Ignore the return value of rdattr_error for now */ - p = xdr_inline_decode(xdr, 4); - if (unlikely(!p)) - goto out_overflow; - } - if (bitmap[0] == 0 && bitmap[1] == FATTR4_WORD1_MOUNTED_ON_FILEID) { - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - xdr_decode_hyper(p, &entry->ino); - } else if (bitmap[0] == FATTR4_WORD0_FILEID) { - p = xdr_inline_decode(xdr, 8); - if (unlikely(!p)) - goto out_overflow; - xdr_decode_hyper(p, &entry->ino); - } + if (decode_getfattr_attrs(xdr, bitmap, entry->fattr, entry->fh, server, 1) < 0) + goto out_overflow; + if (entry->fattr->valid & NFS_ATTR_FATTR_FILEID) + entry->ino = entry->fattr->fileid; } p = xdr_inline_peek(xdr, 8); diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index 1b9a17a1f23..efe2eab8ac9 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -778,6 +778,7 @@ struct nfs4_readdir_arg { struct page ** pages; /* zero-copy data */ unsigned int pgbase; /* zero-copy data */ const u32 * bitmask; + int plus; struct nfs4_sequence_args seq_args; }; @@ -1036,7 +1037,7 @@ struct nfs_rpc_ops { int (*pathconf) (struct nfs_server *, struct nfs_fh *, struct nfs_pathconf *); int (*set_capabilities)(struct nfs_server *, struct nfs_fh *); - __be32 *(*decode_dirent)(struct xdr_stream *, struct nfs_entry *, int plus); + __be32 *(*decode_dirent)(struct xdr_stream *, struct nfs_entry *, struct nfs_server *, int plus); void (*read_setup) (struct nfs_read_data *, struct rpc_message *); int (*read_done) (struct rpc_task *, struct nfs_read_data *); void (*write_setup) (struct nfs_write_data *, struct rpc_message *); -- cgit v1.2.3