[PATCH] NFS: Add support for NFSv3 ACLs
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 <agruen@suse.de> Acked-by: Olaf Kirch <okir@suse.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
This commit is contained in:
		
							parent
							
								
									a257cdd0e2
								
							
						
					
					
						commit
						b7fa0554cf
					
				
							
								
								
									
										11
									
								
								fs/Kconfig
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								fs/Kconfig
									
									
									
									
									
								
							| @ -1268,6 +1268,7 @@ config NFS_FS | ||||
| 	depends on INET | ||||
| 	select LOCKD | ||||
| 	select SUNRPC | ||||
| 	select NFS_ACL_SUPPORT if NFS_V3_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 | ||||
| @ -1310,6 +1311,16 @@ config NFS_V3 | ||||
| 
 | ||||
| 	  If unsure, say Y. | ||||
| 
 | ||||
| config NFS_V3_ACL | ||||
| 	bool "Provide client support for the NFSv3 ACL protocol extension" | ||||
| 	depends on NFS_V3 | ||||
| 	help | ||||
| 	  Implement the NFSv3 ACL protocol extension for manipulating POSIX | ||||
| 	  Access Control Lists.  The server should also be compiled with | ||||
| 	  the NFSv3 ACL protocol extension; see the CONFIG_NFSD_V3_ACL option. | ||||
| 
 | ||||
| 	  If unsure, say N. | ||||
| 
 | ||||
| config NFS_V4 | ||||
| 	bool "Provide NFSv4 client support (EXPERIMENTAL)" | ||||
| 	depends on NFS_FS && EXPERIMENTAL | ||||
|  | ||||
| @ -8,6 +8,7 @@ nfs-y 			:= dir.o file.o inode.o nfs2xdr.o pagelist.o \ | ||||
| 			   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_V3_ACL)	+= nfs3acl.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 | ||||
|  | ||||
							
								
								
									
										21
									
								
								fs/nfs/dir.c
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								fs/nfs/dir.c
									
									
									
									
									
								
							| @ -75,6 +75,27 @@ struct inode_operations nfs_dir_inode_operations = { | ||||
| 	.setattr	= nfs_setattr, | ||||
| }; | ||||
| 
 | ||||
| #ifdef CONFIG_NFS_V3 | ||||
| struct inode_operations nfs3_dir_inode_operations = { | ||||
| 	.create		= nfs_create, | ||||
| 	.lookup		= nfs_lookup, | ||||
| 	.link		= nfs_link, | ||||
| 	.unlink		= nfs_unlink, | ||||
| 	.symlink	= nfs_symlink, | ||||
| 	.mkdir		= nfs_mkdir, | ||||
| 	.rmdir		= nfs_rmdir, | ||||
| 	.mknod		= nfs_mknod, | ||||
| 	.rename		= nfs_rename, | ||||
| 	.permission	= nfs_permission, | ||||
| 	.getattr	= nfs_getattr, | ||||
| 	.setattr	= nfs_setattr, | ||||
| 	.listxattr	= nfs3_listxattr, | ||||
| 	.getxattr	= nfs3_getxattr, | ||||
| 	.setxattr	= nfs3_setxattr, | ||||
| 	.removexattr	= nfs3_removexattr, | ||||
| }; | ||||
| #endif  /* CONFIG_NFS_V3 */ | ||||
| 
 | ||||
| #ifdef CONFIG_NFS_V4 | ||||
| 
 | ||||
| static struct dentry *nfs_atomic_lookup(struct inode *, struct dentry *, struct nameidata *); | ||||
|  | ||||
| @ -71,6 +71,18 @@ struct inode_operations nfs_file_inode_operations = { | ||||
| 	.setattr	= nfs_setattr, | ||||
| }; | ||||
| 
 | ||||
| #ifdef CONFIG_NFS_V3 | ||||
| struct inode_operations nfs3_file_inode_operations = { | ||||
| 	.permission	= nfs_permission, | ||||
| 	.getattr	= nfs_getattr, | ||||
| 	.setattr	= nfs_setattr, | ||||
| 	.listxattr	= nfs3_listxattr, | ||||
| 	.getxattr	= nfs3_getxattr, | ||||
| 	.setxattr	= nfs3_setxattr, | ||||
| 	.removexattr	= nfs3_removexattr, | ||||
| }; | ||||
| #endif  /* CONFIG_NFS_v3 */ | ||||
| 
 | ||||
