fs/nfs/nfs4proc.c | 76 +++++++++++++++++++------- fs/nfs/nfs4xdr.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/nfs4.h | 6 ++ include/linux/nfs_fs_sb.h | 11 +++ include/linux/nfs_xdr.h | 7 ++ 5 files changed, 213 insertions(+), 20 deletions(-) diff -u --recursive --new-file --show-c-function linux-2.6.5-30-xdr_length/fs/nfs/nfs4proc.c linux-2.6.5-31-server_caps/fs/nfs/nfs4proc.c --- linux-2.6.5-30-xdr_length/fs/nfs/nfs4proc.c 2004-03-25 00:37:33.000000000 -0500 +++ linux-2.6.5-31-server_caps/fs/nfs/nfs4proc.c 2004-03-25 14:27:20.000000000 -0500 @@ -204,7 +204,7 @@ nfs4_open_reclaim(struct nfs4_state_owne .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 q .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 *serve .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, 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) { @@ -592,6 +617,8 @@ static int nfs4_proc_get_root(struct nfs break; } if (status == 0) + status = nfs4_server_capabilities(server, fhandle); + if (status == 0) status = nfs4_do_fsinfo(server, fhandle, info); out: return nfs4_map_errors(status); @@ -599,13 +626,14 @@ out: 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 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 inod 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 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 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_s { 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_ser { 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 { 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 .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)); } diff -u --recursive --new-file --show-c-function linux-2.6.5-30-xdr_length/fs/nfs/nfs4xdr.c linux-2.6.5-31-server_caps/fs/nfs/nfs4xdr.c --- linux-2.6.5-30-xdr_length/fs/nfs/nfs4xdr.c 2004-03-25 00:37:38.000000000 -0500 +++ linux-2.6.5-31-server_caps/fs/nfs/nfs4xdr.c 2004-03-25 14:29:51.000000000 -0500 @@ -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; @@ -1637,6 +1641,28 @@ static int nfs4_xdr_enc_statfs(struct rp } /* + * 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 */ static int nfs4_xdr_enc_renew(struct rpc_rqst *req, uint32_t *p, struct nfs4_client *clp) @@ -1785,6 +1811,17 @@ static inline int decode_attr_length(str 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_s 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 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; @@ -2358,6 +2443,34 @@ static int decode_create(struct xdr_stre 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; @@ -3480,6 +3593,25 @@ static int nfs4_xdr_dec_statfs(struct rp } /* + * 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 */ static int nfs4_xdr_dec_renew(struct rpc_rqst *rqstp, uint32_t *p, void *dummy) @@ -3680,6 +3812,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 = { diff -u --recursive --new-file --show-c-function linux-2.6.5-30-xdr_length/include/linux/nfs4.h linux-2.6.5-31-server_caps/include/linux/nfs4.h --- linux-2.6.5-30-xdr_length/include/linux/nfs4.h 2004-03-25 00:37:21.000000000 -0500 +++ linux-2.6.5-31-server_caps/include/linux/nfs4.h 2004-03-25 14:31:59.000000000 -0500 @@ -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 diff -u --recursive --new-file --show-c-function linux-2.6.5-30-xdr_length/include/linux/nfs_fs_sb.h linux-2.6.5-31-server_caps/include/linux/nfs_fs_sb.h --- linux-2.6.5-30-xdr_length/include/linux/nfs_fs_sb.h 2004-03-25 00:30:15.000000000 -0500 +++ linux-2.6.5-31-server_caps/include/linux/nfs_fs_sb.h 2004-03-25 14:22:13.000000000 -0500 @@ -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 diff -u --recursive --new-file --show-c-function linux-2.6.5-30-xdr_length/include/linux/nfs_xdr.h linux-2.6.5-31-server_caps/include/linux/nfs_xdr.h --- linux-2.6.5-30-xdr_length/include/linux/nfs_xdr.h 2004-03-25 00:37:33.000000000 -0500 +++ linux-2.6.5-31-server_caps/include/linux/nfs_xdr.h 2004-03-25 14:20:48.000000000 -0500 @@ -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;