From: Andreas Gruenbacher This adds acl support fo nfs clients via the NFSACL protocol extension, by implementing the getxattr, listxattr, setxattr, and removexattr iops for the system.posix_acl_access and system.posix_acl_default attributes. This patch implements a dumb version that uses no caching (and thus adds some overhead). (Another patch in this patchset adds caching as well.) Signed-off-by: Andreas Gruenbacher Acked-by: Olaf Kirch Signed-off-by: Andrew Morton --- 25-akpm/fs/Kconfig | 12 ++ 25-akpm/fs/nfs/Makefile | 1 25-akpm/fs/nfs/dir.c | 4 25-akpm/fs/nfs/file.c | 4 25-akpm/fs/nfs/inode.c | 55 +++++++++ 25-akpm/fs/nfs/nfs3proc.c | 215 +++++++++++++++++++++++++++++++++++++- 25-akpm/fs/nfs/nfs3xdr.c | 138 ++++++++++++++++++++++++ 25-akpm/fs/nfs/xattr.c | 125 ++++++++++++++++++++++ 25-akpm/include/linux/nfs_fs.h | 16 ++ 25-akpm/include/linux/nfs_fs_sb.h | 3 25-akpm/include/linux/nfs_mount.h | 3 25-akpm/include/linux/nfs_xdr.h | 32 +++++ 12 files changed, 606 insertions(+), 2 deletions(-) diff -puN fs/Kconfig~nfsacl-client-side-of-nfsacl fs/Kconfig --- 25/fs/Kconfig~nfsacl-client-side-of-nfsacl 2005-01-23 01:27:58.122470368 -0800 +++ 25-akpm/fs/Kconfig 2005-01-23 01:27:58.157465048 -0800 @@ -1330,6 +1330,7 @@ config NFS_FS depends on INET select LOCKD select SUNRPC + select NFS_ACL_SUPPORT if NFS_ACL help If you are connected to some other (usually local) Unix computer (using SLIP, PLIP, PPP or Ethernet) and want to mount files residing @@ -1372,6 +1373,17 @@ config NFS_V3 If unsure, say Y. +config NFS_ACL + bool "NFS_ACL protocol extension" + depends on NFS_V3 + select QSORT + help + Implement the NFS_ACL protocol extension for manipulating POSIX + Access Control Lists. The server must also implement the NFS_ACL + protocol extension; see the CONFIG_NFSD_ACL option. + + If unsure, say N. + config NFS_V4 bool "Provide NFSv4 client support (EXPERIMENTAL)" depends on NFS_FS && EXPERIMENTAL diff -puN fs/nfs/dir.c~nfsacl-client-side-of-nfsacl fs/nfs/dir.c --- 25/fs/nfs/dir.c~nfsacl-client-side-of-nfsacl 2005-01-23 01:27:58.137468088 -0800 +++ 25-akpm/fs/nfs/dir.c 2005-01-23 01:27:58.161464440 -0800 @@ -72,6 +72,10 @@ struct inode_operations nfs_dir_inode_op .permission = nfs_permission, .getattr = nfs_getattr, .setattr = nfs_setattr, + .listxattr = nfs_listxattr, + .getxattr = nfs_getxattr, + .setxattr = nfs_setxattr, + .removexattr = nfs_removexattr, }; #ifdef CONFIG_NFS_V4 diff -puN fs/nfs/file.c~nfsacl-client-side-of-nfsacl fs/nfs/file.c --- 25/fs/nfs/file.c~nfsacl-client-side-of-nfsacl 2005-01-23 01:27:58.139467784 -0800 +++ 25-akpm/fs/nfs/file.c 2005-01-23 01:27:58.162464288 -0800 @@ -68,6 +68,10 @@ struct inode_operations nfs_file_inode_o .permission = nfs_permission, .getattr = nfs_getattr, .setattr = nfs_setattr, + .listxattr = nfs_listxattr, + .getxattr = nfs_getxattr, + .setxattr = nfs_setxattr, + .removexattr = nfs_removexattr, }; /* Hack for future NFS swap support */ diff -puN fs/nfs/inode.c~nfsacl-client-side-of-nfsacl fs/nfs/inode.c --- 25/fs/nfs/inode.c~nfsacl-client-side-of-nfsacl 2005-01-23 01:27:58.140467632 -0800 +++ 25-akpm/fs/nfs/inode.c 2005-01-23 01:27:58.164463984 -0800 @@ -104,6 +104,21 @@ struct rpc_program nfs_program = { .pipe_dir_name = "/nfs", }; +#ifdef CONFIG_NFS_ACL +static struct rpc_stat nfsacl_rpcstat = { &nfsacl_program }; +static struct rpc_version * nfsacl_version[] = { + [3] = &nfsacl_version3, +}; + +struct rpc_program nfsacl_program = { + .name = "nfsacl", + .number = NFS3_ACL_PROGRAM, + .nrvers = sizeof(nfsacl_version) / sizeof(nfsacl_version[0]), + .version = nfsacl_version, + .stats = &nfsacl_rpcstat, +}; +#endif /* CONFIG_NFS_ACL */ + static inline unsigned long nfs_fattr_to_ino_t(struct nfs_fattr *fattr) { @@ -165,6 +180,10 @@ nfs_umount_begin(struct super_block *sb) /* -EIO all pending I/O */ if ((rpc = server->client) != NULL) rpc_killall_tasks(rpc); +#ifdef CONFIG_NFS_ACL + if ((rpc = server->client_acl) != NULL) + rpc_killall_tasks(rpc); +#endif /* CONFIG_NFS_ACL */ } @@ -453,7 +472,21 @@ nfs_fill_super(struct super_block *sb, s atomic_inc(&server->client->cl_count); server->client_sys = server->client; } +#ifdef CONFIG_NFS_ACL + if (server->flags & NFS_MOUNT_VER3) { + struct rpc_clnt *clnt = rpc_clone_client(server->client); + if (IS_ERR(clnt)) { + rpc_release_client(server->client_sys); + server->client_sys = NULL; + return PTR_ERR(clnt); + } + rpc_change_program(clnt, &nfsacl_program, 3); + server->client_acl = clnt; + /* Initially assume the nfsacl program is supported */ + server->flags |= NFSACL; + } +#endif if (server->flags & NFS_MOUNT_VER3) { if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) server->namelen = NFS3_MAXNAMLEN; @@ -640,6 +673,20 @@ nfs_init_locked(struct inode *inode, voi /* Don't use READDIRPLUS on directories that we believe are too large */ #define NFS_LIMIT_READDIRPLUS (8*PAGE_SIZE) +#ifdef CONFIG_NFS_ACL +static struct inode_operations nfs_special_inode_operations[] = {{ + .permission = nfs_permission, + .getattr = nfs_getattr, + .setattr = nfs_setattr, + .listxattr = nfs_listxattr, + .getxattr = nfs_getxattr, + .setxattr = nfs_setxattr, + .removexattr = nfs_removexattr, +}}; +#else +#define nfs_special_inode_operations NULL +#endif /* CONFIG_NFS_ACL */ + /* * This is our front-end to iget that looks up inodes by file handle * instead of inode number. @@ -693,8 +740,10 @@ nfs_fhget(struct super_block *sb, struct NFS_FLAGS(inode) |= NFS_INO_ADVISE_RDPLUS; } else if (S_ISLNK(inode->i_mode)) inode->i_op = &nfs_symlink_inode_operations; - else + else { + inode->i_op = nfs_special_inode_operations; init_special_inode(inode, inode->i_mode, fattr->rdev); + } nfsi->read_cache_jiffies = fattr->timestamp; inode->i_atime = fattr->atime; @@ -1458,6 +1507,10 @@ static void nfs_kill_super(struct super_ rpc_shutdown_client(server->client); if (server->client_sys != NULL && !IS_ERR(server->client_sys)) rpc_shutdown_client(server->client_sys); +#ifdef CONFIG_NFS_ACL + if (server->client_acl != NULL && !IS_ERR(server->client_acl)) + rpc_shutdown_client(server->client_acl); +#endif if (!(server->flags & NFS_MOUNT_NONLM)) lockd_down(); /* release rpc.lockd */ diff -puN fs/nfs/Makefile~nfsacl-client-side-of-nfsacl fs/nfs/Makefile --- 25/fs/nfs/Makefile~nfsacl-client-side-of-nfsacl 2005-01-23 01:27:58.142467328 -0800 +++ 25-akpm/fs/nfs/Makefile 2005-01-23 01:27:58.158464896 -0800 @@ -8,6 +8,7 @@ nfs-y := dir.o file.o inode.o nfs2xdr proc.o read.o symlink.o unlink.o write.o nfs-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o nfs-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o +nfs-$(CONFIG_NFS_ACL) += xattr.o nfs-$(CONFIG_NFS_V4) += nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o \ delegation.o idmap.o \ callback.o callback_xdr.o callback_proc.o diff -puN fs/nfs/nfs3proc.c~nfsacl-client-side-of-nfsacl fs/nfs/nfs3proc.c --- 25/fs/nfs/nfs3proc.c~nfsacl-client-side-of-nfsacl 2005-01-23 01:27:58.144467024 -0800 +++ 25-akpm/fs/nfs/nfs3proc.c 2005-01-23 01:27:58.166463680 -0800 @@ -17,6 +17,7 @@ #include #include #include +#include #define NFSDBG_FACILITY NFSDBG_PROC @@ -45,7 +46,7 @@ static inline int nfs3_rpc_call_wrapper(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp, int flags) { struct rpc_message msg = { - .rpc_proc = &nfs3_procedures[proc], + .rpc_proc = &clnt->cl_procinfo[proc], .rpc_argp = argp, .rpc_resp = resp, }; @@ -714,6 +715,213 @@ nfs3_proc_pathconf(struct nfs_server *se return status; } +#ifdef CONFIG_NFS_ACL +static struct posix_acl * +nfs3_proc_getacl(struct inode *inode, int type) +{ + struct nfs_server *server = NFS_SERVER(inode); + struct nfs_fattr fattr; + struct page *pages[NFSACL_MAXPAGES] = { }; + struct nfs3_getaclargs args = { + /* The xdr layer may allocate pages here. */ + .pages = pages, + }; + struct nfs3_getaclres res = { + .fattr = &fattr, + }; + struct posix_acl *acl = NULL; + int status, count; + + if (!(server->flags & NFSACL) || (server->flags & NFS_MOUNT_NOACL)) + return ERR_PTR(-EOPNOTSUPP); + + switch (type) { + case ACL_TYPE_ACCESS: + args.mask = NFS3_ACLCNT|NFS3_ACL; + break; + + case ACL_TYPE_DEFAULT: + if (!S_ISDIR(inode->i_mode)) + return NULL; + args.mask = NFS3_DFACLCNT|NFS3_DFACL; + + default: + return -EINVAL; + } + args.fh = NFS_FH(inode); + + dprintk("NFS call getacl\n"); + status = rpc_call(server->client_acl, NFS3PROC_GETACL, + &args, &res, 0); + dprintk("NFS reply getacl: %d\n", status); + + /* pages may have been allocated at the xdr layer. */ + for (count = 0; count < NFSACL_MAXPAGES && args.pages[count]; count++) + __free_page(args.pages[count]); + + if (status) { + if (status == -ENOSYS) { + dprintk("NFS_ACL extension not supported; disabling\n"); + server->flags &= ~NFSACL; + status = -EOPNOTSUPP; + } else if (status == -ENOTSUPP) + status = -EOPNOTSUPP; + goto getout; + } + if ((args.mask & res.mask) != args.mask) { + status = -EIO; + goto getout; + } + + status = nfs_refresh_inode(inode, &fattr); + if (res.acl_access) { + if (posix_acl_equiv_mode(res.acl_access, NULL) == 0) { + posix_acl_release(res.acl_access); + res.acl_access = NULL; + } + } + + switch(type) { + case ACL_TYPE_ACCESS: + acl = res.acl_access; + res.acl_access = NULL; + break; + + case ACL_TYPE_DEFAULT: + acl = res.acl_default; + res.acl_default = NULL; + break; + } + +getout: + posix_acl_release(res.acl_access); + posix_acl_release(res.acl_default); + + if (status) { + posix_acl_release(acl); + acl = ERR_PTR(status); + } + return acl; +} +#endif /* CONFIG_NFS_ACL */ + +#ifdef CONFIG_NFS_ACL +static int +nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl, + struct posix_acl *dfacl) +{ + struct nfs_server *server = NFS_SERVER(inode); + struct nfs_fattr fattr; + struct page *pages[NFSACL_MAXPAGES] = { }; + struct nfs3_setaclargs args = { + .pages = pages, + }; + int status, count; + + if (!(server->flags & NFSACL) || (server->flags & NFS_MOUNT_NOACL)) + return -EOPNOTSUPP; + + /* We are doing this here, because XDR marshalling can only + return -ENOMEM. */ + if (acl && acl->a_count > NFS3_ACL_MAX_ENTRIES) + return -ENOSPC; + if (dfacl && dfacl->a_count > NFS3_ACL_MAX_ENTRIES) + return -ENOSPC; + args.inode = inode; + args.mask = NFS3_ACL; + args.acl_access = acl; + if (S_ISDIR(inode->i_mode)) { + args.mask |= NFS3_DFACL; + args.acl_default = dfacl; + } + + dprintk("NFS call setacl\n"); + status = rpc_call(server->client_acl, NFS3PROC_SETACL, + &args, &fattr, 0); + dprintk("NFS reply setacl: %d\n", status); + + /* pages may have been allocated at the xdr layer. */ + for (count = 0; count < NFSACL_MAXPAGES && args.pages[count]; count++) + __free_page(args.pages[count]); + + if (status) { + if (status == -ENOSYS) { + dprintk("NFS_ACL SETACL RPC not supported" + "(will not retry)\n"); + server->flags &= ~NFSACL; + status = -EOPNOTSUPP; + } else if (status == -ENOTSUPP) + status = -EOPNOTSUPP; + } else { + NFS_FLAGS(inode) |= NFS_INO_INVALID_ACCESS; + if (acl) { + /* + * Updating the access acl modifies the file mode + * mode permission bits, so update the icache. + */ + mode_t mode = inode->i_mode; + int error = posix_acl_equiv_mode(acl, &mode); + if (error >= 0) + inode->i_mode = mode; + if (error == 0) { + /* + * The acl is equivalent to the file mode + * permission bits. No need to cache it. + */ + acl = NULL; + } + } + status = nfs_refresh_inode(inode, &fattr); + } + + return status; +} +#endif /* CONFIG_NFS_ACL */ + +#ifdef CONFIG_NFS_ACL +static int +nfs3_proc_setacl(struct inode *inode, int type, struct posix_acl *acl) +{ + struct posix_acl *alloc = NULL, *dfacl = NULL; + int status; + + if (S_ISDIR(inode->i_mode)) { + switch(type) { + case ACL_TYPE_ACCESS: + alloc = dfacl = NFS_PROTO(inode)-> + getacl(inode, ACL_TYPE_DEFAULT); + if (IS_ERR(alloc)) + goto fail; + break; + + case ACL_TYPE_DEFAULT: + dfacl = acl; + alloc = acl = NFS_PROTO(inode)-> + getacl(inode, ACL_TYPE_ACCESS); + if (IS_ERR(alloc)) + goto fail; + break; + + default: + return -EINVAL; + } + } else if (type != ACL_TYPE_ACCESS) + return -EINVAL; + + if (acl == NULL) { + alloc = acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL); + if (IS_ERR(alloc)) + goto fail; + } + status = nfs3_proc_setacls(inode, acl, dfacl); + posix_acl_release(alloc); + return status; + +fail: + return PTR_ERR(alloc); +} +#endif /* CONFIG_NFS_ACL */ + extern u32 *nfs3_decode_dirent(u32 *, struct nfs_entry *, int); static void @@ -868,4 +1076,9 @@ struct nfs_rpc_ops nfs_v3_clientops = { .file_open = nfs_open, .file_release = nfs_release, .lock = nfs3_proc_lock, +#ifdef CONFIG_NFS_ACL + .getacl = nfs3_proc_getacl, + .setacl = nfs3_proc_setacl, + .setacls = nfs3_proc_setacls, +#endif /* CONFIG_NFS_ACL */ }; diff -puN fs/nfs/nfs3xdr.c~nfsacl-client-side-of-nfsacl fs/nfs/nfs3xdr.c --- 25/fs/nfs/nfs3xdr.c~nfsacl-client-side-of-nfsacl 2005-01-23 01:27:58.145466872 -0800 +++ 25-akpm/fs/nfs/nfs3xdr.c 2005-01-23 01:27:58.167463528 -0800 @@ -21,6 +21,7 @@ #include #include #include +#include #define NFSDBG_FACILITY NFSDBG_XDR @@ -62,6 +63,8 @@ extern int nfs_stat_to_errno(int); #define NFS3_linkargs_sz (NFS3_fh_sz+NFS3_diropargs_sz) #define NFS3_readdirargs_sz (NFS3_fh_sz+2) #define NFS3_commitargs_sz (NFS3_fh_sz+3) +#define NFS3_getaclargs_sz (NFS3_fh_sz+1) +#define NFS3_setaclargs_sz (NFS3_fh_sz+1+2*(2+5*3)) #define NFS3_attrstat_sz (1+NFS3_fattr_sz) #define NFS3_wccstat_sz (1+NFS3_wcc_data_sz) @@ -78,6 +81,8 @@ extern int nfs_stat_to_errno(int); #define NFS3_fsinfores_sz (1+NFS3_post_op_attr_sz+12) #define NFS3_pathconfres_sz (1+NFS3_post_op_attr_sz+6) #define NFS3_commitres_sz (1+NFS3_wcc_data_sz+2) +#define NFS3_getaclres_sz (1+NFS3_post_op_attr_sz+1+2*(2+5*3)) +#define NFS3_setaclres_sz (1+NFS3_post_op_attr_sz) /* * Map file type to S_IFMT bits @@ -627,6 +632,76 @@ nfs3_xdr_commitargs(struct rpc_rqst *req return 0; } +#ifdef CONFIG_NFS_ACL +/* + * Encode GETACL arguments + */ +static int +nfs3_xdr_getaclargs(struct rpc_rqst *req, u32 *p, + struct nfs3_getaclargs *args) +{ + struct rpc_auth *auth = req->rq_task->tk_auth; + unsigned int replen; + + p = xdr_encode_fhandle(p, args->fh); + *p++ = htonl(args->mask); + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p); + + if (args->mask & (NFS3_ACL | NFS3_DFACL)) { + /* Inline the page array */ + replen = (RPC_REPHDRSIZE + auth->au_rslack + + NFS3_getaclres_sz) << 2; + xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, + NFSACL_MAXPAGES << PAGE_SHIFT); + } + return 0; +} +#endif /* CONFIG_NFS_ACL */ + +#ifdef CONFIG_NFS_ACL +/* + * Encode SETACL arguments + */ +static int +nfs3_xdr_setaclargs(struct rpc_rqst *req, u32 *p, + struct nfs3_setaclargs *args) +{ + struct xdr_buf *buf = &req->rq_snd_buf; + unsigned int base, len_in_head, len = nfsacl_size( + (args->mask & NFS3_ACL) ? args->acl_access : NULL, + (args->mask & NFS3_DFACL) ? args->acl_default : NULL); + int count, err; + + p = xdr_encode_fhandle(p, NFS_FH(args->inode)); + *p++ = htonl(args->mask); + base = (char *)p - (char *)buf->head->iov_base; + /* put as much of the acls into head as possible. */ + len_in_head = min_t(unsigned int, buf->head->iov_len - base, len); + len -= len_in_head; + req->rq_slen = xdr_adjust_iovec(req->rq_svec, p + len_in_head); + + for (count = 0; (count << PAGE_SHIFT) < len; count++) { + args->pages[count] = alloc_page(GFP_KERNEL); + if (!args->pages[count]) { + while (count) + __free_page(args->pages[--count]); + return -ENOMEM; + } + } + xdr_encode_pages(buf, args->pages, 0, len); + + err = nfsacl_encode(buf, base, args->inode, + (args->mask & NFS3_ACL) ? + args->acl_access : NULL, 1, 0); + if (err > 0) + err = nfsacl_encode(buf, base + err, args->inode, + (args->mask & NFS3_DFACL) ? + args->acl_default : NULL, 1, + NFS3_ACL_DEFAULT); + return (err > 0) ? 0 : err; +} +#endif /* CONFIG_NFS_ACL */ + /* * NFS XDR decode functions */ @@ -978,6 +1053,56 @@ nfs3_xdr_commitres(struct rpc_rqst *req, return 0; } +#ifdef CONFIG_NFS_ACL +/* + * Decode GETACL reply + */ +static int +nfs3_xdr_getaclres(struct rpc_rqst *req, u32 *p, + struct nfs3_getaclres *res) +{ + struct xdr_buf *buf = &req->rq_rcv_buf; + int status = ntohl(*p++); + struct posix_acl **acl; + unsigned int *aclcnt; + int err, base; + + if (status != 0) + return -nfs_stat_to_errno(status); + p = xdr_decode_post_op_attr(p, res->fattr); + res->mask = ntohl(*p++); + if (res->mask & ~(NFS3_ACL|NFS3_ACLCNT|NFS3_DFACL|NFS3_DFACLCNT)) + return -EINVAL; + base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base; + + acl = (res->mask & NFS3_ACL) ? &res->acl_access : NULL; + aclcnt = (res->mask & NFS3_ACLCNT) ? &res->acl_access_count : NULL; + err = nfsacl_decode(buf, base, aclcnt, acl); + + acl = (res->mask & NFS3_DFACL) ? &res->acl_default : NULL; + aclcnt = (res->mask & NFS3_DFACLCNT) ? &res->acl_default_count : NULL; + if (err > 0) + err = nfsacl_decode(buf, base + err, aclcnt, acl); + return (err > 0) ? 0 : err; +} +#endif /* CONFIG_NFS_ACL */ + +#ifdef CONFIG_NFS_ACL +/* + * Decode setacl reply. + */ +static int +nfs3_xdr_setaclres(struct rpc_rqst *req, u32 *p, struct nfs_fattr *fattr) +{ + int status = ntohl(*p++); + + if (status) + return -nfs_stat_to_errno(status); + xdr_decode_post_op_attr(p, fattr); + return 0; +} +#endif /* CONFIG_NFS_ACL */ + #ifndef MAX # define MAX(a, b) (((a) > (b))? (a) : (b)) #endif @@ -1021,3 +1146,16 @@ struct rpc_version nfs_version3 = { .procs = nfs3_procedures }; +#ifdef CONFIG_NFS_ACL +static struct rpc_procinfo nfs3_acl_procedures[] = { + PROC(GETACL, getaclargs, getaclres, 1), + PROC(SETACL, setaclargs, setaclres, 0), +}; + +struct rpc_version nfsacl_version3 = { + .number = 3, + .nrprocs = sizeof(nfs3_acl_procedures)/ + sizeof(nfs3_acl_procedures[0]), + .procs = nfs3_acl_procedures, +}; +#endif /* CONFIG_NFS_ACL */ diff -puN /dev/null fs/nfs/xattr.c --- /dev/null 2003-09-15 06:40:47.000000000 -0700 +++ 25-akpm/fs/nfs/xattr.c 2005-01-23 01:27:58.168463376 -0800 @@ -0,0 +1,125 @@ +#include +#include +#include +#include +#include + +ssize_t +nfs_listxattr(struct dentry *dentry, char *buffer, size_t size) +{ + struct inode *inode = dentry->d_inode; + struct posix_acl *acl; + int pos=0, len=0; + + if (NFS_PROTO(inode)->version != 3 || !NFS_PROTO(inode)->getacl) + return -EOPNOTSUPP; + +# define output(s) do { \ + if (pos + sizeof(s) <= size) { \ + memcpy(buffer + pos, s, sizeof(s)); \ + pos += sizeof(s); \ + } \ + len += sizeof(s); \ + } while(0) + + acl = NFS_PROTO(inode)->getacl(inode, ACL_TYPE_ACCESS); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl) { + output("system.posix_acl_access"); + posix_acl_release(acl); + } + + if (S_ISDIR(inode->i_mode)) { + acl = NFS_PROTO(inode)->getacl(inode, ACL_TYPE_DEFAULT); + if (IS_ERR(acl)) + return PTR_ERR(acl); + if (acl) { + output("system.posix_acl_default"); + posix_acl_release(acl); + } + } + +# undef output + + if (!buffer || len <= size) + return len; + return -ERANGE; +} + +ssize_t +nfs_getxattr(struct dentry *dentry, const char *name, void *buffer, size_t size) +{ + struct inode *inode = dentry->d_inode; + struct posix_acl *acl; + int type, error = 0; + + if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0) + type = ACL_TYPE_ACCESS; + else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0) + type = ACL_TYPE_DEFAULT; + else + return -EOPNOTSUPP; + + acl = ERR_PTR(-EOPNOTSUPP); + if (NFS_PROTO(inode)->version == 3 && NFS_PROTO(inode)->getacl) + acl = NFS_PROTO(inode)->getacl(inode, type); + if (IS_ERR(acl)) + return PTR_ERR(acl); + else if (acl) { + if (type == ACL_TYPE_ACCESS && acl->a_count == 0) + error = -ENODATA; + else + error = posix_acl_to_xattr(acl, buffer, size); + posix_acl_release(acl); + } else + error = -ENODATA; + + return error; +} + +int +nfs_setxattr(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) +{ + struct inode *inode = dentry->d_inode; + struct posix_acl *acl; + int type, error; + + if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0) + type = ACL_TYPE_ACCESS; + else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0) + type = ACL_TYPE_DEFAULT; + else + return -EOPNOTSUPP; + if (NFS_PROTO(inode)->version != 3 || !NFS_PROTO(inode)->setacl) + return -EOPNOTSUPP; + + acl = posix_acl_from_xattr(value, size); + if (IS_ERR(acl)) + return PTR_ERR(acl); + error = NFS_PROTO(inode)->setacl(inode, type, acl); + posix_acl_release(acl); + + return error; +} + +int +nfs_removexattr(struct dentry *dentry, const char *name) +{ + struct inode *inode = dentry->d_inode; + int error, type; + + if (strcmp(name, XATTR_NAME_ACL_ACCESS) == 0) + type = ACL_TYPE_ACCESS; + else if (strcmp(name, XATTR_NAME_ACL_DEFAULT) == 0) + type = ACL_TYPE_DEFAULT; + else + return -EOPNOTSUPP; + + error = -EOPNOTSUPP; + if (NFS_PROTO(inode)->version == 3 && NFS_PROTO(inode)->setacl) + error = NFS_PROTO(inode)->setacl(inode, type, NULL); + + return error; +} diff -puN include/linux/nfs_fs.h~nfsacl-client-side-of-nfsacl include/linux/nfs_fs.h --- 25/include/linux/nfs_fs.h~nfsacl-client-side-of-nfsacl 2005-01-23 01:27:58.147466568 -0800 +++ 25-akpm/include/linux/nfs_fs.h 2005-01-23 01:27:58.169463224 -0800 @@ -329,6 +329,22 @@ static inline struct rpc_cred *nfs_file_ } /* + * linux/fs/nfs/xattr.c + */ +#ifdef CONFIG_NFS_ACL +extern ssize_t nfs_listxattr(struct dentry *, char *, size_t); +extern ssize_t nfs_getxattr(struct dentry *, const char *, void *, size_t); +extern int nfs_setxattr(struct dentry *, const char *, + const void *, size_t, int); +extern int nfs_removexattr (struct dentry *, const char *name); +#else +# define nfs_listxattr NULL +# define nfs_getxattr NULL +# define nfs_setxattr NULL +# define nfs_removexattr NULL +#endif + +/* * linux/fs/nfs/direct.c */ extern ssize_t nfs_direct_IO(int, struct kiocb *, const struct iovec *, loff_t, diff -puN include/linux/nfs_fs_sb.h~nfsacl-client-side-of-nfsacl include/linux/nfs_fs_sb.h --- 25/include/linux/nfs_fs_sb.h~nfsacl-client-side-of-nfsacl 2005-01-23 01:27:58.148466416 -0800 +++ 25-akpm/include/linux/nfs_fs_sb.h 2005-01-23 01:27:58.169463224 -0800 @@ -10,6 +10,9 @@ struct nfs_server { struct rpc_clnt * client; /* RPC client handle */ struct rpc_clnt * client_sys; /* 2nd handle for FSINFO */ +#ifdef CONFIG_NFS_ACL + struct rpc_clnt * client_acl; /* ACL RPC client handle */ +#endif /* CONFIG_NFS_ACL */ struct nfs_rpc_ops * rpc_ops; /* NFS protocol vector */ struct backing_dev_info backing_dev_info; int flags; /* various flags */ diff -puN include/linux/nfs_mount.h~nfsacl-client-side-of-nfsacl include/linux/nfs_mount.h --- 25/include/linux/nfs_mount.h~nfsacl-client-side-of-nfsacl 2005-01-23 01:27:58.150466112 -0800 +++ 25-akpm/include/linux/nfs_mount.h 2005-01-23 01:27:58.170463072 -0800 @@ -63,4 +63,7 @@ struct nfs_mount_data { #define NFS_MOUNT_SECFLAVOUR 0x2000 /* 5 */ #define NFS_MOUNT_FLAGMASK 0xFFFF +/* Feature flag for the NFS_ACL protocol extension */ +#define NFSACL 0x10000 + #endif diff -puN include/linux/nfs_xdr.h~nfsacl-client-side-of-nfsacl include/linux/nfs_xdr.h --- 25/include/linux/nfs_xdr.h~nfsacl-client-side-of-nfsacl 2005-01-23 01:27:58.151465960 -0800 +++ 25-akpm/include/linux/nfs_xdr.h 2005-01-23 01:27:58.170463072 -0800 @@ -2,6 +2,7 @@ #define _LINUX_NFS_XDR_H #include +#include struct nfs4_fsid { __u64 major; @@ -354,6 +355,20 @@ struct nfs_readdirargs { struct page ** pages; }; +struct nfs3_getaclargs { + struct nfs_fh * fh; + int mask; + struct page ** pages; +}; + +struct nfs3_setaclargs { + struct inode * inode; + int mask; + struct posix_acl * acl_access; + struct posix_acl * acl_default; + struct page ** pages; +}; + struct nfs_diropok { struct nfs_fh * fh; struct nfs_fattr * fattr; @@ -477,6 +492,15 @@ struct nfs3_readdirres { int plus; }; +struct nfs3_getaclres { + struct nfs_fattr * fattr; + int mask; + unsigned int acl_access_count; + unsigned int acl_default_count; + struct posix_acl * acl_access; + struct posix_acl * acl_default; +}; + #ifdef CONFIG_NFS_V4 typedef u64 clientid4; @@ -713,6 +737,11 @@ struct nfs_rpc_ops { int (*file_open) (struct inode *, struct file *); int (*file_release) (struct inode *, struct file *); int (*lock)(struct file *, int, struct file_lock *); +#ifdef CONFIG_NFS_ACL + struct posix_acl * (*getacl)(struct inode *, int); + int (*setacl)(struct inode *, int, struct posix_acl *); + int (*setacls)(struct inode *, struct posix_acl *, struct posix_acl *); +#endif /* CONFIG_NFS_ACL */ }; /* @@ -734,4 +763,7 @@ extern struct rpc_version nfs_version4; extern struct rpc_program nfs_program; extern struct rpc_stat nfs_rpcstat; +extern struct rpc_version nfsacl_version3; +extern struct rpc_program nfsacl_program; + #endif _