| /* Hack for future NFS swap support */ | ||||
| #ifndef IS_SWAPFILE | ||||
| # define IS_SWAPFILE(inode)	(0) | ||||
|  | ||||
| @ -108,6 +108,21 @@ static struct rpc_program	nfs_program = { | ||||
| 	.pipe_dir_name		= "/nfs", | ||||
| }; | ||||
| 
 | ||||
| #ifdef CONFIG_NFS_V3_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 =		NFS_ACL_PROGRAM, | ||||
| 	.nrvers =		sizeof(nfsacl_version) / sizeof(nfsacl_version[0]), | ||||
| 	.version =		nfsacl_version, | ||||
| 	.stats =		&nfsacl_rpcstat, | ||||
| }; | ||||
| #endif  /* CONFIG_NFS_V3_ACL */ | ||||
| 
 | ||||
| static inline unsigned long | ||||
| nfs_fattr_to_ino_t(struct nfs_fattr *fattr) | ||||
| { | ||||
| @ -165,6 +180,9 @@ nfs_umount_begin(struct super_block *sb) | ||||
| 	/* -EIO all pending I/O */ | ||||
| 	if (!IS_ERR(rpc)) | ||||
| 		rpc_killall_tasks(rpc); | ||||
| 	rpc = NFS_SB(sb)->client_acl; | ||||
| 	if (!IS_ERR(rpc)) | ||||
| 		rpc_killall_tasks(rpc); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| @ -461,8 +479,17 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent) | ||||
| 		atomic_inc(&server->client->cl_count); | ||||
| 		server->client_sys = server->client; | ||||
| 	} | ||||
| 
 | ||||
| 	if (server->flags & NFS_MOUNT_VER3) { | ||||
| #ifdef CONFIG_NFS_V3_ACL | ||||
| 		if (!(server->flags & NFS_MOUNT_NOACL)) { | ||||
| 			server->client_acl = rpc_bind_new_program(server->client, &nfsacl_program, 3); | ||||
| 			/* No errors! Assume that Sun nfsacls are supported */ | ||||
| 			if (!IS_ERR(server->client_acl)) | ||||
| 				server->caps |= NFS_CAP_ACLS; | ||||
| 		} | ||||
| #else | ||||
| 		server->flags &= ~NFS_MOUNT_NOACL; | ||||
| #endif /* CONFIG_NFS_V3_ACL */ | ||||
| 		if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN) | ||||
| 			server->namelen = NFS3_MAXNAMLEN; | ||||
| 		sb->s_time_gran = 1; | ||||
| @ -546,6 +573,7 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt) | ||||
| 		{ NFS_MOUNT_NOCTO, ",nocto", "" }, | ||||
| 		{ NFS_MOUNT_NOAC, ",noac", "" }, | ||||
| 		{ NFS_MOUNT_NONLM, ",nolock", ",lock" }, | ||||
| 		{ NFS_MOUNT_NOACL, ",noacl", "" }, | ||||
| 		{ 0, NULL, NULL } | ||||
| 	}; | ||||
| 	struct proc_nfs_info *nfs_infop; | ||||
| @ -1452,7 +1480,7 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type, | ||||
| 	memset(server, 0, sizeof(struct nfs_server)); | ||||
| 	/* Zero out the NFS state stuff */ | ||||
| 	init_nfsv4_state(server); | ||||
| 	server->client = server->client_sys = ERR_PTR(-EINVAL); | ||||
| 	server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL); | ||||
| 
 | ||||
| 	root = &server->fh; | ||||
| 	if (data->flags & NFS_MOUNT_VER3) | ||||
| @ -1513,6 +1541,8 @@ static void nfs_kill_super(struct super_block *s) | ||||
| 		rpc_shutdown_client(server->client); | ||||
| 	if (!IS_ERR(server->client_sys)) | ||||
| 		rpc_shutdown_client(server->client_sys); | ||||
| 	if (!IS_ERR(server->client_acl)) | ||||
| 		rpc_shutdown_client(server->client_acl); | ||||
| 
 | ||||
| 	if (!(server->flags & NFS_MOUNT_NONLM)) | ||||
| 		lockd_down();	/* release rpc.lockd */ | ||||
| @ -1794,7 +1824,7 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type, | ||||
| 	memset(server, 0, sizeof(struct nfs_server)); | ||||
| 	/* Zero out the NFS state stuff */ | ||||
| 	init_nfsv4_state(server); | ||||
| 	server->client = server->client_sys = ERR_PTR(-EINVAL); | ||||
| 	server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL); | ||||
| 
 | ||||
| 	p = nfs_copy_user_string(NULL, &data->hostname, 256); | ||||
| 	if (IS_ERR(p)) | ||||
|  | ||||
							
								
								
									
										303
									
								
								fs/nfs/nfs3acl.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										303
									
								
								fs/nfs/nfs3acl.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,303 @@ | ||||
| #include <linux/fs.h> | ||||
| #include <linux/nfs.h> | ||||
| #include <linux/nfs3.h> | ||||
| #include <linux/nfs_fs.h> | ||||
| #include <linux/xattr_acl.h> | ||||
| #include <linux/nfsacl.h> | ||||
| 
 | ||||
| #define NFSDBG_FACILITY	NFSDBG_PROC | ||||
| 
 | ||||
| ssize_t nfs3_listxattr(struct dentry *dentry, char *buffer, size_t size) | ||||
| { | ||||
| 	struct inode *inode = dentry->d_inode; | ||||
| 	struct posix_acl *acl; | ||||
| 	int pos=0, len=0; | ||||
| 
 | ||||
| #	define output(s) do {						\ | ||||
| 			if (pos + sizeof(s) <= size) {			\ | ||||
| 				memcpy(buffer + pos, s, sizeof(s));	\ | ||||
| 				pos += sizeof(s);			\ | ||||
| 			}						\ | ||||
| 			len += sizeof(s);				\ | ||||
| 		} while(0) | ||||
| 
 | ||||
| 	acl = nfs3_proc_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 = nfs3_proc_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 nfs3_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 = nfs3_proc_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 nfs3_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; | ||||
| 
 | ||||
| 	acl = posix_acl_from_xattr(value, size); | ||||
| 	if (IS_ERR(acl)) | ||||
| 		return PTR_ERR(acl); | ||||
| 	error = nfs3_proc_setacl(inode, type, acl); | ||||
| 	posix_acl_release(acl); | ||||
| 
 | ||||
| 	return error; | ||||
| } | ||||
| 
 | ||||
| int nfs3_removexattr(struct dentry *dentry, const char *name) | ||||
| { | ||||
| 	struct inode *inode = dentry->d_inode; | ||||
| 	int 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; | ||||
| 
 | ||||
| 	return nfs3_proc_setacl(inode, type, NULL); | ||||
| } | ||||
| 
 | ||||
| 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 = { | ||||
| 		.fh = NFS_FH(inode), | ||||
| 		/* The xdr layer may allocate pages here. */ | ||||
| 		.pages = pages, | ||||
| 	}; | ||||
| 	struct nfs3_getaclres res = { | ||||
| 		.fattr =	&fattr, | ||||
| 	}; | ||||
| 	struct posix_acl *acl = NULL; | ||||
| 	int status, count; | ||||
| 
 | ||||
| 	if (!nfs_server_capable(inode, NFS_CAP_ACLS)) | ||||
| 		return ERR_PTR(-EOPNOTSUPP); | ||||
| 
 | ||||
| 	switch (type) { | ||||
| 		case ACL_TYPE_ACCESS: | ||||
| 			args.mask = NFS_ACLCNT|NFS_ACL; | ||||
| 			break; | ||||
| 
 | ||||
| 		case ACL_TYPE_DEFAULT: | ||||
| 			if (!S_ISDIR(inode->i_mode)) | ||||
| 				return NULL; | ||||
| 			args.mask = NFS_DFACLCNT|NFS_DFACL; | ||||
| 			break; | ||||
| 
 | ||||
| 		default: | ||||
| 			return ERR_PTR(-EINVAL); | ||||
| 	} | ||||
| 
 | ||||
| 	dprintk("NFS call getacl\n"); | ||||
| 	status = rpc_call(server->client_acl, ACLPROC3_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]); | ||||
| 
 | ||||
| 	switch (status) { | ||||
| 		case 0: | ||||
| 			status = nfs_refresh_inode(inode, &fattr); | ||||
| 			break; | ||||
| 		case -EPFNOSUPPORT: | ||||
| 		case -EPROTONOSUPPORT: | ||||
| 			dprintk("NFS_V3_ACL extension not supported; disabling\n"); | ||||
| 			server->caps &= ~NFS_CAP_ACLS; | ||||
| 		case -ENOTSUPP: | ||||
| 			status = -EOPNOTSUPP; | ||||
| 		default: | ||||
| 			goto getout; | ||||
| 	} | ||||
| 	if ((args.mask & res.mask) != args.mask) { | ||||
| 		status = -EIO; | ||||
| 		goto getout; | ||||
| 	} | ||||
| 
 | ||||
| 	if (res.acl_access != NULL) { | ||||
| 		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; | ||||
| 	} | ||||
| 
 | ||||
| getout: | ||||
| 	posix_acl_release(res.acl_access); | ||||
| 	posix_acl_release(res.acl_default); | ||||
| 
 | ||||
| 	if (status != 0) { | ||||
| 		posix_acl_release(acl); | ||||
| 		acl = ERR_PTR(status); | ||||
| 	} | ||||
| 	return 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 = { | ||||
| 		.inode = inode, | ||||
| 		.mask = NFS_ACL, | ||||
| 		.acl_access = acl, | ||||
| 		.pages = pages, | ||||
| 	}; | ||||
| 	int status, count; | ||||
| 
 | ||||
| 	status = -EOPNOTSUPP; | ||||
| 	if (!nfs_server_capable(inode, NFS_CAP_ACLS)) | ||||
| 		goto out; | ||||
| 
 | ||||
| 	/* We are doing this here, because XDR marshalling can only
 | ||||
| 	   return -ENOMEM. */ | ||||
| 	status = -ENOSPC; | ||||
| 	if (acl != NULL && acl->a_count > NFS_ACL_MAX_ENTRIES) | ||||
| 		goto out; | ||||
| 	if (dfacl != NULL && dfacl->a_count > NFS_ACL_MAX_ENTRIES) | ||||
| 		goto out; | ||||
| 	if (S_ISDIR(inode->i_mode)) { | ||||
| 		args.mask |= NFS_DFACL; | ||||
| 		args.acl_default = dfacl; | ||||
| 	} | ||||
| 
 | ||||
| 	dprintk("NFS call setacl\n"); | ||||
| 	nfs_begin_data_update(inode); | ||||
| 	status = rpc_call(server->client_acl, ACLPROC3_SETACL, | ||||
| 			  &args, &fattr, 0); | ||||
| 	NFS_FLAGS(inode) |= NFS_INO_INVALID_ACCESS; | ||||
| 	nfs_end_data_update(inode); | ||||
| 	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]); | ||||
| 
 | ||||
| 	switch (status) { | ||||
| 		case 0: | ||||
| 			status = nfs_refresh_inode(inode, &fattr); | ||||
| 			break; | ||||
| 		case -EPFNOSUPPORT: | ||||
| 		case -EPROTONOSUPPORT: | ||||
| 			dprintk("NFS_V3_ACL SETACL RPC not supported" | ||||
| 					"(will not retry)\n"); | ||||
| 			server->caps &= ~NFS_CAP_ACLS; | ||||
| 		case -ENOTSUPP: | ||||
| 			status = -EOPNOTSUPP; | ||||
| 	} | ||||
| out: | ||||
| 	return status; | ||||
| } | ||||
| 
 | ||||
| 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 = nfs3_proc_getacl(inode, | ||||
| 						ACL_TYPE_DEFAULT); | ||||
| 				if (IS_ERR(alloc)) | ||||
| 					goto fail; | ||||
| 				break; | ||||
| 
 | ||||
| 			case ACL_TYPE_DEFAULT: | ||||
| 				dfacl = acl; | ||||
| 				alloc = acl = nfs3_proc_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); | ||||
| } | ||||
| @ -17,6 +17,7 @@ | ||||
| #include <linux/nfs_page.h> | ||||
| #include <linux/lockd/bind.h> | ||||
| #include <linux/smp_lock.h> | ||||
| #include <linux/nfs_mount.h> | ||||
| 
 | ||||
