ACL support for the NFSv4 client. fs/nfs/dir.c | 2 fs/nfs/file.c | 2 fs/nfs/inode.c | 87 ++++++++++++++++++++++++++++++++++++ fs/nfs/nfs4proc.c | 115 +++++++++++++++++++++++++++++++++++++++++++++--- fs/nfs/nfs4xdr.c | 77 ++++++++++++++++++++++++++++++-- include/linux/nfs_fs.h | 6 ++ include/linux/nfs_xdr.h | 11 ++++ 7 files changed, 290 insertions(+), 10 deletions(-) diff -puN include/linux/nfs_xdr.h~acl_client include/linux/nfs_xdr.h --- current/include/linux/nfs_xdr.h~acl_client Mon Jun 2 12:54:10 2003 +++ current-marius/include/linux/nfs_xdr.h Mon Jun 2 12:54:10 2003 @@ -39,6 +39,9 @@ struct nfs_fattr { __u64 change_attr; /* NFSv4 change attribute */ __u64 pre_change_attr;/* pre-op NFSv4 change attribute */ unsigned long timestamp; +#ifdef CONFIG_NFS_V4 + struct nfs4_acl *acl; /* NFSv4 ACL */ +#endif /* CONFIG_NFS_V4 */ }; #define NFS_ATTR_WCC 0x0001 /* pre-op WCC data */ @@ -251,6 +254,9 @@ struct nfs_setattrargs { struct iattr * iap; struct nfs4_getattr * attr; struct nfs_server * server; /* Needed for name mapping */ +#ifdef CONFIG_NFS_V4 + struct nfs4_acl * acl; +#endif /* CONFIG_NFS_V4 */ }; struct nfs_setattrres { @@ -451,6 +457,7 @@ struct nfs4_getattr { struct nfs_fsstat * gt_fsstat; /* response */ struct nfs_fsinfo * gt_fsinfo; /* response */ struct nfs_pathconf * gt_pathconf; /* response */ + struct nfs4_acl ** gt_acl; /* response */ u32 * gt_bmres; /* response */ }; @@ -674,6 +681,10 @@ struct nfs_rpc_ops { void (*write_setup) (struct nfs_write_data *, unsigned int count, int how); void (*commit_setup) (struct nfs_write_data *, u64 start, u32 len, int how); int (*file_open) (struct inode *, struct file *); +#ifdef CONFIG_NFS_V4 + struct posix_acl *(*get_posix_acl) (struct inode *, int); + int (*set_posix_acl) (struct inode *, int, struct posix_acl *); +#endif /* CONFIG_NFS_V4 */ }; /* diff -puN include/linux/nfs_fs.h~acl_client include/linux/nfs_fs.h --- current/include/linux/nfs_fs.h~acl_client Mon Jun 2 12:54:10 2003 +++ current-marius/include/linux/nfs_fs.h Mon Jun 2 12:54:10 2003 @@ -22,6 +22,10 @@ #include #include +#ifdef CONFIG_NFS_V4 +#include +#endif /* CONFIG_NFS_V4 */ + #include #include #include @@ -240,11 +244,13 @@ extern struct inode *nfs_fhget(struct de struct nfs_fattr *); extern int __nfs_refresh_inode(struct inode *, struct nfs_fattr *); extern int nfs_getattr(struct vfsmount *, struct dentry *, struct kstat *); +extern ssize_t nfs_getxattr(struct dentry *, const char *, void *, size_t); extern int nfs_permission(struct inode *, int); extern int nfs_open(struct inode *, struct file *); extern int nfs_release(struct inode *, struct file *); extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *); extern int nfs_setattr(struct dentry *, struct iattr *); +extern int nfs_setxattr(struct dentry *, const char *, const void *, size_t, int); /* * linux/fs/nfs/file.c diff -puN fs/nfs/file.c~acl_client fs/nfs/file.c --- current/fs/nfs/file.c~acl_client Mon Jun 2 12:54:10 2003 +++ current-marius/fs/nfs/file.c Mon Jun 2 12:54:10 2003 @@ -61,6 +61,8 @@ struct inode_operations nfs_file_inode_o .permission = nfs_permission, .getattr = nfs_getattr, .setattr = nfs_setattr, + .getxattr = nfs_getxattr, + .setxattr = nfs_setxattr, }; /* Hack for future NFS swap support */ diff -puN fs/nfs/inode.c~acl_client fs/nfs/inode.c --- current/fs/nfs/inode.c~acl_client Mon Jun 2 12:54:10 2003 +++ current-marius/fs/nfs/inode.c Mon Jun 2 12:54:10 2003 @@ -812,6 +812,58 @@ out: return error; } +int +nfs_setxattr(struct dentry *dentry, const char *key, const void *buf, + size_t buflen, int flags) +{ +#ifdef CONFIG_NFS_V4 + struct posix_acl *acl; + int type, error; + struct inode *inode = dentry->d_inode; + + if (NFS_PROTO(inode)->set_posix_acl == NULL) + return (-EOPNOTSUPP); + + if (strlen(key) == sizeof(XATTR_NAME_ACL_ACCESS) - 1 && + memcmp(key, XATTR_NAME_ACL_ACCESS, + sizeof(XATTR_NAME_ACL_ACCESS) - 1) == 0) + type = ACL_TYPE_ACCESS; + else if (strlen(key) == sizeof(XATTR_NAME_ACL_DEFAULT) - 1 && + memcmp(key, XATTR_NAME_ACL_DEFAULT, + sizeof(XATTR_NAME_ACL_ACCESS) - 1) == 0) + type = ACL_TYPE_DEFAULT; + else + return (-EINVAL); + + if (!S_ISREG(inode->i_mode) && + (!S_ISDIR(inode->i_mode) || inode->i_mode & S_ISVTX)) + return (-EPERM); + + /* XXX MARIUS - ACL perms? */ + error = permission(inode, MAY_WRITE); + + if (error) + return (error); + + if (buf == NULL) + return (-EINVAL); + + acl = posix_acl_from_xattr(buf, buflen); + if (IS_ERR(acl)) + return (PTR_ERR(acl)); + if (acl == NULL) + return (-ENODATA); + + error = posix_acl_valid(acl); + if (error) + return (error); + + return (NFS_PROTO(inode)->set_posix_acl(inode, type, acl)); +#else + return (-EOPNOTSUPP); +#endif /* CONFIG_NFS_V4 */ +} + /* * Wait for the inode to get unlocked. * (Used for NFS_INO_LOCKED and NFS_INO_REVALIDATING). @@ -841,6 +893,41 @@ int nfs_getattr(struct vfsmount *mnt, st return err; } +ssize_t +nfs_getxattr(struct dentry *dentry, const char *key, void *buf, + size_t buflen) +{ +#ifdef CONFIG_NFS_V4 + int type = 0; + ssize_t error; + struct inode *inode = dentry->d_inode; + struct posix_acl *acl; + + if (NFS_PROTO(inode)->get_posix_acl == NULL) + return (-EOPNOTSUPP); + + if (strlen(key) == sizeof(XATTR_NAME_ACL_ACCESS) - 1 && + memcmp(key, XATTR_NAME_ACL_ACCESS, + sizeof(XATTR_NAME_ACL_ACCESS) - 1) == 0) + type = ACL_TYPE_ACCESS; + else if (strlen(key) == sizeof(XATTR_NAME_ACL_DEFAULT) - 1 && + memcmp(key, XATTR_NAME_ACL_DEFAULT, + sizeof(XATTR_NAME_ACL_ACCESS) - 1) == 0) + type = ACL_TYPE_DEFAULT; + else + return (-EINVAL); + + acl = NFS_PROTO(inode)->get_posix_acl(inode, type); + + if (IS_ERR(acl)) + return (PTR_ERR(acl)); + + return (posix_acl_to_xattr(acl, buf, buflen)); +#else + return (-EOPNOTSUPP); +#endif /* CONFIG_NFS_V4 */ +} + /* * Ensure that mmap has a recent RPC credential for use when writing out * shared pages diff -puN fs/nfs/nfs4proc.c~acl_client fs/nfs/nfs4proc.c --- current/fs/nfs/nfs4proc.c~acl_client Mon Jun 2 12:54:10 2003 +++ current-marius/fs/nfs/nfs4proc.c Mon Jun 2 12:54:10 2003 @@ -45,6 +45,7 @@ #include #include #include +#include #define NFSDBG_FACILITY NFSDBG_PROC @@ -213,12 +214,18 @@ u32 nfs4_mount_bitmap[2] = { | FATTR4_WORD1_TIME_MODIFY }; +u32 nfs4_acl_bitmap[2] = { + FATTR4_WORD0_ACL, + 0 +}; + static inline void __nfs4_setup_getattr(struct nfs4_compound *cp, u32 *bitmap, struct nfs_fattr *fattr, struct nfs_fsstat *fsstat, struct nfs_fsinfo *fsinfo, struct nfs_pathconf *pathconf, + struct nfs4_acl **acl, u32 *bmres) { struct nfs4_getattr *getattr = GET_OP(cp, getattr); @@ -229,6 +236,7 @@ __nfs4_setup_getattr(struct nfs4_compoun getattr->gt_fsinfo = fsinfo; getattr->gt_pathconf = pathconf; getattr->gt_bmres = bmres; + getattr->gt_acl = acl; OPNUM(cp) = OP_GETATTR; cp->req_nops++; @@ -240,7 +248,7 @@ nfs4_setup_getattr(struct nfs4_compound u32 *bmres) { __nfs4_setup_getattr(cp, nfs4_fattr_bitmap, fattr, - NULL, NULL, NULL, bmres); + NULL, NULL, NULL, NULL, bmres); } static void @@ -250,7 +258,7 @@ nfs4_setup_getrootattr(struct nfs4_compo u32 *bmres) { __nfs4_setup_getattr(cp, nfs4_mount_bitmap, - fattr, NULL, fsinfo, NULL, bmres); + fattr, NULL, fsinfo, NULL, NULL, bmres); } static void @@ -259,7 +267,7 @@ nfs4_setup_statfs(struct nfs4_compound * u32 *bmres) { __nfs4_setup_getattr(cp, nfs4_statfs_bitmap, - NULL, fsstat, NULL, NULL, bmres); + NULL, fsstat, NULL, NULL, NULL, bmres); } static void @@ -268,7 +276,7 @@ nfs4_setup_fsinfo(struct nfs4_compound * u32 *bmres) { __nfs4_setup_getattr(cp, nfs4_fsinfo_bitmap, - NULL, NULL, fsinfo, NULL, bmres); + NULL, NULL, fsinfo, NULL, NULL, bmres); } static void @@ -277,7 +285,14 @@ nfs4_setup_pathconf(struct nfs4_compound u32 *bmres) { __nfs4_setup_getattr(cp, nfs4_pathconf_bitmap, - NULL, NULL, NULL, pathconf, bmres); + NULL, NULL, NULL, pathconf, NULL, bmres); +} + +static void +nfs4_setup_getacl(struct nfs4_compound *cp, struct nfs4_acl **acl, u32 *bmres) +{ + __nfs4_setup_getattr(cp, nfs4_acl_bitmap, + NULL, NULL, NULL, NULL, acl, bmres); } static void @@ -656,7 +671,7 @@ nfs4_do_open(struct inode *dir, struct q }; memcpy(oc_arg.stateid, o_res.stateid, sizeof(nfs4_stateid)); - status = rpc_call_sync(server->client, &msg, 0); + status = rpc_call_sync(server->client, &msg, 0); if (status) goto out_up; nfs4_increment_seqid(status, sp); @@ -898,6 +913,92 @@ no_open: return status; } +static struct posix_acl * +nfs4_proc_get_posix_acl(struct inode *inode, int type) +{ + struct nfs4_compound compound; + struct nfs4_op ops[2]; + struct nfs4_acl *acl; + u32 bmres[2]; + int error; + struct posix_acl *pacl; + + nfs4_setup_compound(&compound, ops, NFS_SERVER(inode), "getattr"); + nfs4_setup_putfh(&compound, NFS_FH(inode)); + nfs4_setup_getacl(&compound, &acl, bmres); + + error = nfs4_call_compound(&compound, NULL, 0); + + if (error < 0 || acl == NULL) + return (error < 0 ? ERR_PTR(error) : ERR_PTR(-ENODATA)); + + if (type == ACL_TYPE_ACCESS) + error = nfs4_acl_nfsv4_to_posix(inode, acl, &pacl, NULL); + else + error = nfs4_acl_nfsv4_to_posix(inode, acl, NULL, &pacl); + + nfs4_acl_free(acl); + + if (error < 0) + pacl = ERR_PTR(error); + + return (pacl); +} + +static int +nfs4_proc_set_posix_acl(struct inode *inode, int type, struct posix_acl *pacl) +{ + struct iattr ia; + u32 g_bmres[2]; + struct nfs4_acl *acl, *xacl; + struct nfs_fattr fattr; + u32 bmval[2]; + int error; + struct nfs4_getattr getattr = { + .gt_bmval = bmval, + .gt_attrs = &fattr, + .gt_bmres = g_bmres, + .gt_acl = &xacl, + }; + struct nfs_setattrargs arg = { + .fh = NFS_FH(inode), + .iap = &ia, + .attr = &getattr, + .server = NFS_SERVER(inode), + }; + struct nfs_setattrres res = { + .attr = &getattr, + .server = NFS_SERVER(inode), + }; + struct rpc_message msg = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_SETATTR], + .rpc_argp = &arg, + .rpc_resp = &res, + }; + + bmval[0] = FATTR4_WORD0_ACL; + bmval[1] = 0; + + ia.ia_valid = 0; + fattr.valid = 0; + + if (type == ACL_TYPE_ACCESS) + acl = nfs4_acl_posix_to_nfsv4(inode, pacl, NULL); + else + acl = nfs4_acl_posix_to_nfsv4(inode, NULL, pacl); + if (IS_ERR(acl)) + return (PTR_ERR(acl)); + + arg.acl = acl; + + error = rpc_call_sync(NFS_SERVER(inode)->client, &msg, 0); + + if (xacl != NULL) + nfs4_acl_free(xacl); + + return (error); +} + static int nfs4_proc_lookup(struct inode *dir, struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr) @@ -1772,6 +1873,8 @@ struct nfs_rpc_ops nfs_v4_clientops = { .write_setup = nfs4_proc_write_setup, .commit_setup = nfs4_proc_commit_setup, .file_open = nfs4_proc_file_open, + .set_posix_acl = nfs4_proc_set_posix_acl, + .get_posix_acl = nfs4_proc_get_posix_acl, }; /* diff -puN fs/nfs/nfs4xdr.c~acl_client fs/nfs/nfs4xdr.c --- current/fs/nfs/nfs4xdr.c~acl_client Mon Jun 2 12:54:10 2003 +++ current-marius/fs/nfs/nfs4xdr.c Mon Jun 2 18:00:59 2003 @@ -51,6 +51,7 @@ #include #include #include +#include #define NFSDBG_FACILITY NFSDBG_XDR @@ -235,7 +236,7 @@ encode_compound_hdr(struct xdr_stream *x } static int -encode_attrs(struct xdr_stream *xdr, struct iattr *iap, +encode_attrs(struct xdr_stream *xdr, struct iattr *iap, struct nfs4_acl *acl, struct nfs_server *server) { char owner_name[256]; @@ -296,6 +297,17 @@ encode_attrs(struct xdr_stream *xdr, str len += 16; else if (iap->ia_valid & ATTR_MTIME) len += 4; + if (acl != NULL) { + struct nfs4_ace *ace; + struct list_head *h; + + len += acl->naces * 16 + 4; + list_for_each(h, &acl->ace_head) { + ace = list_entry(h, struct nfs4_ace, l_ace); + len += XDR_QUADLEN(ace->wholen) << 2; + } + } + RESERVE_SPACE(len); /* @@ -310,6 +322,22 @@ encode_attrs(struct xdr_stream *xdr, str bmval0 |= FATTR4_WORD0_SIZE; WRITE64(iap->ia_size); } + if (acl != NULL) { + struct list_head *h; + struct nfs4_ace *ace; + + bmval0 |= FATTR4_WORD0_ACL; + + WRITE32(acl->naces); + list_for_each(h, &acl->ace_head) { + ace = list_entry(h, struct nfs4_ace, l_ace); + WRITE32(ace->type); + WRITE32(ace->flag); + WRITE32(ace->access_mask); + WRITE32(ace->wholen); + WRITEMEM(ace->who, ace->wholen); + } + } if (iap->ia_valid & ATTR_MODE) { bmval1 |= FATTR4_WORD1_MODE; WRITE32(iap->ia_mode); @@ -434,7 +462,7 @@ encode_create(struct xdr_stream *xdr, st WRITE32(create->cr_namelen); WRITEMEM(create->cr_name, create->cr_namelen); - return encode_attrs(xdr, create->cr_attrs, server); + return encode_attrs(xdr, create->cr_attrs, NULL, server); } static int @@ -577,7 +605,7 @@ encode_open(struct xdr_stream *xdr, stru else if (arg->u.attrs) { RESERVE_SPACE(4); WRITE32(arg->createmode); - if ((status = encode_attrs(xdr, arg->u.attrs, arg->server))) + if ((status = encode_attrs(xdr, arg->u.attrs, NULL, arg->server))) return status; } else { @@ -772,7 +800,7 @@ encode_setattr(struct xdr_stream *xdr, s WRITE32(OP_SETATTR); WRITEMEM(arg->stateid, sizeof(nfs4_stateid)); - if ((status = encode_attrs(xdr, arg->iap, server))) + if ((status = encode_attrs(xdr, arg->iap, arg->acl, server))) return status; return 0; @@ -1389,6 +1417,47 @@ decode_getattr(struct xdr_stream *xdr, s READ32(fsinfo->lease_time); dprintk("read_attrs: lease_time=%d\n", fsinfo->lease_time); } + if (bmval0 & FATTR4_WORD0_ACL) { + struct nfs4_acl *acl; + struct nfs4_ace ace; + int i; + u_int nace; + + if (getattr->gt_acl == NULL) + goto xdr_error; /* XXX MARIUS */ + + READ_BUF(4); len += 4; + READ32(nace); + + if (nace == 0) { + *getattr->gt_acl = NULL; + goto out_acl; + } + + acl = *getattr->gt_acl = nfs4_acl_new(); + if (acl == NULL) { + status = -ENOMEM; + goto out; + } + + for (i = 0; i < nace; i++) { + READ_BUF(16); len += 16; + READ32(ace.type); + READ32(ace.flag); + READ32(ace.access_mask); + READ32(ace.wholen); + READ_BUF(ace.wholen); + len += XDR_QUADLEN(ace.wholen) << 2; + status = nfs4_acl_add_ace(acl, ace.type, ace.flag, + ace.access_mask, (char *)p, ace.wholen); + if (status < 0) + goto out; + p += XDR_QUADLEN(ace.wholen); + } + out_acl: + } else if (getattr->gt_acl != NULL) + *getattr->gt_acl = NULL; + if (bmval0 & FATTR4_WORD0_FILEID) { READ_BUF(8); len += 8; diff -puN fs/nfs/dir.c~acl_client fs/nfs/dir.c --- current/fs/nfs/dir.c~acl_client Mon Jun 2 12:54:10 2003 +++ current-marius/fs/nfs/dir.c Mon Jun 2 12:54:10 2003 @@ -70,6 +70,8 @@ struct inode_operations nfs_dir_inode_op .permission = nfs_permission, .getattr = nfs_getattr, .setattr = nfs_setattr, + .getxattr = nfs_getxattr, + .setxattr = nfs_setxattr, }; /* _