diff --git a/config/kernel-acl.m4 b/config/kernel-acl.m4 index 0f1c24656730..b344a3923e9d 100644 --- a/config/kernel-acl.m4 +++ b/config/kernel-acl.m4 @@ -213,6 +213,82 @@ AC_DEFUN([ZFS_AC_KERNEL_INODE_OPERATIONS_SET_ACL], [ ]) ]) +dnl # +dnl # 3.17 API change, +dnl # check whether generic_key_instantiate() exists +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_GENERIC_KEY_INSTANTIATE], [ + ZFS_LINUX_TEST_SRC([generic_key_instantiate], [ + #include + ],[ + struct key_type k; + + k.instantiate = generic_key_instantiate; + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_GENERIC_KEY_INSTANTIATE], [ + AC_MSG_CHECKING([whether generic_key_instantiate() exists]) + ZFS_LINUX_TEST_RESULT([generic_key_instantiate], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_KERNEL_GENERIC_KEY_INSTANTIATE, 1, + [generic_key_instantiate() exists]) + ],[ + AC_MSG_RESULT(no) + ]) +]) + +dnl # +dnl # 3.17 API change, +dnl # check whether KEY_FLAG_ROOT_CAN_INVAL exists +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_KEY_FLAG_ROOT_CAN_INVAL_EXISTS], [ + ZFS_LINUX_TEST_SRC([KEY_FLAG_ROOT_CAN_INVAL], [ + #include + ],[ + unsigned long addr; + + set_bit(KEY_FLAG_ROOT_CAN_INVAL, &addr); + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_KEY_FLAG_ROOT_CAN_INVAL_EXISTS], [ + AC_MSG_CHECKING([whether HAVE_KEY_FLAG_ROOT_CAN_INVAL exists]) + ZFS_LINUX_TEST_RESULT([KEY_FLAG_ROOT_CAN_INVAL], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_KERNEL_KEY_FLAG_ROOT_CAN_INVAL, 1, + [HAVE_KEY_FLAG_ROOT_CAN_INVAL exists]) + ],[ + AC_MSG_RESULT(no) + ]) +]) + +dnl # +dnl # 4.4 API change, +dnl # check whether user_key_payload() exists +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_USER_KEY_PAYLOAD], [ + ZFS_LINUX_TEST_SRC([user_key_payload], [ + #include + ],[ + const struct user_key_payload *ukp; + const struct key *k = NULL; + + ukp = user_key_payload(k); + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_USER_KEY_PAYLOAD], [ + AC_MSG_CHECKING([whether user_key_payload() exists]) + ZFS_LINUX_TEST_RESULT([user_key_payload], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_KERNEL_USER_KEY_PAYLOAD, 1, + [user_key_payload() exists]) + ],[ + AC_MSG_RESULT(no) + ]) +]) + dnl # dnl # 4.7 API change, dnl # The kernel get_acl will now check cache before calling i_op->get_acl and @@ -239,6 +315,86 @@ AC_DEFUN([ZFS_AC_KERNEL_GET_ACL_HANDLE_CACHE], [ ]) ]) +dnl # +dnl # 4.7 API change, +dnl # keyring_alloc() takes 8 args instead of 7 +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_KEYRING_ALLOC_8_ARGS], [ + ZFS_LINUX_TEST_SRC([keyring_alloc_8_args], [ + #include + #include + ],[ + struct key *k; + struct cred c; + + k = keyring_alloc("", GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, &c, 0, + 0, NULL, NULL); + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_KEYRING_ALLOC_8_ARGS], [ + AC_MSG_CHECKING([whether keyring_alloc() takes 8 args]) + ZFS_LINUX_TEST_RESULT([keyring_alloc_8_args], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_KERNEL_KEYRING_ALLOC_WITH_8_ARGS, 1, + [keyring_alloc() takes 8 args]) + ],[ + AC_MSG_RESULT(no) + ]) +]) + +dnl # +dnl # 4.11 API change, +dnl # check whether user_key_payload_rcu() exists +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_USER_KEY_PAYLOAD_RCU], [ + ZFS_LINUX_TEST_SRC([user_key_payload_rcu], [ + #include + ],[ + const struct user_key_payload *ukp; + const struct key *k = NULL; + + ukp = user_key_payload_rcu(k); + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_USER_KEY_PAYLOAD_RCU], [ + AC_MSG_CHECKING([whether user_key_payload_rcu() exists]) + ZFS_LINUX_TEST_RESULT([user_key_payload_rcu], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_KERNEL_USER_KEY_PAYLOAD_RCU, 1, + [user_key_payload_rcu() exists]) + ],[ + AC_MSG_RESULT(no) + ]) +]) + +dnl # +dnl # 4.13.10 API change, +dnl # check whether key_is_positive() exists +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_KEY_IS_POSITIVE], [ + ZFS_LINUX_TEST_SRC([key_is_positive], [ + #include + ],[ + struct key *k = NULL; + bool v; + + v = key_is_positive(k); + ]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_KEY_IS_POSITIVE], [ + AC_MSG_CHECKING([whether key_is_positive() exists]) + ZFS_LINUX_TEST_RESULT([key_is_positive], [ + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_KERNEL_KEY_IS_POSITIVE, 1, + [key_is_positive() exists]) + ],[ + AC_MSG_RESULT(no) + ]) +]) + dnl # dnl # 4.16 kernel: check if struct posix_acl acl.a_refcount is a refcount_t. dnl # It's an atomic_t on older kernels. @@ -272,7 +428,13 @@ AC_DEFUN([ZFS_AC_KERNEL_SRC_ACL], [ ZFS_AC_KERNEL_SRC_POSIX_ACL_VALID_WITH_NS ZFS_AC_KERNEL_SRC_INODE_OPERATIONS_GET_ACL ZFS_AC_KERNEL_SRC_INODE_OPERATIONS_SET_ACL + ZFS_AC_KERNEL_SRC_GENERIC_KEY_INSTANTIATE + ZFS_AC_KERNEL_SRC_KEY_FLAG_ROOT_CAN_INVAL_EXISTS + ZFS_AC_KERNEL_SRC_USER_KEY_PAYLOAD ZFS_AC_KERNEL_SRC_GET_ACL_HANDLE_CACHE + ZFS_AC_KERNEL_SRC_KEYRING_ALLOC_8_ARGS + ZFS_AC_KERNEL_SRC_USER_KEY_PAYLOAD_RCU + ZFS_AC_KERNEL_SRC_KEY_IS_POSITIVE ZFS_AC_KERNEL_SRC_ACL_HAS_REFCOUNT ]) @@ -284,6 +446,12 @@ AC_DEFUN([ZFS_AC_KERNEL_ACL], [ ZFS_AC_KERNEL_POSIX_ACL_VALID_WITH_NS ZFS_AC_KERNEL_INODE_OPERATIONS_GET_ACL ZFS_AC_KERNEL_INODE_OPERATIONS_SET_ACL + ZFS_AC_KERNEL_GENERIC_KEY_INSTANTIATE + ZFS_AC_KERNEL_KEY_FLAG_ROOT_CAN_INVAL_EXISTS + ZFS_AC_KERNEL_USER_KEY_PAYLOAD ZFS_AC_KERNEL_GET_ACL_HANDLE_CACHE + ZFS_AC_KERNEL_KEYRING_ALLOC_8_ARGS + ZFS_AC_KERNEL_USER_KEY_PAYLOAD_RCU + ZFS_AC_KERNEL_KEY_IS_POSITIVE ZFS_AC_KERNEL_ACL_HAS_REFCOUNT ]) diff --git a/config/kernel-config-defined.m4 b/config/kernel-config-defined.m4 index 0ee4231cc2db..087fce413d58 100644 --- a/config/kernel-config-defined.m4 +++ b/config/kernel-config-defined.m4 @@ -24,6 +24,7 @@ AC_DEFUN([ZFS_AC_KERNEL_CONFIG_DEFINED], [ ZFS_AC_KERNEL_SRC_CONFIG_TRIM_UNUSED_KSYMS ZFS_AC_KERNEL_SRC_CONFIG_ZLIB_INFLATE ZFS_AC_KERNEL_SRC_CONFIG_ZLIB_DEFLATE + ZFS_AC_KERNEL_SRC_ZFS_NFS4_ACL AC_MSG_CHECKING([for kernel config option compatibility]) ZFS_LINUX_TEST_COMPILE_ALL([config]) @@ -34,6 +35,7 @@ AC_DEFUN([ZFS_AC_KERNEL_CONFIG_DEFINED], [ ZFS_AC_KERNEL_CONFIG_TRIM_UNUSED_KSYMS ZFS_AC_KERNEL_CONFIG_ZLIB_INFLATE ZFS_AC_KERNEL_CONFIG_ZLIB_DEFLATE + ZFS_AC_KERNEL_ZFS_NFS4_ACL ]) dnl # @@ -181,3 +183,36 @@ AC_DEFUN([ZFS_AC_KERNEL_CONFIG_ZLIB_DEFLATE], [ *** Rebuild the kernel with CONFIG_ZLIB_DEFLATE=y|m set.]) ]) ]) + +dnl # +dnl # Check if ZFS_NFS4_ACL should be defined +dnl # +dnl # This requires CONFIG_KEYS to be enabled and +dnl # CONFIG_PREEMPT_RCU to be disabled. The key subsystem is used +dnl # to map between uid/gid values and user/group names, and +dnl # if CONFIG_PREEMPT_RCU compiling will result in the error: +dnl # +dnl # FATAL: modpost: GPL-incompatible module zfs.ko uses GPL-only symbol +dnl # __rcu_read_lock' +dnl # +AC_DEFUN([ZFS_AC_KERNEL_SRC_ZFS_NFS4_ACL], [ + ZFS_LINUX_TEST_SRC([zfs_nfs4_acl], [ + #if !defined(CONFIG_KEYS) + #error CONFIG_KEYS required for ZFS ACL support + #endif + #if defined(CONFIG_PREEMPT_RCU) + #error CONFIG_PREEMPT_RCU not compatible with ZFS ACL support + #endif + ],[]) +]) + +AC_DEFUN([ZFS_AC_KERNEL_ZFS_NFS4_ACL], [ + AC_MSG_CHECKING([whether kernel options for ZFS ACLs are set]) + ZFS_LINUX_TEST_RESULT([zfs_nfs4_acl], [ + AC_MSG_RESULT([yes]) + AC_DEFINE(ZFS_NFS4_ACL, 1, [kernel options for ZFS ACLs are set]) + ],[ + AC_MSG_RESULT([no]) + ]) +]) + diff --git a/include/os/linux/kernel/linux/xattr_compat.h b/include/os/linux/kernel/linux/xattr_compat.h index 8348e99198af..6cf6860f01b7 100644 --- a/include/os/linux/kernel/linux/xattr_compat.h +++ b/include/os/linux/kernel/linux/xattr_compat.h @@ -26,6 +26,10 @@ #ifndef _ZFS_XATTR_H #define _ZFS_XATTR_H +#include +#include +#include +#include #include /* @@ -164,6 +168,19 @@ fn(struct dentry *dentry, const char *name, const void *buffer, \ #error "Unsupported kernel" #endif +/* + * Linux 4.4 and 4.11 API changes; rcu_dereference_key became + * user_key_payload in 4.4 and then changed to user_key_payload_rcu + * in 4.11. + */ +#if defined(HAVE_KERNEL_USER_KEY_PAYLOAD) +#define zpl_user_key_payload_rcu(k) user_key_payload(k) +#elif defined(HAVE_KERNEL_USER_KEY_PAYLOAD_RCU) +#define zpl_user_key_payload_rcu(k) user_key_payload_rcu(k) +#else +#define zpl_user_key_payload_rcu(k) rcu_dereference_key(k) +#endif + /* * Linux 3.7 API change. posix_acl_{from,to}_xattr gained the user_ns * parameter. All callers are expected to pass the &init_user_ns which diff --git a/include/os/linux/spl/sys/acl.h b/include/os/linux/spl/sys/acl.h index 9fc79c025caf..a20309eb19c5 100644 --- a/include/os/linux/spl/sys/acl.h +++ b/include/os/linux/spl/sys/acl.h @@ -54,6 +54,7 @@ typedef struct ace_object { #define ACE_READ_NAMED_ATTRS 0x00000008 #define ACE_WRITE_NAMED_ATTRS 0x00000010 #define ACE_EXECUTE 0x00000020 +#define ACE_TRAVERSE 0x00000020 #define ACE_DELETE_CHILD 0x00000040 #define ACE_READ_ATTRIBUTES 0x00000080 #define ACE_WRITE_ATTRIBUTES 0x00000100 @@ -109,6 +110,30 @@ typedef struct ace_object { ACE_WRITE_NAMED_ATTRS|ACE_EXECUTE|ACE_DELETE_CHILD|ACE_READ_ATTRIBUTES| \ ACE_WRITE_ATTRIBUTES|ACE_DELETE|ACE_READ_ACL|ACE_WRITE_ACL| \ ACE_WRITE_OWNER|ACE_SYNCHRONIZE) + +#define ACE_ALL_WRITE_PERMS (ACE_WRITE_DATA|ACE_APPEND_DATA| \ + ACE_WRITE_ATTRIBUTES|ACE_WRITE_NAMED_ATTRS|ACE_WRITE_ACL| \ + ACE_WRITE_OWNER|ACE_DELETE|ACE_DELETE_CHILD) + +#define ACE_READ_PERMS (ACE_READ_DATA|ACE_READ_ACL|ACE_READ_ATTRIBUTES| \ + ACE_READ_NAMED_ATTRS) + +#define ACE_WRITE_PERMS (ACE_WRITE_DATA|ACE_APPEND_DATA|ACE_WRITE_ATTRIBUTES| \ + ACE_WRITE_NAMED_ATTRS) + +#define ACE_MODIFY_PERMS (ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \ + ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_READ_NAMED_ATTRS| \ + ACE_WRITE_NAMED_ATTRS|ACE_EXECUTE|ACE_DELETE_CHILD|ACE_READ_ATTRIBUTES| \ + ACE_WRITE_ATTRIBUTES|ACE_DELETE|ACE_READ_ACL|ACE_SYNCHRONIZE) + +/* + * The following flags are supported by both NFSv4 ACLs and ace_t. + */ +#define ACE_NFSV4_SUP_FLAGS (ACE_FILE_INHERIT_ACE | \ + ACE_DIRECTORY_INHERIT_ACE | \ + ACE_NO_PROPAGATE_INHERIT_ACE | \ + ACE_INHERIT_ONLY_ACE | \ + ACE_IDENTIFIER_GROUP) /* END CSTYLED */ #define VSA_ACE 0x0010 diff --git a/include/os/linux/zfs/sys/Makefile.am b/include/os/linux/zfs/sys/Makefile.am index 16df22c6c025..dc43596a9aca 100644 --- a/include/os/linux/zfs/sys/Makefile.am +++ b/include/os/linux/zfs/sys/Makefile.am @@ -22,6 +22,7 @@ KERNEL_H = \ $(top_srcdir)/include/os/linux/zfs/sys/zfs_vfsops.h \ $(top_srcdir)/include/os/linux/zfs/sys/zfs_vnops.h \ $(top_srcdir)/include/os/linux/zfs/sys/zfs_znode_impl.h \ + $(top_srcdir)/include/os/linux/zfs/sys/zpl_xattr.h \ $(top_srcdir)/include/os/linux/zfs/sys/zpl.h if CONFIG_KERNEL diff --git a/include/os/linux/zfs/sys/zpl.h b/include/os/linux/zfs/sys/zpl.h index ef5a0b842d09..928f1604e055 100644 --- a/include/os/linux/zfs/sys/zpl.h +++ b/include/os/linux/zfs/sys/zpl.h @@ -91,6 +91,8 @@ zpl_chmod_acl(struct inode *ip) } #endif /* CONFIG_FS_POSIX_ACL */ +extern int zpl_permission(struct inode *ip, int mask); + extern xattr_handler_t *zpl_xattr_handlers[]; /* zpl_ctldir.c */ diff --git a/include/os/linux/zfs/sys/zpl_xattr.h b/include/os/linux/zfs/sys/zpl_xattr.h new file mode 100644 index 000000000000..ddbd1f975593 --- /dev/null +++ b/include/os/linux/zfs/sys/zpl_xattr.h @@ -0,0 +1,28 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#ifndef _ZPL_XATTR_H +#define _ZPL_XATTR_H + +int zpl_xattr_init(void); +void zpl_xattr_fini(void); + +#endif /* _ZPL_XATTR_H */ diff --git a/include/sys/zfs_ioctl.h b/include/sys/zfs_ioctl.h index d4ffe70bbe36..4abf1c244b23 100644 --- a/include/sys/zfs_ioctl.h +++ b/include/sys/zfs_ioctl.h @@ -68,6 +68,7 @@ extern "C" { */ #define ZFS_ACLTYPE_OFF 0 #define ZFS_ACLTYPE_POSIXACL 1 +#define ZFS_ACLTYPE_NFSV4 2 /* * Field manipulation macros for the drr_versioninfo field of the diff --git a/lib/libspl/include/sys/acl.h b/lib/libspl/include/sys/acl.h index e6df864f850f..bace54ccbdf8 100644 --- a/lib/libspl/include/sys/acl.h +++ b/lib/libspl/include/sys/acl.h @@ -19,8 +19,12 @@ * CDDL HEADER END */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2014 Garrett D'Amore + * Copyright 2014 Nexenta Systems, Inc. All rights reserved. + * Copyright 2017 RackTop Systems. */ #ifndef _SYS_ACL_H @@ -74,37 +78,55 @@ typedef struct acl_info acl_t; /* * The following are defined for ace_t. + * + * Note, these are intentionally the same as the Windows + * "File Access Rights Constants" you can find on MSDN. + * (See also: "Standard Access Rights" on MSDN). + * + * The equivalent Windows names for these are just like + * those show below, with FILE_ in place of ACE_, except + * as noted below. Also note that Windows uses a special + * privilege: BYPASS_TRAVERSE_CHECKING, normally granted + * to everyone, that causes the absence of ACE_TRAVERSE + * to be ignored. */ -#define ACE_READ_DATA 0x00000001 -#define ACE_LIST_DIRECTORY 0x00000001 -#define ACE_WRITE_DATA 0x00000002 -#define ACE_ADD_FILE 0x00000002 -#define ACE_APPEND_DATA 0x00000004 -#define ACE_ADD_SUBDIRECTORY 0x00000004 -#define ACE_READ_NAMED_ATTRS 0x00000008 -#define ACE_WRITE_NAMED_ATTRS 0x00000010 -#define ACE_EXECUTE 0x00000020 -#define ACE_DELETE_CHILD 0x00000040 -#define ACE_READ_ATTRIBUTES 0x00000080 -#define ACE_WRITE_ATTRIBUTES 0x00000100 -#define ACE_DELETE 0x00010000 -#define ACE_READ_ACL 0x00020000 -#define ACE_WRITE_ACL 0x00040000 -#define ACE_WRITE_OWNER 0x00080000 -#define ACE_SYNCHRONIZE 0x00100000 - -#define ACE_FILE_INHERIT_ACE 0x0001 -#define ACE_DIRECTORY_INHERIT_ACE 0x0002 -#define ACE_NO_PROPAGATE_INHERIT_ACE 0x0004 -#define ACE_INHERIT_ONLY_ACE 0x0008 +#define ACE_READ_DATA 0x00000001 /* file: read data */ +#define ACE_LIST_DIRECTORY 0x00000001 /* dir: list files */ +#define ACE_WRITE_DATA 0x00000002 /* file: write data */ +#define ACE_ADD_FILE 0x00000002 /* dir: create file */ +#define ACE_APPEND_DATA 0x00000004 /* file: append data */ +#define ACE_ADD_SUBDIRECTORY 0x00000004 /* dir: create subdir */ +#define ACE_READ_NAMED_ATTRS 0x00000008 /* FILE_READ_EA */ +#define ACE_WRITE_NAMED_ATTRS 0x00000010 /* FILE_WRITE_EA */ +#define ACE_EXECUTE 0x00000020 /* file: execute */ +#define ACE_TRAVERSE 0x00000020 /* dir: lookup name */ +#define ACE_DELETE_CHILD 0x00000040 /* dir: unlink child */ +#define ACE_READ_ATTRIBUTES 0x00000080 /* (all) stat, etc. */ +#define ACE_WRITE_ATTRIBUTES 0x00000100 /* (all) utimes, etc. */ +#define ACE_DELETE 0x00010000 /* (all) unlink self */ +#define ACE_READ_ACL 0x00020000 /* (all) getsecattr */ +#define ACE_WRITE_ACL 0x00040000 /* (all) setsecattr */ +#define ACE_WRITE_OWNER 0x00080000 /* (all) chown */ +#define ACE_SYNCHRONIZE 0x00100000 /* (all) see MSDN */ + +/* + * Some of the following are the same as Windows uses. (but NOT ALL!) + * See the "ACE_HEADER" structure description on MSDN for details. + * Comments show relations to the MSDN names. + */ +#define ACE_FILE_INHERIT_ACE 0x0001 /* = OBJECT_INHERIT_ACE */ +#define ACE_DIRECTORY_INHERIT_ACE 0x0002 /* = CONTAINER_INHERIT_ACE */ +#define ACE_NO_PROPAGATE_INHERIT_ACE 0x0004 /* = NO_PROPAGATE_INHERIT_ACE */ +#define ACE_INHERIT_ONLY_ACE 0x0008 /* = INHERIT_ONLY_ACE */ #define ACE_SUCCESSFUL_ACCESS_ACE_FLAG 0x0010 #define ACE_FAILED_ACCESS_ACE_FLAG 0x0020 #define ACE_IDENTIFIER_GROUP 0x0040 -#define ACE_INHERITED_ACE 0x0080 +#define ACE_INHERITED_ACE 0x0080 /* INHERITED_ACE, 0x10 on NT */ #define ACE_OWNER 0x1000 #define ACE_GROUP 0x2000 #define ACE_EVERYONE 0x4000 +/* These four are the same as Windows, but with an ACE_ prefix added. */ #define ACE_ACCESS_ALLOWED_ACE_TYPE 0x0000 #define ACE_ACCESS_DENIED_ACE_TYPE 0x0001 #define ACE_SYSTEM_AUDIT_ACE_TYPE 0x0002 @@ -116,10 +138,9 @@ typedef struct acl_info acl_t; #define ACL_FLAGS_ALL (ACL_AUTO_INHERIT|ACL_PROTECTED| \ ACL_DEFAULTED) -#ifdef _KERNEL - /* * These are only applicable in a CIFS context. + * Here again, same as Windows, but with an ACE_ prefix added. */ #define ACE_ACCESS_ALLOWED_COMPOUND_ACE_TYPE 0x04 #define ACE_ACCESS_ALLOWED_OBJECT_ACE_TYPE 0x05 @@ -137,6 +158,8 @@ typedef struct acl_info acl_t; #define ACE_ALL_TYPES 0x001F +#if defined(_KERNEL) + typedef struct ace_object { uid_t a_who; /* uid or gid */ uint32_t a_access_mask; /* read,write,... */ @@ -154,6 +177,21 @@ typedef struct ace_object { ACE_WRITE_ATTRIBUTES|ACE_DELETE|ACE_READ_ACL|ACE_WRITE_ACL| \ ACE_WRITE_OWNER|ACE_SYNCHRONIZE) +#define ACE_ALL_WRITE_PERMS (ACE_WRITE_DATA|ACE_APPEND_DATA| \ + ACE_WRITE_ATTRIBUTES|ACE_WRITE_NAMED_ATTRS|ACE_WRITE_ACL| \ + ACE_WRITE_OWNER|ACE_DELETE|ACE_DELETE_CHILD) + +#define ACE_READ_PERMS (ACE_READ_DATA|ACE_READ_ACL|ACE_READ_ATTRIBUTES| \ + ACE_READ_NAMED_ATTRS) + +#define ACE_WRITE_PERMS (ACE_WRITE_DATA|ACE_APPEND_DATA|ACE_WRITE_ATTRIBUTES| \ + ACE_WRITE_NAMED_ATTRS) + +#define ACE_MODIFY_PERMS (ACE_READ_DATA|ACE_LIST_DIRECTORY|ACE_WRITE_DATA| \ + ACE_ADD_FILE|ACE_APPEND_DATA|ACE_ADD_SUBDIRECTORY|ACE_READ_NAMED_ATTRS| \ + ACE_WRITE_NAMED_ATTRS|ACE_EXECUTE|ACE_DELETE_CHILD|ACE_READ_ATTRIBUTES| \ + ACE_WRITE_ATTRIBUTES|ACE_DELETE|ACE_READ_ACL|ACE_SYNCHRONIZE) + /* * The following flags are supported by both NFSv4 ACLs and ace_t. */ @@ -217,6 +255,7 @@ typedef struct ace_object { #define ACL_APPEND_ID 0x1 /* append uid/gid to user/group entries */ #define ACL_COMPACT_FMT 0x2 /* build ACL in ls -V format */ #define ACL_NORESOLVE 0x4 /* don't do name service lookups */ +#define ACL_SID_FMT 0x8 /* use usersid/groupsid when appropriate */ /* * Legacy aclcheck errors for aclent_t ACLs @@ -272,13 +311,8 @@ extern int cmp2acls(void *, void *); #endif /* !defined(_KERNEL) */ -#if defined(__STDC__) extern int acl(const char *path, int cmd, int cnt, void *buf); extern int facl(int fd, int cmd, int cnt, void *buf); -#else /* !__STDC__ */ -extern int acl(); -extern int facl(); -#endif /* defined(__STDC__) */ #ifdef __cplusplus } diff --git a/man/man8/zfsprops.8 b/man/man8/zfsprops.8 index 139198db0c48..8e794ad7ebc9 100644 --- a/man/man8/zfsprops.8 +++ b/man/man8/zfsprops.8 @@ -660,6 +660,23 @@ property set to off then ACLs are disabled. .It Sy noacl an alias for .Sy off +.It Sy nfsv4 +indicates that NFSv4 ACLs should be used. These ACLs can be managed with +the +.Xr zgetfacl 1 +and +.Xr zsetfacl 1 +commands pending Linux kernel support and tools for NFSv4 local +filesystem ACLs. They are also accessible via the standard Linux +system.nfs4_acl extended attribute if the kernel request-key mechanism is +configured and the NFSv4 nfsidmap program is installed along with the +following line in either /etc/request-key.conf or in a file in +/etc/request-key.d: +.Bd -literal +create zfs_nfs4acl_resolver * * /usr/sbin/nfsidmap -t 600 %k %d +.Ed +Unless you are installing zfs manually, this dependency and configuration was likely +dealt with by your package manager. .It Sy posixacl indicates POSIX ACLs should be used. POSIX ACLs are specific to Linux and are not functional on other platforms. POSIX ACLs are stored as an extended diff --git a/module/os/freebsd/zfs/kmod_core.c b/module/os/freebsd/zfs/kmod_core.c index 10807afa3d31..3c178d0ac6d2 100644 --- a/module/os/freebsd/zfs/kmod_core.c +++ b/module/os/freebsd/zfs/kmod_core.c @@ -106,11 +106,6 @@ SYSCTL_INT(_vfs_zfs_version, OID_AUTO, ioctl, CTLFLAG_RD, &zfs_version_ioctl, static struct cdev *zfsdev; -extern void zfs_init(void); -extern void zfs_fini(void); -extern void zfs_ioctl_init(void); - - static struct root_hold_token *zfs_root_token; extern uint_t rrw_tsd_key; diff --git a/module/os/freebsd/zfs/zfs_vfsops.c b/module/os/freebsd/zfs/zfs_vfsops.c index d6f7fc11e9bd..bf637c7f6201 100644 --- a/module/os/freebsd/zfs/zfs_vfsops.c +++ b/module/os/freebsd/zfs/zfs_vfsops.c @@ -2165,7 +2165,7 @@ zfs_vnodes_adjust_back(void) #endif } -void +int zfs_init(void) { @@ -2191,6 +2191,8 @@ zfs_init(void) dmu_objset_register_type(DMU_OST_ZFS, zfs_space_delta_cb); zfsvfs_taskq = taskq_create("zfsvfs", 1, minclsyspri, 0, 0, 0); + + return (0); } void diff --git a/module/os/linux/zfs/policy.c b/module/os/linux/zfs/policy.c index 5525302266c7..9ad49573e29b 100644 --- a/module/os/linux/zfs/policy.c +++ b/module/os/linux/zfs/policy.c @@ -33,6 +33,7 @@ #include #include #include +#include /* * The passed credentials cannot be directly verified because Linux only @@ -105,12 +106,19 @@ secpolicy_sys_config(const cred_t *cr, boolean_t checkonly) * Like secpolicy_vnode_access() but we get the actual wanted mode and the * current mode of the file, not the missing bits. * - * Enforced in the Linux VFS. + * If filesystem is using NFSv4 ACLs, validate the current mode + * and the wanted mode are the same, otherwise access fails. + * + * If using POSIX ACLs or no ACLs, enforced in the Linux VFS. */ int secpolicy_vnode_access2(const cred_t *cr, struct inode *ip, uid_t owner, mode_t curmode, mode_t wantmode) { + if (ITOZSB(ip)->z_acl_type == ZFS_ACLTYPE_NFSV4 && + (~curmode & wantmode) != 0) + return (EACCES); + return (0); } diff --git a/module/os/linux/zfs/zfs_vfsops.c b/module/os/linux/zfs/zfs_vfsops.c index 6a713524ff92..2b63079588e3 100644 --- a/module/os/linux/zfs/zfs_vfsops.c +++ b/module/os/linux/zfs/zfs_vfsops.c @@ -59,6 +59,7 @@ #include #include #include +#include #include #include "zfs_comutil.h" @@ -365,6 +366,10 @@ acltype_changed_cb(void *arg, uint64_t newval) zfsvfs->z_sb->s_flags &= ~SB_POSIXACL; #endif /* CONFIG_FS_POSIX_ACL */ break; + case ZFS_ACLTYPE_NFSV4: + zfsvfs->z_acl_type = ZFS_ACLTYPE_NFSV4; + zfsvfs->z_sb->s_flags &= ~SB_POSIXACL; + break; default: break; } @@ -2126,13 +2131,14 @@ zfs_get_vfs_flag_unmounted(objset_t *os) return (unmounted); } -void +int zfs_init(void) { zfsctl_init(); zfs_znode_init(); dmu_objset_register_type(DMU_OST_ZFS, zfs_space_delta_cb); register_filesystem(&zpl_fs_type); + return (zpl_xattr_init()); } void @@ -2143,6 +2149,7 @@ zfs_fini(void) */ taskq_wait(system_delay_taskq); taskq_wait(system_taskq); + zpl_xattr_fini(); unregister_filesystem(&zpl_fs_type); zfs_znode_fini(); zfsctl_fini(); diff --git a/module/os/linux/zfs/zpl_inode.c b/module/os/linux/zfs/zpl_inode.c index c1b5825748c9..33e7a5c5fe30 100644 --- a/module/os/linux/zfs/zpl_inode.c +++ b/module/os/linux/zfs/zpl_inode.c @@ -668,6 +668,7 @@ const struct inode_operations zpl_inode_operations = { #endif /* HAVE_SET_ACL */ .get_acl = zpl_get_acl, #endif /* CONFIG_FS_POSIX_ACL */ + .permission = zpl_permission, }; const struct inode_operations zpl_dir_inode_operations = { @@ -701,6 +702,7 @@ const struct inode_operations zpl_dir_inode_operations = { #endif /* HAVE_SET_ACL */ .get_acl = zpl_get_acl, #endif /* CONFIG_FS_POSIX_ACL */ + .permission = zpl_permission, }; const struct inode_operations zpl_symlink_inode_operations = { @@ -740,6 +742,7 @@ const struct inode_operations zpl_special_inode_operations = { #endif /* HAVE_SET_ACL */ .get_acl = zpl_get_acl, #endif /* CONFIG_FS_POSIX_ACL */ + .permission = zpl_permission, }; dentry_operations_t zpl_dentry_operations = { diff --git a/module/os/linux/zfs/zpl_super.c b/module/os/linux/zfs/zpl_super.c index 08cf758624dd..da92b0d5236f 100644 --- a/module/os/linux/zfs/zpl_super.c +++ b/module/os/linux/zfs/zpl_super.c @@ -188,16 +188,17 @@ __zpl_show_options(struct seq_file *seq, zfsvfs_t *zfsvfs) seq_printf(seq, ",%s", zfsvfs->z_flags & ZSB_XATTR ? "xattr" : "noxattr"); -#ifdef CONFIG_FS_POSIX_ACL switch (zfsvfs->z_acl_type) { case ZFS_ACLTYPE_POSIXACL: seq_puts(seq, ",posixacl"); break; + case ZFS_ACLTYPE_NFSV4: + seq_puts(seq, ",acl"); + break; default: seq_puts(seq, ",noacl"); break; } -#endif /* CONFIG_FS_POSIX_ACL */ return (0); } diff --git a/module/os/linux/zfs/zpl_xattr.c b/module/os/linux/zfs/zpl_xattr.c index 956aed528201..442ad0a7201c 100644 --- a/module/os/linux/zfs/zpl_xattr.c +++ b/module/os/linux/zfs/zpl_xattr.c @@ -80,6 +80,7 @@ #include #include #include +#include #include #include #include @@ -1337,6 +1338,657 @@ xattr_handler_t zpl_xattr_acl_default_handler = #endif /* CONFIG_FS_POSIX_ACL */ +int +zpl_permission(struct inode *ip, int mask) +{ + if (ITOZSB(ip)->z_acl_type == ZFS_ACLTYPE_NFSV4) { + /* + * XXX - mask could also include + * MAY_APPEND|MAY_ACCESS|MAY_OPEN|MAY_CHDIR, do we care? + * What about V_APPEND zfs_access flag? + */ + return (-zfs_access(ip, (mask & + (MAY_READ|MAY_WRITE|MAY_EXEC)) << 6, 0, CRED())); + } else { + return (generic_permission(ip, mask)); + } +} + +#if defined(ZFS_NFS4_ACL) + +static const struct cred *nfs4acl_resolver_cred; + +#define NFS4ACL_XATTR "system.nfs4_acl" +#define NFS4ACL_NAMESZ 128 +#define NFS4ACL_UINT_MAXLEN 11 +#define NFS4ACL_XDR_MOD 4 + +#if defined(HAVE_KERNEL_GENERIC_KEY_INSTANTIATE) +static int +nfs4acl_key_preparse(struct key_preparsed_payload *kpp) +{ + struct user_key_payload *ukp; + size_t dl = kpp->datalen; + + if (kpp->data == NULL || dl < 1 || dl > 32767) + return (-EINVAL); + + ukp = kmem_alloc(dl + sizeof (*ukp), KM_SLEEP); + ukp->datalen = dl; + kpp->quotalen = dl; + kpp->payload.data[0] = ukp; + memcpy(ukp->data, kpp->data, dl); + + return (0); +} + +static void +nfs4acl_key_free_preparse(struct key_preparsed_payload *kpp) +{ + kfree(kpp->payload.data[0]); +} +#else /* HAVE_KERNEL_GENERIC_KEY_INSTANTIATE */ +static int +nfs4acl_key_instantiate(struct key *k, struct key_preparsed_payload *kpp) +{ + const void *data = kpp->data; + size_t datalen = kpp->datalen; + + struct user_key_payload *ukp; + int ret; + + if (!data || datalen < 1 || datalen > 32767) + return (-EINVAL); + + ret = key_payload_reserve(k, datalen); + if (ret < 0) + return (ret); + + ukp = kmem_alloc(sizeof (*ukp) + datalen, KM_SLEEP); + + ukp->datalen = datalen; + memcpy(ukp->data, data, datalen); + rcu_assign_keypointer(k, ukp); + + return (0); +} + +static int +nfs4acl_key_match(const struct key *k, const void *desc) +{ + return (strcmp(k->description, desc) == 0); +} +#endif /* HAVE_KERNEL_GENERIC_KEY_INSTANTIATE */ + +static void +nfs4acl_key_destroy(struct key *k) +{ +#if defined(HAVE_KERNEL_GENERIC_KEY_INSTANTIATE) + struct user_key_payload *ukp = k->payload.data[0]; +#else + struct user_key_payload *ukp = k->payload.data; +#endif + kmem_free(ukp, sizeof (*ukp) + ukp->datalen); +} + +static void +nfs4acl_key_describe(const struct key *k, struct seq_file *sf) +{ + seq_puts(sf, k->description); +#if defined(HAVE_KERNEL_KEY_IS_POSITIVE) + if (key_is_positive(k)) +#else + if (key_is_instantiated(k)) +#endif + seq_printf(sf, ": %u", k->datalen); +} + +static long +nfs4acl_key_read(const struct key *k, char __user *ub, size_t ublen) +{ + const struct user_key_payload *ukp; + long ret; + + ukp = zpl_user_key_payload_rcu(k); + ret = ukp->datalen; + + if (ub && ublen > 0) { + if (ublen > ukp->datalen) + ublen = ukp->datalen; + + if (copy_to_user(ub, ukp->data, ublen) != 0) + ret = -EFAULT; + } + + return (ret); +} + +/* + * Use the existing nfs4 userspace resolver mechanism, but instantiate it + * under a different name to avoid a conflict with nfs. This will require a new + * line in /etc/request-key.conf or a new file in /etc/request-key.d containing: + * + * create zfs_nfs4acl_resolver * * /usr/sbin/nfsidmap -t 600 %k %d + * + */ + +static struct key_type key_type_nfs4acl_resolver = { + .name = "zfs_nfs4acl_resolver", +#if defined(HAVE_KERNEL_GENERIC_KEY_INSTANTIATE) + .preparse = nfs4acl_key_preparse, + .free_preparse = nfs4acl_key_free_preparse, + .instantiate = generic_key_instantiate, +#else + .instantiate = nfs4acl_key_instantiate, + .match = nfs4acl_key_match, +#endif + .revoke = user_revoke, + .destroy = nfs4acl_key_destroy, + .describe = nfs4acl_key_describe, + .read = nfs4acl_key_read, +}; + +static int +nfs4acl_get_key(const char *name, size_t namelen, const char *type, + char **allocp, char *bufp, int size) +{ + /* type (user/group), :, max name length, null */ + char desc[5 + 1 + NFS4ACL_NAMESZ + 1]; + const struct cred *saved_cred; + struct key *rkey; + const struct user_key_payload *ukp; + int ret; + + ret = snprintf(desc, sizeof (desc), "%s:%s", type, name); + if (ret == -1 || ret >= sizeof (desc)) + return (-ENOMEM); + + saved_cred = override_creds(nfs4acl_resolver_cred); + rkey = request_key(&key_type_nfs4acl_resolver, desc, ""); + revert_creds(saved_cred); + + if (IS_ERR(rkey)) { + ret = PTR_ERR(rkey); + goto get_key_out; + } else { +#if defined(HAVE_KERNEL_KEY_FLAG_ROOT_CAN_INVAL) + set_bit(KEY_FLAG_ROOT_CAN_INVAL, &rkey->flags); +#endif + } + + rcu_read_lock(); + rkey->perm |= KEY_USR_VIEW; + + ret = key_validate(rkey); + if (ret < 0) + goto get_key_out2; + + ukp = zpl_user_key_payload_rcu(rkey); + if (IS_ERR_OR_NULL(ukp)) { + ret = PTR_ERR(ukp); + goto get_key_out2; + } + + ret = ukp->datalen; + if (ret == 0) { + ret = -EINVAL; + goto get_key_out2; + } + + if (allocp) { + *allocp = kmem_alloc(ret + 1, KM_SLEEP); + memcpy(*allocp, ukp->data, ret); + memset(*allocp+ret, 0, 1); + } else if (bufp) { + if (size < ret + 1) { + ret = -ERANGE; + goto get_key_out2; + } + memcpy(bufp, ukp->data, ret); + memset(bufp+ret, 0, 1); + } + +get_key_out2: + rcu_read_unlock(); + key_put(rkey); + +get_key_out: + return (ret); +} + +static int +nfs4acl_map_id_to_name(__u32 id, char *type, char **mapped_name) +{ + char lookup_str[NFS4ACL_UINT_MAXLEN]; + int lookup_str_len; + int ret; + + lookup_str_len = snprintf(lookup_str, sizeof (lookup_str), "%u", id); + if (lookup_str_len == -1 || lookup_str_len >= sizeof (lookup_str)) + return (-ENOMEM); + + ret = nfs4acl_get_key(lookup_str, lookup_str_len, type, mapped_name, + NULL, 0); + /* if mapping fails, use a string representation of the numeric id */ + if (ret < 0) { + ret = lookup_str_len; + /* return just the size if no valid buffer was supplied */ + if (mapped_name) { + *mapped_name = kmem_alloc(ret+1, KM_SLEEP); + memcpy(*mapped_name, lookup_str, ret); + memset(*mapped_name+ret, 0, 1); + } + } + + return (ret); +} + +static int +__zpl_xattr_nfs4acl_list(struct inode *ip, char *list, size_t list_size, + const char *name, size_t name_len) +{ + char *xattr_name = NFS4ACL_XATTR; + size_t xattr_size = sizeof (NFS4ACL_XATTR); + + if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_NFSV4) + return (0); + + if (list && xattr_size <= list_size) + memcpy(list, xattr_name, xattr_size); + + return (xattr_size); +} +ZPL_XATTR_LIST_WRAPPER(zpl_xattr_nfs4acl_list); + +static int +xattr_nfs4acl_size_names(vsecattr_t *vsecp, char **mapped_names) +{ + int i, size; + + /* number of aces */ + size = sizeof (u32); + + for (i = 0; i < vsecp->vsa_aclcnt; i++) { + ace_t *acep = vsecp->vsa_aclentp + (i * sizeof (ace_t)); + char **mapped_name = mapped_names ? (mapped_names + i) : NULL; + int who_strlen; + + /* ace type */ + size += sizeof (u32); + + /* ace flags */ + size += sizeof (u32); + + /* ace access_mask */ + size += sizeof (u32); + + switch (acep->a_flags & ACE_TYPE_FLAGS) { + case ACE_OWNER: + who_strlen = strlen("OWNER@"); + break; + + case ACE_GROUP|ACE_IDENTIFIER_GROUP: + who_strlen = strlen("GROUP@"); + break; + + case ACE_IDENTIFIER_GROUP: + who_strlen = nfs4acl_map_id_to_name( + acep->a_who, "group", mapped_name); + if (who_strlen < 0) { + return (who_strlen); + } + break; + + case ACE_EVERYONE: + who_strlen = strlen("EVERYONE@"); + break; + + case 0: + who_strlen = nfs4acl_map_id_to_name( + acep->a_who, "user", mapped_name); + if (who_strlen < 0) { + return (who_strlen); + } + break; + + default: + return (-EINVAL); + break; + } + + /* length of who string */ + size += sizeof (u32); + + /* update length for xdr padding */ + who_strlen = ((who_strlen / NFS4ACL_XDR_MOD) * NFS4ACL_XDR_MOD * + sizeof (char)) + (who_strlen % NFS4ACL_XDR_MOD ? + NFS4ACL_XDR_MOD : 0); + + /* who string itself */ + size += who_strlen; + } + + return (size); +} + +static int +__zpl_xattr_nfs4acl_get(struct inode *ip, const char *name, + void *buffer, size_t size) +{ + cred_t *cr = CRED(); + vsecattr_t vsecp; + char **mapped_names = NULL; + char *bufp; + int i, ret; + + /* xattr_resolve_name will do this for us if this is defined */ +#ifndef HAVE_XATTR_HANDLER_NAME + if (strcmp(name, "") != 0) + return (-EINVAL); +#endif + if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_NFSV4) + return (-EOPNOTSUPP); + + vsecp.vsa_mask = VSA_ACE_ALLTYPES | VSA_ACECNT | VSA_ACE | + VSA_ACE_ACLFLAGS; + + crhold(cr); + ret = -zfs_getsecattr(ip, &vsecp, 0, cr); + crfree(cr); + + if (ret) + return (ret); + + if (vsecp.vsa_aclcnt == 0) { + ret = -ENODATA; + goto nfs4acl_get_out; + } + + /* + * If there's a valid buffer, store the mapped names so we don't need + * to lookup them up twice, once to calculate size and again to use + * them + */ + if (buffer) { + mapped_names = kmem_alloc(vsecp.vsa_aclcnt * sizeof (char *), + KM_SLEEP); + memset(mapped_names, 0, vsecp.vsa_aclcnt * sizeof (char *)); + } + + ret = xattr_nfs4acl_size_names(&vsecp, mapped_names); + if (ret < 0 || !buffer) { + goto nfs4acl_get_out2; + } + + if (ret > size) { + ret = -ERANGE; + goto nfs4acl_get_out2; + } + + bufp = buffer; + + /* number of aces */ + *((u32*)bufp) = htonl(vsecp.vsa_aclcnt); + bufp += sizeof (u32); + + for (i = 0; i < vsecp.vsa_aclcnt; i++) { + ace_t *acep = vsecp.vsa_aclentp + (i * sizeof (ace_t)); + char **mapped_name = (mapped_names + i); + char *who_str; + int who_strlen; + + /* ace type */ + *((u32*)bufp) = htonl(acep->a_type); + bufp += sizeof (u32); + + /* ace flags, reduced to NFSv4 supported set */ + *((u32*)bufp) = htonl(acep->a_flags & ACE_NFSV4_SUP_FLAGS); + bufp += sizeof (u32); + + /* ace access_mask */ + *((u32*)bufp) = htonl(acep->a_access_mask); + bufp += sizeof (u32); + + switch (acep->a_flags & ACE_TYPE_FLAGS) { + case ACE_OWNER: + who_str = "OWNER@"; + break; + + case ACE_GROUP|ACE_IDENTIFIER_GROUP: + who_str = "GROUP@"; + break; + + case ACE_IDENTIFIER_GROUP: + who_str = *mapped_name; + break; + + case ACE_EVERYONE: + who_str = "EVERYONE@"; + break; + + case 0: + who_str = *mapped_name; + break; + + default: + ret = -EINVAL; + goto nfs4acl_get_out2; + break; + } + + who_strlen = strlen(who_str); + + /* length of who string */ + *((u32*)bufp) = htonl(who_strlen); + bufp += sizeof (u32); + + /* who string */ + memcpy(bufp, who_str, who_strlen); + + /* update for xdr padding */ + who_strlen = ((who_strlen / NFS4ACL_XDR_MOD) * NFS4ACL_XDR_MOD * + sizeof (char)) + (who_strlen % NFS4ACL_XDR_MOD ? + NFS4ACL_XDR_MOD : 0); + + bufp += who_strlen; + } + +nfs4acl_get_out2: + if (mapped_names) { + for (i = 0; i < vsecp.vsa_aclcnt; i++) { + char **mapped_name = (mapped_names + i); + + if (*mapped_name) { + kmem_free(*mapped_name, strlen(*mapped_name)+1); + } + } + kmem_free(mapped_names, vsecp.vsa_aclcnt * sizeof (char *)); + } + + +nfs4acl_get_out: + kmem_free(vsecp.vsa_aclentp, vsecp.vsa_aclentsz); + + return (ret); +} +ZPL_XATTR_GET_WRAPPER(zpl_xattr_nfs4acl_get); + +static int +__zpl_xattr_nfs4acl_set(struct inode *ip, const char *name, + const void *value, size_t size, int flags) +{ + cred_t *cr = CRED(); + char id_str[NFS4ACL_UINT_MAXLEN]; + vsecattr_t vsecp; + char *bufp; + int used_size, i; + int ret = 0; + + /* xattr_resolve_name will do this for us if this is defined */ +#ifndef HAVE_XATTR_HANDLER_NAME + if (strcmp(name, "") != 0) + return (-EINVAL); +#endif + if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_NFSV4) + return (-EOPNOTSUPP); + + bufp = (char *)value; + + /* number of aces */ + used_size = sizeof (u32); + if (used_size > size) + return (-EINVAL); + + vsecp.vsa_aclcnt = ntohl(*((u32 *)bufp)); + bufp += sizeof (u32); + + vsecp.vsa_aclentsz = vsecp.vsa_aclcnt * sizeof (ace_t); + vsecp.vsa_aclentp = kmem_alloc(vsecp.vsa_aclentsz, KM_SLEEP); + + for (i = 0; i < vsecp.vsa_aclcnt; i++) { + ace_t *acep = vsecp.vsa_aclentp + (i * sizeof (ace_t)); + int who_strlen; + + /* ace type */ + used_size += sizeof (u32); + if (used_size > size) { + ret = -EINVAL; + goto nfs4acl_set_out; + } + acep->a_type = ntohl(*((u32 *)bufp)); + bufp += sizeof (u32); + + /* ace flags */ + used_size += sizeof (u32); + if (used_size > size) { + ret = -EINVAL; + goto nfs4acl_set_out; + } + acep->a_flags = ntohl(*((u32 *)bufp)); + bufp += sizeof (u32); + + if (acep->a_flags & ~ACE_NFSV4_SUP_FLAGS) { + ret = -EINVAL; + goto nfs4acl_set_out; + } + + /* ace access_mask */ + used_size += sizeof (u32); + if (used_size > size) { + ret = -EINVAL; + goto nfs4acl_set_out; + } + acep->a_access_mask = ntohl(*((u32 *)bufp)); + bufp += sizeof (u32); + + /* length of who string */ + used_size += sizeof (u32); + if (used_size > size) { + ret = -EINVAL; + goto nfs4acl_set_out; + } + who_strlen = ntohl(*((u32 *)bufp)); + bufp += sizeof (u32); + + used_size += who_strlen; + if (used_size > size) { + ret = -EINVAL; + goto nfs4acl_set_out; + } + + if (who_strlen == 6 && !memcmp(bufp, "OWNER@", 6)) { + acep->a_flags |= ACE_OWNER; + acep->a_who = -1; + } else if (who_strlen == 6 && !memcmp(bufp, "GROUP@", 6)) { + acep->a_flags |= ACE_GROUP; + acep->a_who = -1; + } else if (who_strlen == 9 && !memcmp(bufp, "EVERYONE@", 9)) { + acep->a_flags |= ACE_EVERYONE; + acep->a_who = -1; + } else if ((acep->a_flags & ACE_TYPE_FLAGS) == + ACE_IDENTIFIER_GROUP || !(acep->a_flags & ACE_TYPE_FLAGS)) { + long id_long; + + ret = nfs4acl_get_key(bufp, who_strlen, + acep->a_flags & ACE_TYPE_FLAGS ? "gid" : + "uid", NULL, id_str, sizeof (id_str)); + + /* + * If we couldn't map the name to a numeric id, + * see if the name is all digits, and if so, + * just use it as the id directly + */ + if (ret < 0) { + int c = 0; + while (c < who_strlen && c < + sizeof (id_str)) { + if (bufp[c] == '@') { + id_str[c] = '\0'; + ret = 0; + break; + } + if (!isdigit(bufp[c])) + break; + + id_str[c] = bufp[c]; + c++; + } + + if (ret < 0 || c == 0) { + ret = -EINVAL; + goto nfs4acl_set_out; + } + } + + ret = kstrtol(id_str, 10, &id_long); + if (ret < 0) + goto nfs4acl_set_out; + + acep->a_who = id_long; + } else { + ret = -EINVAL; + goto nfs4acl_set_out; + } + /* update for xdr padding */ + who_strlen = ((who_strlen / NFS4ACL_XDR_MOD) * NFS4ACL_XDR_MOD * + sizeof (char)) + (who_strlen % NFS4ACL_XDR_MOD ? + NFS4ACL_XDR_MOD : 0); + + bufp += who_strlen; + } + + crhold(cr); + ret = -zfs_setsecattr(ITOZ(ip), &vsecp, 0, cr); + crfree(cr); + +nfs4acl_set_out: + + kmem_free(vsecp.vsa_aclentp, vsecp.vsa_aclentsz); + + return (ret); +} +ZPL_XATTR_SET_WRAPPER(zpl_xattr_nfs4acl_set); + +/* + * ACL access xattr namespace handlers. + * + * Use .name instead of .prefix when available. xattr_resolve_name will match + * whole name and reject anything that has .name only as prefix. + */ +xattr_handler_t zpl_xattr_nfs4acl_handler = +{ +#ifdef HAVE_XATTR_HANDLER_NAME + .name = NFS4ACL_XATTR, +#else + .prefix = NFS4ACL_XATTR, +#endif + .list = zpl_xattr_nfs4acl_list, + .get = zpl_xattr_nfs4acl_get, + .set = zpl_xattr_nfs4acl_set, +}; + +#endif /* ZFS_NFS4_ACL */ + xattr_handler_t *zpl_xattr_handlers[] = { &zpl_xattr_security_handler, &zpl_xattr_trusted_handler, @@ -1345,6 +1997,9 @@ xattr_handler_t *zpl_xattr_handlers[] = { &zpl_xattr_acl_access_handler, &zpl_xattr_acl_default_handler, #endif /* CONFIG_FS_POSIX_ACL */ +#ifdef ZFS_NFS4_ACL + &zpl_xattr_nfs4acl_handler, +#endif /* ZFS_NFS4_ACL */ NULL }; @@ -1373,9 +2028,81 @@ zpl_xattr_handler(const char *name) return (&zpl_xattr_acl_default_handler); #endif /* CONFIG_FS_POSIX_ACL */ +#ifdef ZFS_NFS4_ACL + if (strncmp(name, NFS4ACL_XATTR, + sizeof (NFS4ACL_XATTR)) == 0) + return (&zpl_xattr_nfs4acl_handler); +#endif /* ZFS_NFS4_ACL */ + return (NULL); } +int +zpl_xattr_init(void) +{ +#ifdef ZFS_NFS4_ACL + int ret = 0; + struct cred *cred; + struct key *keyring; + + cred = prepare_kernel_cred(NULL); + + if (!cred) + return (-ENOMEM); + +#if defined(HAVE_KERNEL_KEYRING_ALLOC_WITH_8_ARGS) + keyring = keyring_alloc(".zfs_nfs4acl_resolver", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ, + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); +#else + keyring = keyring_alloc(".zfs_nfs4acl_resolver", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ, + KEY_ALLOC_NOT_IN_QUOTA, NULL); +#endif + + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto xattr_init_out; + } + + ret = register_key_type(&key_type_nfs4acl_resolver); + if (ret < 0) + goto xattr_init_out2; + + set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags); + cred->thread_keyring = keyring; + cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; + nfs4acl_resolver_cred = cred; + + return (0); + +xattr_init_out2: + key_put(keyring); + +xattr_init_out: + put_cred(cred); + + return (ret); + +#endif /* ZFS_NFS4_ACL */ + + return (0); +} + +void +zpl_xattr_fini(void) +{ +#ifdef ZFS_NFS4_ACL + key_revoke(nfs4acl_resolver_cred->thread_keyring); + unregister_key_type(&key_type_nfs4acl_resolver); + put_cred(nfs4acl_resolver_cred); +#endif /* ZFS_NFS4_ACL */ +} + #if !defined(HAVE_POSIX_ACL_RELEASE) || defined(HAVE_POSIX_ACL_RELEASE_GPL_ONLY) struct acl_rel_struct { struct acl_rel_struct *next; diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c index d62eec3f0236..2a191d477dd4 100644 --- a/module/zcommon/zfs_prop.c +++ b/module/zcommon/zfs_prop.c @@ -173,6 +173,7 @@ zfs_prop_init(void) { "disabled", ZFS_ACLTYPE_OFF }, { "noacl", ZFS_ACLTYPE_OFF }, { "posixacl", ZFS_ACLTYPE_POSIXACL }, + { "nfsv4", ZFS_ACLTYPE_NFSV4 }, { NULL } }; @@ -345,7 +346,7 @@ zfs_prop_init(void) #ifndef __FreeBSD__ zprop_register_index(ZFS_PROP_ACLTYPE, "acltype", ZFS_ACLTYPE_OFF, PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, - "noacl | posixacl", "ACLTYPE", acltype_table); + "noacl | nfsv4 | posixacl", "ACLTYPE", acltype_table); #endif zprop_register_index(ZFS_PROP_ACLINHERIT, "aclinherit", ZFS_ACL_RESTRICTED, PROP_INHERIT, ZFS_TYPE_FILESYSTEM, diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index 2104bef714c2..4b090b895418 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -222,7 +222,7 @@ kmutex_t zfsdev_state_lock; zfsdev_state_t *zfsdev_state_list; -extern void zfs_init(void); +extern int zfs_init(void); extern void zfs_fini(void); /* @@ -7516,7 +7516,8 @@ zfs_kmod_init(void) return (error); spa_init(SPA_MODE_READ | SPA_MODE_WRITE); - zfs_init(); + if ((error = zfs_init()) != 0) + goto out; zfs_ioctl_init();