| #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, | ||||
| 	}; | ||||
| @ -825,8 +826,8 @@ nfs3_proc_lock(struct file *filp, int cmd, struct file_lock *fl) | ||||
| struct nfs_rpc_ops	nfs_v3_clientops = { | ||||
| 	.version	= 3,			/* protocol version */ | ||||
| 	.dentry_ops	= &nfs_dentry_operations, | ||||
| 	.dir_inode_ops	= &nfs_dir_inode_operations, | ||||
| 	.file_inode_ops	= &nfs_file_inode_operations, | ||||
| 	.dir_inode_ops	= &nfs3_dir_inode_operations, | ||||
| 	.file_inode_ops	= &nfs3_file_inode_operations, | ||||
| 	.getroot	= nfs3_proc_get_root, | ||||
| 	.getattr	= nfs3_proc_getattr, | ||||
| 	.setattr	= nfs3_proc_setattr, | ||||
|  | ||||
							
								
								
									
										147
									
								
								fs/nfs/nfs3xdr.c
									
									
									
									
									
								
							
							
						
						
									
										147
									
								
								fs/nfs/nfs3xdr.c
									
									
									
									
									
								
							| @ -21,6 +21,7 @@ | ||||
| #include <linux/nfs.h> | ||||
| #include <linux/nfs3.h> | ||||
| #include <linux/nfs_fs.h> | ||||
| #include <linux/nfsacl.h> | ||||
| 
 | ||||
| #define NFSDBG_FACILITY		NFSDBG_XDR | ||||
| 
 | ||||
| @ -79,6 +80,11 @@ extern int			nfs_stat_to_errno(int); | ||||
| #define NFS3_pathconfres_sz	(1+NFS3_post_op_attr_sz+6) | ||||
| #define NFS3_commitres_sz	(1+NFS3_wcc_data_sz+2) | ||||
| 
 | ||||
| #define ACL3_getaclargs_sz	(NFS3_fh_sz+1) | ||||
| #define ACL3_setaclargs_sz	(NFS3_fh_sz+1+2*(2+5*3)) | ||||
| #define ACL3_getaclres_sz	(1+NFS3_post_op_attr_sz+1+2*(2+5*3)) | ||||
| #define ACL3_setaclres_sz	(1+NFS3_post_op_attr_sz) | ||||
| 
 | ||||
| /*
 | ||||
|  * Map file type to S_IFMT bits | ||||
|  */ | ||||
| @ -627,6 +633,74 @@ nfs3_xdr_commitargs(struct rpc_rqst *req, u32 *p, struct nfs_writeargs *args) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_NFS_V3_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 & (NFS_ACL | NFS_DFACL)) { | ||||
| 		/* Inline the page array */ | ||||
| 		replen = (RPC_REPHDRSIZE + auth->au_rslack + | ||||
| 			  ACL3_getaclres_sz) << 2; | ||||
| 		xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages, 0, | ||||
| 				 NFSACL_MAXPAGES << PAGE_SHIFT); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * 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 & NFS_ACL)   ? args->acl_access  : NULL, | ||||
| 		(args->mask & NFS_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 & NFS_ACL) ? | ||||
| 			    args->acl_access : NULL, 1, 0); | ||||
| 	if (err > 0) | ||||
| 		err = nfsacl_encode(buf, base + err, args->inode, | ||||
| 				    (args->mask & NFS_DFACL) ? | ||||
| 				    args->acl_default : NULL, 1, | ||||
| 				    NFS_ACL_DEFAULT); | ||||
| 	return (err > 0) ? 0 : err; | ||||
| } | ||||
| #endif  /* CONFIG_NFS_V3_ACL */ | ||||
| 
 | ||||
| /*
 | ||||
|  * NFS XDR decode functions | ||||
|  */ | ||||
| @ -978,6 +1052,54 @@ nfs3_xdr_commitres(struct rpc_rqst *req, u32 *p, struct nfs_writeres *res) | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| #ifdef CONFIG_NFS_V3_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 & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT)) | ||||
| 		return -EINVAL; | ||||
| 	base = (char *)p - (char *)req->rq_rcv_buf.head->iov_base; | ||||
| 
 | ||||
| 	acl = (res->mask & NFS_ACL) ? &res->acl_access : NULL; | ||||
| 	aclcnt = (res->mask & NFS_ACLCNT) ? &res->acl_access_count : NULL; | ||||
| 	err = nfsacl_decode(buf, base, aclcnt, acl); | ||||
| 
 | ||||
| 	acl = (res->mask & NFS_DFACL) ? &res->acl_default : NULL; | ||||
| 	aclcnt = (res->mask & NFS_DFACLCNT) ? &res->acl_default_count : NULL; | ||||
| 	if (err > 0) | ||||
| 		err = nfsacl_decode(buf, base + err, aclcnt, acl); | ||||
| 	return (err > 0) ? 0 : err; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * 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_V3_ACL */ | ||||
| 
 | ||||
| #ifndef MAX | ||||
| # define MAX(a, b)	(((a) > (b))? (a) : (b)) | ||||
| #endif | ||||
| @ -1021,3 +1143,28 @@ struct rpc_version		nfs_version3 = { | ||||
| 	.procs			= nfs3_procedures | ||||
| }; | ||||
| 
 | ||||
| #ifdef CONFIG_NFS_V3_ACL | ||||
| static struct rpc_procinfo	nfs3_acl_procedures[] = { | ||||
| 	[ACLPROC3_GETACL] = { | ||||
| 		.p_proc = ACLPROC3_GETACL, | ||||
| 		.p_encode = (kxdrproc_t) nfs3_xdr_getaclargs, | ||||
| 		.p_decode = (kxdrproc_t) nfs3_xdr_getaclres, | ||||
| 		.p_bufsiz = MAX(ACL3_getaclargs_sz, ACL3_getaclres_sz) << 2, | ||||
| 		.p_timer = 1, | ||||
| 	}, | ||||
| 	[ACLPROC3_SETACL] = { | ||||
| 		.p_proc = ACLPROC3_SETACL, | ||||
| 		.p_encode = (kxdrproc_t) nfs3_xdr_setaclargs, | ||||
| 		.p_decode = (kxdrproc_t) nfs3_xdr_setaclres, | ||||
| 		.p_bufsiz = MAX(ACL3_setaclargs_sz, ACL3_setaclres_sz) << 2, | ||||
| 		.p_timer = 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_V3_ACL */ | ||||
|  | ||||
| @ -124,6 +124,7 @@ enum { | ||||
| 	Opt_soft, Opt_hard, Opt_intr, | ||||
| 	Opt_nointr, Opt_posix, Opt_noposix, Opt_cto, Opt_nocto, Opt_ac,  | ||||
| 	Opt_noac, Opt_lock, Opt_nolock, Opt_v2, Opt_v3, Opt_udp, Opt_tcp, | ||||
| 	Opt_acl, Opt_noacl, | ||||
| 	/* Error token */ | ||||
| 	Opt_err | ||||
| }; | ||||
| @ -158,6 +159,8 @@ static match_table_t __initdata tokens = { | ||||
| 	{Opt_udp, "udp"}, | ||||
| 	{Opt_tcp, "proto=tcp"}, | ||||
| 	{Opt_tcp, "tcp"}, | ||||
| 	{Opt_acl, "acl"}, | ||||
| 	{Opt_noacl, "noacl"}, | ||||
| 	{Opt_err, NULL} | ||||
| 	 | ||||
| }; | ||||
| @ -266,6 +269,12 @@ static int __init root_nfs_parse(char *name, char *buf) | ||||
| 			case Opt_tcp: | ||||
| 				nfs_data.flags |= NFS_MOUNT_TCP; | ||||
| 				break; | ||||
| 			case Opt_acl: | ||||
| 				nfs_data.flags &= ~NFS_MOUNT_NOACL; | ||||
| 				break; | ||||
| 			case Opt_noacl: | ||||
| 				nfs_data.flags |= NFS_MOUNT_NOACL; | ||||
| 				break; | ||||
| 			default :  | ||||
| 				return 0; | ||||
| 		} | ||||
|  | ||||
| @ -301,6 +301,9 @@ extern u32 root_nfs_parse_addr(char *name); /*__init*/ | ||||
|  * linux/fs/nfs/file.c | ||||
|  */ | ||||
| extern struct inode_operations nfs_file_inode_operations; | ||||
| #ifdef CONFIG_NFS_V3 | ||||
| extern struct inode_operations nfs3_file_inode_operations; | ||||
| #endif /* CONFIG_NFS_V3 */ | ||||
| extern struct file_operations nfs_file_operations; | ||||
| extern struct address_space_operations nfs_file_aops; | ||||
| 
 | ||||
| @ -315,6 +318,22 @@ static inline struct rpc_cred *nfs_file_cred(struct file *file) | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * linux/fs/nfs/xattr.c | ||||
|  */ | ||||
| #ifdef CONFIG_NFS_V3_ACL | ||||
| extern ssize_t nfs3_listxattr(struct dentry *, char *, size_t); | ||||
| extern ssize_t nfs3_getxattr(struct dentry *, const char *, void *, size_t); | ||||
| extern int nfs3_setxattr(struct dentry *, const char *, | ||||
| 			const void *, size_t, int); | ||||
| extern int nfs3_removexattr (struct dentry *, const char *name); | ||||
| #else | ||||
| # define nfs3_listxattr NULL | ||||
| # define nfs3_getxattr NULL | ||||
| # define nfs3_setxattr NULL | ||||
| # define nfs3_removexattr NULL | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * linux/fs/nfs/direct.c | ||||
|  */ | ||||
| @ -329,6 +348,9 @@ extern ssize_t nfs_file_direct_write(struct kiocb *iocb, const char __user *buf, | ||||
|  * linux/fs/nfs/dir.c | ||||
|  */ | ||||
| extern struct inode_operations nfs_dir_inode_operations; | ||||
| #ifdef CONFIG_NFS_V3 | ||||
| extern struct inode_operations nfs3_dir_inode_operations; | ||||
| #endif /* CONFIG_NFS_V3 */ | ||||
| extern struct file_operations nfs_dir_operations; | ||||
| extern struct dentry_operations nfs_dentry_operations; | ||||
| 
 | ||||
| @ -449,6 +471,15 @@ static inline void nfs_readdata_free(struct nfs_read_data *p) | ||||
| 
 | ||||
| extern void  nfs_readdata_release(struct rpc_task *task); | ||||
| 
 | ||||
| /*
 | ||||
|  * linux/fs/nfs3proc.c | ||||
|  */ | ||||
| #ifdef CONFIG_NFS_V3_ACL | ||||
| extern struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type); | ||||
| extern int nfs3_proc_setacl(struct inode *inode, int type, | ||||
| 			    struct posix_acl *acl); | ||||
| #endif /* CONFIG_NFS_V3_ACL */ | ||||
| 
 | ||||
| /*
 | ||||
|  * linux/fs/mount_clnt.c | ||||
|  * (Used only by nfsroot module) | ||||
|  | ||||
| @ -10,6 +10,7 @@ | ||||
| struct nfs_server { | ||||
| 	struct rpc_clnt *	client;		/* RPC client handle */ | ||||
| 	struct rpc_clnt *	client_sys;	/* 2nd handle for FSINFO */ | ||||
| 	struct rpc_clnt *	client_acl;	/* ACL RPC client handle */ | ||||
| 	struct nfs_rpc_ops *	rpc_ops;	/* NFS protocol vector */ | ||||
| 	struct backing_dev_info	backing_dev_info; | ||||
| 	int			flags;		/* various flags */ | ||||
|  | ||||
| @ -58,6 +58,7 @@ struct nfs_mount_data { | ||||
| #define NFS_MOUNT_KERBEROS	0x0100	/* 3 */ | ||||
| #define NFS_MOUNT_NONLM		0x0200	/* 3 */ | ||||
| #define NFS_MOUNT_BROKEN_SUID	0x0400	/* 4 */ | ||||
| #define NFS_MOUNT_NOACL		0x0800	/* 4 */ | ||||
| #define NFS_MOUNT_STRICTLOCK	0x1000	/* reserved for NFSv4 */ | ||||
| #define NFS_MOUNT_SECFLAVOUR	0x2000	/* 5 */ | ||||
| #define NFS_MOUNT_FLAGMASK	0xFFFF | ||||
|  | ||||
| @ -2,6 +2,7 @@ | ||||
| #define _LINUX_NFS_XDR_H | ||||
| 
 | ||||
| #include <linux/sunrpc/xprt.h> | ||||
| #include <linux/nfsacl.h> | ||||
| 
 | ||||
| struct nfs4_fsid { | ||||
| 	__u64 major; | ||||
| @ -368,6 +369,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; | ||||
| @ -491,6 +506,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; | ||||
| @ -748,4 +772,7 @@ extern struct rpc_version	nfs_version2; | ||||
| extern struct rpc_version	nfs_version3; | ||||
| extern struct rpc_version	nfs_version4; | ||||
| 
 | ||||
| extern struct rpc_version	nfsacl_version3; | ||||
| extern struct rpc_program	nfsacl_program; | ||||
| 
 | ||||
| #endif | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user