Commit 4e2cf619 authored by Trond Myklebust's avatar Trond Myklebust

NFSv4: Check server capabilities at mount time so that we can optimize away

requests for attributes that are not supported. In particular, we wish to
determine whether or not the server supports ACLs.
parent 64596af0
......@@ -204,7 +204,7 @@ nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state)
.share_access = state->state,
.clientid = server->nfs4_state->cl_clientid,
.claim = NFS4_OPEN_CLAIM_PREVIOUS,
.bitmask = nfs4_fattr_bitmap,
.bitmask = server->attr_bitmask,
};
struct nfs_openres o_res = {
.f_attr = &fattr,
......@@ -248,7 +248,7 @@ nfs4_do_open(struct inode *dir, struct qstr *name, int flags, struct iattr *satt
.createmode = (flags & O_EXCL) ? NFS4_CREATE_EXCLUSIVE : NFS4_CREATE_UNCHECKED,
.name = name,
.server = server,
.bitmask = nfs4_fattr_bitmap,
.bitmask = server->attr_bitmask,
};
struct nfs_openres o_res = {
.f_attr = &f_attr,
......@@ -370,7 +370,7 @@ nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
.fh = fhandle,
.iap = sattr,
.server = server,
.bitmask = nfs4_fattr_bitmap,
.bitmask = server->attr_bitmask,
};
struct nfs_setattrres res = {
.fattr = fattr,
......@@ -517,6 +517,31 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags)
return 0;
}
static int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle)
{
struct nfs4_server_caps_res res = {};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SERVER_CAPS],
.rpc_argp = fhandle,
.rpc_resp = &res,
};
int status;
status = rpc_call_sync(server->client, &msg, 0);
if (status == 0) {
memcpy(server->attr_bitmask, res.attr_bitmask, sizeof(server->attr_bitmask));
if (res.attr_bitmask[0] & FATTR4_WORD0_ACL)
server->caps |= NFS_CAP_ACLS;
if (res.has_links != 0)
server->caps |= NFS_CAP_HARDLINKS;
if (res.has_symlinks != 0)
server->caps |= NFS_CAP_SYMLINKS;
server->acl_bitmask = res.acl_bitmask;
}
return status;
}
static int nfs4_lookup_root(struct nfs_server *server, struct nfs_fh *fhandle,
struct nfs_fsinfo *info)
{
......@@ -591,6 +616,8 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
}
break;
}
if (status == 0)
status = nfs4_server_capabilities(server, fhandle);
if (status == 0)
status = nfs4_do_fsinfo(server, fhandle, info);
out:
......@@ -599,13 +626,14 @@ static int nfs4_proc_get_root(struct nfs_server *server, struct nfs_fh *fhandle,
static int nfs4_proc_getattr(struct inode *inode, struct nfs_fattr *fattr)
{
struct nfs_server *server = NFS_SERVER(inode);
struct nfs4_getattr_arg args = {
.fh = NFS_FH(inode),
.bitmask = nfs4_fattr_bitmap,
.bitmask = server->attr_bitmask,
};
struct nfs4_getattr_res res = {
.fattr = fattr,
.server = NFS_SERVER(inode),
.server = server,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETATTR],
......@@ -681,13 +709,14 @@ static int nfs4_proc_lookup(struct inode *dir, struct qstr *name,
struct nfs_fh *fhandle, struct nfs_fattr *fattr)
{
int status;
struct nfs_server *server = NFS_SERVER(dir);
struct nfs4_lookup_arg args = {
.bitmask = nfs4_fattr_bitmap,
.bitmask = server->attr_bitmask,
.dir_fh = NFS_FH(dir),
.name = name,
};
struct nfs4_lookup_res res = {
.server = NFS_SERVER(dir),
.server = server,
.fattr = fattr,
.fh = fhandle,
};
......@@ -1046,16 +1075,17 @@ static int nfs4_proc_symlink(struct inode *dir, struct qstr *name,
struct qstr *path, struct iattr *sattr, struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
struct nfs_server *server = NFS_SERVER(dir);
struct nfs4_create_arg arg = {
.dir_fh = NFS_FH(dir),
.server = NFS_SERVER(dir),
.server = server,
.name = name,
.attrs = sattr,
.ftype = NF4LNK,
.bitmask = nfs4_fattr_bitmap,
.bitmask = server->attr_bitmask,
};
struct nfs4_create_res res = {
.server = NFS_SERVER(dir),
.server = server,
.fh = fhandle,
.fattr = fattr,
};
......@@ -1079,16 +1109,17 @@ static int nfs4_proc_mkdir(struct inode *dir, struct qstr *name,
struct iattr *sattr, struct nfs_fh *fhandle,
struct nfs_fattr *fattr)
{
struct nfs_server *server = NFS_SERVER(dir);
struct nfs4_create_arg arg = {
.dir_fh = NFS_FH(dir),
.server = NFS_SERVER(dir),
.server = server,
.name = name,
.attrs = sattr,
.ftype = NF4DIR,
.bitmask = nfs4_fattr_bitmap,
.bitmask = server->attr_bitmask,
};
struct nfs4_create_res res = {
.server = NFS_SERVER(dir),
.server = server,
.fh = fhandle,
.fattr = fattr,
};
......@@ -1140,15 +1171,16 @@ static int nfs4_proc_mknod(struct inode *dir, struct qstr *name,
struct iattr *sattr, dev_t rdev, struct nfs_fh *fh,
struct nfs_fattr *fattr)
{
struct nfs_server *server = NFS_SERVER(dir);
struct nfs4_create_arg arg = {
.dir_fh = NFS_FH(dir),
.server = NFS_SERVER(dir),
.server = server,
.name = name,
.attrs = sattr,
.bitmask = nfs4_fattr_bitmap,
.bitmask = server->attr_bitmask,
};
struct nfs4_create_res res = {
.server = NFS_SERVER(dir),
.server = server,
.fh = fh,
.fattr = fattr,
};
......@@ -1190,7 +1222,7 @@ static int nfs4_proc_statfs(struct nfs_server *server, struct nfs_fh *fhandle,
{
struct nfs4_statfs_arg args = {
.fh = fhandle,
.bitmask = nfs4_statfs_bitmap,
.bitmask = server->attr_bitmask,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_STATFS],
......@@ -1207,7 +1239,7 @@ static int nfs4_do_fsinfo(struct nfs_server *server, struct nfs_fh *fhandle,
{
struct nfs4_fsinfo_arg args = {
.fh = fhandle,
.bitmask = nfs4_fsinfo_bitmap,
.bitmask = server->attr_bitmask,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_FSINFO],
......@@ -1229,7 +1261,7 @@ static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
{
struct nfs4_pathconf_arg args = {
.fh = fhandle,
.bitmask = nfs4_pathconf_bitmap,
.bitmask = server->attr_bitmask,
};
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_PATHCONF],
......@@ -1237,6 +1269,12 @@ static int nfs4_proc_pathconf(struct nfs_server *server, struct nfs_fh *fhandle,
.rpc_resp = pathconf,
};
/* None of the pathconf attributes are mandatory to implement */
if ((args.bitmask[0] & nfs4_pathconf_bitmap[0]) == 0) {
memset(pathconf, 0, sizeof(*pathconf));
return 0;
}
pathconf->fattr->valid = 0;
return nfs4_map_errors(rpc_call_sync(server->client, &msg, 0));
}
......
......@@ -335,6 +335,10 @@ static int nfs_stat_to_errno(int);
#define NFS4_dec_statfs_sz (compound_decode_hdr_maxsz + \
decode_putfh_maxsz + \
op_decode_hdr_maxsz + 12)
#define NFS4_enc_server_caps_sz (compound_encode_hdr_maxsz + \
encode_getattr_maxsz)
#define NFS4_dec_server_caps_sz (compound_decode_hdr_maxsz + \
decode_getattr_maxsz)
static struct {
unsigned int mode;
......@@ -1636,6 +1640,28 @@ static int nfs4_xdr_enc_statfs(struct rpc_rqst *req, uint32_t *p, const struct n
return status;
}
/*
* GETATTR_BITMAP request
*/
static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req, uint32_t *p, const struct nfs_fh *fhandle)
{
struct xdr_stream xdr;
struct compound_hdr hdr = {
.nops = 2,
};
int status;
xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_compound_hdr(&xdr, &hdr);
status = encode_putfh(&xdr, fhandle);
if (status == 0)
status = encode_getattr_one(&xdr, FATTR4_WORD0_SUPPORTED_ATTRS|
FATTR4_WORD0_LINK_SUPPORT|
FATTR4_WORD0_SYMLINK_SUPPORT|
FATTR4_WORD0_ACLSUPPORT);
return status;
}
/*
* a RENEW request
*/
......@@ -1785,6 +1811,17 @@ static inline int decode_attr_length(struct xdr_stream *xdr, uint32_t *attrlen,
return 0;
}
static int decode_attr_supported(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *bitmask)
{
if (likely(bitmap[0] & FATTR4_WORD0_SUPPORTED_ATTRS)) {
decode_attr_bitmap(xdr, bitmask);
bitmap[0] &= ~FATTR4_WORD0_SUPPORTED_ATTRS;
} else
bitmask[0] = bitmask[1] = 0;
dprintk("%s: bitmask=0x%x%x\n", __FUNCTION__, bitmask[0], bitmask[1]);
return 0;
}
static int decode_attr_type(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *type)
{
uint32_t *p;
......@@ -1838,6 +1875,38 @@ static int decode_attr_size(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *
return 0;
}
static int decode_attr_link_support(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
{
uint32_t *p;
*res = 0;
if (unlikely(bitmap[0] & (FATTR4_WORD0_LINK_SUPPORT - 1U)))
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_LINK_SUPPORT)) {
READ_BUF(4);
READ32(*res);
bitmap[0] &= ~FATTR4_WORD0_LINK_SUPPORT;
}
dprintk("%s: link support=%s\n", __FUNCTION__, *res == 0 ? "false" : "true");
return 0;
}
static int decode_attr_symlink_support(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
{
uint32_t *p;
*res = 0;
if (unlikely(bitmap[0] & (FATTR4_WORD0_SYMLINK_SUPPORT - 1U)))
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_SYMLINK_SUPPORT)) {
READ_BUF(4);
READ32(*res);
bitmap[0] &= ~FATTR4_WORD0_SYMLINK_SUPPORT;
}
dprintk("%s: symlink support=%s\n", __FUNCTION__, *res == 0 ? "false" : "true");
return 0;
}
static int decode_attr_fsid(struct xdr_stream *xdr, uint32_t *bitmap, struct nfs4_fsid *fsid)
{
uint32_t *p;
......@@ -1874,6 +1943,22 @@ static int decode_attr_lease_time(struct xdr_stream *xdr, uint32_t *bitmap, uint
return 0;
}
static int decode_attr_aclsupport(struct xdr_stream *xdr, uint32_t *bitmap, uint32_t *res)
{
uint32_t *p;
*res = ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL;
if (unlikely(bitmap[0] & (FATTR4_WORD0_ACLSUPPORT - 1U)))
return -EIO;
if (likely(bitmap[0] & FATTR4_WORD0_ACLSUPPORT)) {
READ_BUF(4);
READ32(*res);
bitmap[0] &= ~FATTR4_WORD0_ACLSUPPORT;
}
dprintk("%s: ACLs supported=%u\n", __FUNCTION__, (unsigned int)*res);
return 0;
}
static int decode_attr_fileid(struct xdr_stream *xdr, uint32_t *bitmap, uint64_t *fileid)
{
uint32_t *p;
......@@ -2354,6 +2439,34 @@ static int decode_create(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
return 0;
}
static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_res *res)
{
uint32_t *savep;
uint32_t attrlen,
bitmap[2] = {0};
int status;
if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
goto xdr_error;
if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
goto xdr_error;
if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
goto xdr_error;
if ((status = decode_attr_supported(xdr, bitmap, res->attr_bitmask)) != 0)
goto xdr_error;
if ((status = decode_attr_link_support(xdr, bitmap, &res->has_links)) != 0)
goto xdr_error;
if ((status = decode_attr_symlink_support(xdr, bitmap, &res->has_symlinks)) != 0)
goto xdr_error;
if ((status = decode_attr_aclsupport(xdr, bitmap, &res->acl_bitmask)) != 0)
goto xdr_error;
status = verify_attr_len(xdr, savep, attrlen);
xdr_error:
if (status != 0)
printk(KERN_NOTICE "%s: xdr error %d!\n", __FUNCTION__, -status);
return status;
}
static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat)
{
uint32_t *savep;
......@@ -3475,6 +3588,25 @@ static int nfs4_xdr_dec_statfs(struct rpc_rqst *req, uint32_t *p, struct nfs_fss
return status;
}
/*
* GETATTR_BITMAP request
*/
static int nfs4_xdr_dec_server_caps(struct rpc_rqst *req, uint32_t *p, struct nfs4_server_caps_res *res)
{
struct xdr_stream xdr;
struct compound_hdr hdr;
int status;
xdr_init_decode(&xdr, &req->rq_rcv_buf, p);
if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
goto out;
if ((status = decode_putfh(&xdr)) != 0)
goto out;
status = decode_server_caps(&xdr, res);
out:
return status;
}
/*
* Decode RENEW response
*/
......@@ -3676,6 +3808,7 @@ struct rpc_procinfo nfs4_procedures[] = {
PROC(STATFS, enc_statfs, dec_statfs),
PROC(READLINK, enc_readlink, dec_readlink),
PROC(READDIR, enc_readdir, dec_readdir),
PROC(SERVER_CAPS, enc_server_caps, dec_server_caps),
};
struct rpc_version nfs_version4 = {
......
......@@ -47,6 +47,11 @@
#define NFS4_ACE_SYSTEM_AUDIT_ACE_TYPE 2
#define NFS4_ACE_SYSTEM_ALARM_ACE_TYPE 3
#define ACL4_SUPPORT_ALLOW_ACL 0x01
#define ACL4_SUPPORT_DENY_ACL 0x02
#define ACL4_SUPPORT_AUDIT_ACL 0x04
#define ACL4_SUPPORT_ALARM_ACL 0x08
typedef struct { char data[NFS4_VERIFIER_SIZE]; } nfs4_verifier;
typedef struct { char data[16]; } nfs4_stateid;
......@@ -314,6 +319,7 @@ enum {
NFSPROC4_CLNT_STATFS,
NFSPROC4_CLNT_READLINK,
NFSPROC4_CLNT_READDIR,
NFSPROC4_CLNT_SERVER_CAPS,
};
#endif
......
......@@ -38,10 +38,19 @@ struct nfs_server {
struct list_head nfs4_siblings; /* List of other nfs_server structs
* that share the same clientid
*/
u32 attr_bitmask[2];/* V4 bitmask representing the set
of attributes supported on this
filesystem */
u32 acl_bitmask; /* V4 bitmask representing the ACEs
that are supported on this
filesystem */
#endif
};
/* Server capabilities */
#define NFS_CAP_READDIRPLUS (1)
#define NFS_CAP_READDIRPLUS (1U << 0)
#define NFS_CAP_HARDLINKS (1U << 1)
#define NFS_CAP_SYMLINKS (1U << 2)
#define NFS_CAP_ACLS (1U << 3)
#endif
......@@ -611,6 +611,13 @@ struct nfs4_statfs_arg {
const u32 * bitmask;
};
struct nfs4_server_caps_res {
u32 attr_bitmask[2];
u32 acl_bitmask;
u32 has_links;
u32 has_symlinks;
};
#endif /* CONFIG_NFS_V4 */
struct nfs_page;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment