diff --git a/include/os/freebsd/zfs/sys/zfs_vfsops_os.h b/include/os/freebsd/zfs/sys/zfs_vfsops_os.h index a263b48f7517..4dc05581fc8e 100644 --- a/include/os/freebsd/zfs/sys/zfs_vfsops_os.h +++ b/include/os/freebsd/zfs/sys/zfs_vfsops_os.h @@ -240,7 +240,9 @@ struct zfsvfs { RW_WRITE_HELD(&(zfsvfs)->z_teardown_inactive_lock) #endif -#define ZSB_XATTR 0x0001 /* Enable user xattrs */ +#define ZSB_XATTR 0x0001 /* Enable user xattrs */ +#define ZSB_XATTR_COMPAT 0x0002 /* Enable cross-platform user xattrs */ + /* * Normal filesystems (those not under .zfs/snapshot) have a total * file ID size limited to 12 bytes (including the length field) due to diff --git a/include/os/linux/zfs/sys/zfs_vfsops_os.h b/include/os/linux/zfs/sys/zfs_vfsops_os.h index 7b4a1aac9aad..cc8376c7c15f 100644 --- a/include/os/linux/zfs/sys/zfs_vfsops_os.h +++ b/include/os/linux/zfs/sys/zfs_vfsops_os.h @@ -171,7 +171,8 @@ struct zfsvfs { #define ZFS_TEARDOWN_HELD(zfsvfs) \ RRM_LOCK_HELD(&(zfsvfs)->z_teardown_lock) -#define ZSB_XATTR 0x0001 /* Enable user xattrs */ +#define ZSB_XATTR 0x0001 /* Enable user xattrs */ +#define ZSB_XATTR_COMPAT 0x0002 /* Enable cross-platform user xattrs */ /* * Allow a maximum number of links. While ZFS does not internally limit diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index 71d736d5cc97..27a87a9bbeb8 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -186,6 +186,7 @@ typedef enum { ZFS_PROP_IVSET_GUID, /* not exposed to the user */ ZFS_PROP_REDACTED, ZFS_PROP_REDACT_SNAPS, + ZFS_PROP_XATTR_COMPAT, ZFS_NUM_PROPS } zfs_prop_t; @@ -457,6 +458,11 @@ typedef enum zfs_key_location { #define DEFAULT_PBKDF2_ITERATIONS 350000 #define MIN_PBKDF2_ITERATIONS 100000 +typedef enum zfs_xattr_compat { + ZFS_XATTR_COMPAT_LINUX = 0, + ZFS_XATTR_COMPAT_ALL, +} zfs_xattr_compat_t; + /* * On-disk version number. */ @@ -1613,6 +1619,36 @@ typedef enum { #define ZFS_EV_HIST_DSID "history_dsid" #define ZFS_EV_RESILVER_TYPE "resilver_type" +/* + * xattr namespace prefixes. These are forbidden in xattr names. + * + * For cross-platform compatibility, xattrs in the user namespace should not be + * prefixed with the namespace name, but for backwards compatibility with older + * ZFS on Linux versions we do prefix the namespace. + */ +#define ZFS_XA_NS_FREEBSD_PREFIX "freebsd:" +#define ZFS_XA_NS_FREEBSD_PREFIX_LEN strlen("freebsd:") +#define ZFS_XA_NS_LINUX_SECURITY_PREFIX "security." +#define ZFS_XA_NS_LINUX_SECURITY_PREFIX_LEN strlen("security.") +#define ZFS_XA_NS_LINUX_SYSTEM_PREFIX "system." +#define ZFS_XA_NS_LINUX_SYSTEM_PREFIX_LEN strlen("system.") +#define ZFS_XA_NS_LINUX_TRUSTED_PREFIX "trusted." +#define ZFS_XA_NS_LINUX_TRUSTED_PREFIX_LEN strlen("trusted.") +#define ZFS_XA_NS_LINUX_USER_PREFIX "user." +#define ZFS_XA_NS_LINUX_USER_PREFIX_LEN strlen("user.") + +/* BEGIN CSTYLED */ +#define ZFS_XA_NS_PREFIX_MATCH(ns, name) \ + (strncmp(name, ZFS_XA_NS_##ns##_PREFIX, ZFS_XA_NS_##ns##_PREFIX_LEN) == 0) + +#define ZFS_XA_NS_PREFIX_FORBIDDEN(name) \ + (ZFS_XA_NS_PREFIX_MATCH(FREEBSD, name) || \ + ZFS_XA_NS_PREFIX_MATCH(LINUX_SECURITY, name) || \ + ZFS_XA_NS_PREFIX_MATCH(LINUX_SYSTEM, name) || \ + ZFS_XA_NS_PREFIX_MATCH(LINUX_TRUSTED, name) || \ + ZFS_XA_NS_PREFIX_MATCH(LINUX_USER, name)) +/* END CSTYLED */ + #ifdef __cplusplus } #endif diff --git a/include/zfeature_common.h b/include/zfeature_common.h index 76dd7ed57478..88a28f042d32 100644 --- a/include/zfeature_common.h +++ b/include/zfeature_common.h @@ -75,6 +75,7 @@ typedef enum spa_feature { SPA_FEATURE_DEVICE_REBUILD, SPA_FEATURE_ZSTD_COMPRESS, SPA_FEATURE_DRAID, + SPA_FEATURE_XATTR_COMPAT, SPA_FEATURES } spa_feature_t; diff --git a/lib/libnvpair/libnvpair.abi b/lib/libnvpair/libnvpair.abi index 19bda07d287c..777be6ceb34c 100644 --- a/lib/libnvpair/libnvpair.abi +++ b/lib/libnvpair/libnvpair.abi @@ -240,7 +240,7 @@ - + @@ -303,11 +303,11 @@ - - - - - + + + + + @@ -365,12 +365,12 @@ - - - - - - + + + + + + @@ -397,41 +397,41 @@ - - - + + + - + - + - + - + - + - + - + - + - + - + - + @@ -552,105 +552,105 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -660,461 +660,461 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + + + - - - + + + - - + + - + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -1123,37 +1123,37 @@ - - - + + + - - - - + + + + - - - + + + - - - - - + + + + + - - + + - - - + + + @@ -1745,10 +1745,10 @@ - - - - + + + + @@ -1797,7 +1797,7 @@ - + @@ -1847,9 +1847,9 @@ - - - + + + @@ -1872,11 +1872,11 @@ - + - + @@ -2603,7 +2603,7 @@ - + @@ -3175,7 +3175,7 @@ - + diff --git a/lib/libuutil/libuutil.abi b/lib/libuutil/libuutil.abi index 9c62e36ad2d1..6aa7332d6419 100644 --- a/lib/libuutil/libuutil.abi +++ b/lib/libuutil/libuutil.abi @@ -266,13 +266,13 @@ - + - - + + @@ -280,26 +280,26 @@ - - - + + + - - - + + + - - + + - - + + - - + + @@ -324,11 +324,11 @@ - - + + - + @@ -520,95 +520,95 @@ - - - + + + - - - + + + - - - - + + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - + + + + + - - + + - - + + - - - + + + - - - + + + - - - + + + - - + + - - + + - - + + - - + + - - - - + + + + @@ -623,28 +623,28 @@ - - - - + + + + - - - - + + + + - - + + - - - - - - + + + + + + @@ -719,7 +719,7 @@ - + @@ -745,25 +745,25 @@ - - + + - - + + - - - - + + + + - - - - + + + + @@ -779,21 +779,21 @@ - - - - + + + + - - + + - + @@ -900,103 +900,103 @@ - - - + + + - - - + + + - - + + - - + + - - + + - - - - + + + + - - - - + + + + - - - + + + - - - + + + - - - - - + + + + + - - + + - - + + - - - + + + - - - + + + - - - + + + - - - - - + + + + + - - - - + + + + - - + + - - - - + + + + @@ -1011,32 +1011,32 @@ - - - - + + + + - - - - + + + + - - + + - - - - - - + + + + + + - + @@ -1145,22 +1145,22 @@ - - - - - + + + + + - - + + - + - - + + @@ -1200,10 +1200,10 @@ - - - - + + + + @@ -1219,20 +1219,20 @@ - - - - - + + + + + - - + + - - - + + + @@ -1251,44 +1251,44 @@ - - - - + + + + - - + + - - - + + + - - + + - - - + + + - - + + - + - + - + @@ -1313,26 +1313,26 @@ - + - - - + + + - - - + + + - - - + + + @@ -1345,7 +1345,7 @@ - + @@ -1447,7 +1447,7 @@ - + @@ -1470,100 +1470,100 @@ - - + + - - + + - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + - - + + - - + + - - + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - - + + + + - - + + - - - - + + + + - + - - - + + + @@ -1579,34 +1579,34 @@ - - - + + + - - - - - + + + + + - - - - - + + + + + - - - + + + @@ -1628,7 +1628,7 @@ - + @@ -1636,8 +1636,8 @@ - - + + @@ -1659,7 +1659,7 @@ - + @@ -1808,13 +1808,13 @@ - + - + diff --git a/lib/libzfs/libzfs.abi b/lib/libzfs/libzfs.abi index 9f2e199cd0bb..8c3e0c5dcdad 100644 --- a/lib/libzfs/libzfs.abi +++ b/lib/libzfs/libzfs.abi @@ -414,47 +414,47 @@ - + - - + + - + - + - + - + - + - + - + - + - + - + - + @@ -557,9 +557,10 @@ - + + - + @@ -1055,26 +1056,26 @@ - - - - - + + + + + - - + + - - - + + + - - + + @@ -1085,28 +1086,28 @@ - - - + + + - - - - + + + + - - - + + + - - + + - - + + @@ -1256,45 +1257,45 @@ - + - - - - + + + + - - - - + + + + - - + + - - - + + + - - + + - - - + + + - - + + @@ -1386,52 +1387,52 @@ - - - - - + + + + + - - + + - - - - + + + + - - - + + + - - - - - + + + + + - - - - - - - - + + + + + + + + - - - - + + + + @@ -1461,7 +1462,7 @@ - + @@ -1573,64 +1574,64 @@ - - + + - - - - - - - - - - - - + + + + + + + + + + + + - - - + + + - - - - + + + + - - - + + + - - - - - + + + + + - - - - + + + + - - - - - - + + + + + + - + @@ -1646,49 +1647,49 @@ - + - - - - - + + + + + - - - - - - + + + + + + - - - - + + + + - - - - - + + + + + - - - - - + + + + + - - - + + + @@ -1718,23 +1719,23 @@ - - - - - + + + + + - - + + - - + + - - + + @@ -1749,132 +1750,132 @@ - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - + + - - - - + + + + - - - - + + + + - - - - + + + + - - - + + + - - - - - + + + + + - - - + + + - - - - + + + + - - - - + + + + - - - - + + + + - - + + - - + + - - + + - - - - - - + + + + + + - - - - + + + + - - - - - - + + + + + + - - - - + + + + - + @@ -1883,96 +1884,96 @@ - + - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - + + + + + + + + + - - + + - - - - - - + + + + + + - - - - + + + + - - - - + + + + - - - + + + - - - - + + + + - - - - - - - - - + + + + + + + + + - - - + + + - - - + + + - - - - - + + + + + @@ -1990,47 +1991,47 @@ - - - - + + + + - - - + + + - - + + - - + + - - + + - - - - + + + + - - - - + + + + - - + + - - + + @@ -2272,43 +2273,43 @@ - - - + + + - - - + + + - - - + + + - - + + - - + + - - - + + + - - - - - + + + + + - - + + @@ -2479,13 +2480,13 @@ - + - + @@ -2500,13 +2501,13 @@ - + - + @@ -2536,7 +2537,7 @@ - + @@ -2545,10 +2546,10 @@ - + - + @@ -2587,16 +2588,16 @@ - + - + - + @@ -2676,13 +2677,13 @@ - - - - - - - + + + + + + + @@ -2716,7 +2717,7 @@ - + @@ -2732,7 +2733,7 @@ - + @@ -2743,18 +2744,18 @@ - + - - - - - - + + + + + + - - + + @@ -2788,60 +2789,60 @@ - - - - - + + + + + - - - - - + + + + + - - - - + + + + - - - - - + + + + + - - - - - - + + + + + + - - - - + + + + - - - - - - - + + + + + + + - - - - + + + + @@ -2872,7 +2873,7 @@ - + @@ -2899,33 +2900,33 @@ - - + + - - - - + + + + - - - - + + + + - - - - - - - + + + + + + + @@ -2941,87 +2942,87 @@ - - - + + + - - + + - - - - + + + + - - - + + + - - + + - - + + - - + + - - - + + + - - - + + + - - - - + + + + - - + + - - + + - - + + - - + + - - + + - - - + + + - - - + + + - - - + + + @@ -3031,82 +3032,82 @@ - - - - + + + + - - + + - - + + - - - + + + - - - + + + - - - - - + + + + + - - + + - - - + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - - - - - + + + + + + - - - + + + - - - - + + + + @@ -3206,7 +3207,7 @@ - + @@ -3216,26 +3217,26 @@ - - - - - + + + + + - - - + + + - - - + + + - + @@ -3247,128 +3248,128 @@ - - - - - - + + + + + + - - - + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - + + + + - - - + + + - - - - - - + + + + + + - - - - - + + + + + - - - + + + - - - - - + + + + + - - - + + + - - - + + + - - - - - + + + + + - - - + + + - - - + + + - - + + - - - + + + - - - - + + + + - - - - + + + + - - + + - - - + + + @@ -3383,29 +3384,29 @@ - - - - - - + + + + + + - - - + + + - - - - - - - + + + + + + + - + @@ -3429,26 +3430,26 @@ - - - - - + + + + + - - - - + + + + - - - - + + + + - + @@ -3459,71 +3460,71 @@ - + - - - - - + + + + + - - - + + + - - - - + + + + - - - - - - + + + + + + - - - - - - + + + + + + - + - - + + - - - - - + + + + + - + - + @@ -3540,155 +3541,155 @@ - - - - - + + + + + - + - - - - - + + + + + - - - - + + + + - - - - - - + + + + + + - - + + - - - - - + + + + + - - - - - + + + + + - - - + + + - - - - + + + + - - - + + + - - + + - - + + - - - + + + - - - - - - + + + + + + - - + + - - + + - - + + - - + + - - - + + + - - - - + + + + - - - + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - + @@ -3726,37 +3727,37 @@ - - - - - - - - - - - - + + + + + + + + + + + + - - + + - - - + + + - - - - + + + + - - + + @@ -3831,7 +3832,7 @@ - + @@ -3858,7 +3859,7 @@ - + @@ -3876,22 +3877,22 @@ - + - + - + - + - + - + @@ -3903,7 +3904,7 @@ - + @@ -3919,7 +3920,7 @@ - + @@ -3964,13 +3965,13 @@ - - - - - - - + + + + + + + @@ -4031,51 +4032,51 @@ - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + @@ -4189,7 +4190,7 @@ - + @@ -4227,7 +4228,7 @@ - + @@ -4235,71 +4236,71 @@ - + - - - - + + + + - - - - + + + + - - - - + + + + - + - - + + - + - - - + + + - - - - - - - + + + + + + + - - - - + + + + - - + + - - - - - + + + + + @@ -4356,21 +4357,21 @@ - - - - - - - - - + + + + + + + + + - - - - + + + + @@ -4421,200 +4422,200 @@ - - - - - - - - + + + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - + + - - - + + + - - - - + + + + - - - - + + + + - - + + - - + + - - + + - - + + - + - - + + - - - + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - + + + + - - - + + + - - - + + + - - - - - + + + + + - - - + + + - - - + + + - - + + - - - - + + + + - - - - + + + + - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - + + + - - + + - - + + - - + + @@ -4692,7 +4693,7 @@ - + @@ -4746,7 +4747,7 @@ - + @@ -4795,7 +4796,7 @@ - + @@ -4839,7 +4840,7 @@ - + @@ -4851,7 +4852,7 @@ - + @@ -4948,7 +4949,7 @@ - + @@ -5060,7 +5061,7 @@ - + @@ -5069,32 +5070,32 @@ - + - + - + - + - + - + - + - + - + - + @@ -5134,33 +5135,34 @@ - + + - - + + - - + + - + - + - - + + - + @@ -5191,7 +5193,7 @@ - + @@ -5210,21 +5212,21 @@ - + - + - + - + - + - + @@ -5244,7 +5246,7 @@ - + @@ -5296,7 +5298,7 @@ - + @@ -5310,7 +5312,7 @@ - + @@ -5333,7 +5335,7 @@ - + @@ -5534,7 +5536,7 @@ - + @@ -5591,20 +5593,20 @@ - + - + - + - + - + @@ -5680,64 +5682,64 @@ - + - - - + + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -5747,52 +5749,52 @@ - - + + - - - - + + + + - - - + + + - - - - + + + + - - - - + + + + - - + + - - + + - - + + - - + + - - + + @@ -5886,7 +5888,7 @@ - + @@ -5956,7 +5958,7 @@ - + @@ -6069,115 +6071,115 @@ - - - - + + + + - - + + - - + + - - - + + + - - - + + + - - - - - + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - + + + - + - + @@ -6205,7 +6207,7 @@ - + @@ -6231,7 +6233,7 @@ - + diff --git a/lib/libzfs_core/libzfs_core.abi b/lib/libzfs_core/libzfs_core.abi index a16f5874bca4..7a9e064faae8 100644 --- a/lib/libzfs_core/libzfs_core.abi +++ b/lib/libzfs_core/libzfs_core.abi @@ -292,7 +292,7 @@ - + @@ -325,25 +325,25 @@ - - - + + + - - - + + + - + - + @@ -351,13 +351,13 @@ - - - - + + + + - + @@ -369,61 +369,61 @@ - - - - - - + + + + + + - - - - + + + + - - - - + + + + - + - - - - - - - - + + + + + + + + - + - - - - - - + + + + + + - - - + + + @@ -431,82 +431,82 @@ - - - - - - + + + + + + - - + + - - - - - + + + + + - - - - - - - + + + + + + + - - + + - - + + - - - - - - - + + + + + + + - - - + + + - - - + + + - - - - + + + + - - - + + + - - - + + + - - - - + + + + @@ -1030,68 +1030,68 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1102,122 +1102,122 @@ - - - - - + + + + + - - - - - - - - - - + + + + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - + + + + + + + - - - - - - + + + + + + - - - - - + + + + + - - - + + + - - - + + + - - - - + + + + - - - - + + + + - - + + - - - - + + + + - - - - + + + + - - - - + + + + - - + + - - - + + + - - - - + + + + - - - - + + + + @@ -1225,19 +1225,19 @@ - - - - - - + + + + + + - + - + @@ -1361,18 +1361,18 @@ - - - - - + + + + + - - - - + + + + @@ -1403,7 +1403,7 @@ - + @@ -1449,38 +1449,38 @@ - - - - - - - - - - - - + + + + + + + + + + + + - + - + - + - + - + - + @@ -1488,35 +1488,35 @@ - + - - - - - + + + + + - - - + + + - - - - + + + + - - - + + + - - - + + + @@ -1546,7 +1546,7 @@ - + @@ -1624,6 +1624,9 @@ + + + @@ -1700,29 +1703,29 @@ - - - - - + + + + + - - - - + + + + - - - - + + + + - - - - + + + + @@ -1733,15 +1736,15 @@ - - - - - + + + + + - - + + @@ -1751,49 +1754,49 @@ - + - - - - - - + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1801,12 +1804,12 @@ - + - - - + + + @@ -1828,7 +1831,7 @@ - + @@ -1887,7 +1890,7 @@ - + @@ -2106,7 +2109,7 @@ - + @@ -2349,7 +2352,7 @@ - + @@ -2447,58 +2450,58 @@ - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -2563,29 +2566,29 @@ - + - + - + - + - + - + - + - + - + @@ -2602,46 +2605,46 @@ - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - - - + + + + - - - - - + + + + + @@ -2748,7 +2751,7 @@ - + @@ -2760,13 +2763,13 @@ - + - - + + @@ -2779,7 +2782,7 @@ - + @@ -3038,7 +3041,7 @@ - + @@ -3061,99 +3064,99 @@ - - + + - - + + - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - + + - - + + - - + + - - + + - - - + + + - - - + + + - - - + + + - - - - + + + + - - - - + + + + - - + + - - - - + + + + - + - - - + + + @@ -3166,34 +3169,34 @@ - - - + + + - - - - - + + + + + - - - - - + + + + + - - - + + + @@ -3209,13 +3212,13 @@ - + - + @@ -3231,8 +3234,8 @@ - - + + @@ -3345,45 +3348,45 @@ - - - + + + - - + + - - + + - - + + - - - + + + - - + + - - + + - - - + + + - - - - + + + + diff --git a/lib/libzfsbootenv/libzfsbootenv.abi b/lib/libzfsbootenv/libzfsbootenv.abi index cf44cd5b49d7..4d0860e45966 100644 --- a/lib/libzfsbootenv/libzfsbootenv.abi +++ b/lib/libzfsbootenv/libzfsbootenv.abi @@ -15,16 +15,16 @@ - + - - - + + + @@ -34,10 +34,10 @@ - - - - + + + + @@ -102,38 +102,38 @@ - + - - - + + + - - - - - - + + + + + + - - + + - - - - + + + + - - - - + + + + @@ -224,7 +224,7 @@ - + @@ -335,10 +335,10 @@ - - - - + + + + diff --git a/man/man5/zpool-features.5 b/man/man5/zpool-features.5 index c97870dbbe82..f3ddb8ccacee 100644 --- a/man/man5/zpool-features.5 +++ b/man/man5/zpool-features.5 @@ -1021,6 +1021,27 @@ and may take a while to complete for filesystems containing a large number of files. .RE +.sp +.ne 2 +.na +\fBxattr_compat\fR +.ad +.RS 4n +.TS +l l . +GUID com.ixsystems:xattr_compat +READ\-ONLY COMPATIBLE yes +DEPENDENCIES extensible_dataset +.TE + +This feature enables the use of a cross-platform compatible encoding for xattrs +in the user namespace on Linux. + +This feature becomes \fBactive\fR on a filesystem when an xattr is written to +the user namespace on Linux after the feature has been enabled, and returns to +\fBenabled\fR after all filesystems with the feature active have been destroyed. +.RE + .sp .ne 2 .na diff --git a/man/man8/zfsprops.8 b/man/man8/zfsprops.8 index aa49839ee791..1c6d10b600ae 100644 --- a/man/man8/zfsprops.8 +++ b/man/man8/zfsprops.8 @@ -1815,6 +1815,38 @@ are equivalent to the and .Sy noxattr mount options. +.It Sy xattr_compat Ns = Ns Sy all Ns | Ns Sy linux +Controls the preferred encoding of xattrs in the user namespace. +When set to +.Sy all +(the default) with +.Sy feature@xattr_compat +enabled on the pool, xattrs written in the user namespace are stored in a +format compatible across all supported platforms, and xattrs in the user +namespace from all platforms are accessible. +There is no notion of xattr namespaces on illumos, so all xattrs from +illumos are presented in the user namespace on other platforms. +The xattrs not in the user namespace are considered platform-specific and are +not exposed on other platforms. +Existing xattrs in the +.Sy xattr_compat=linux +format are accessible and are replaced with the cross-platform compatible +format when written. +When +.Sy feature@xattr_compat +is disabled, xattrs behave as with +.Sy xattr_compat=linux . +When set to +.Sy linux , +xattrs written in the user namespace are stored in a format that is compatible +with ZFS on Linux prior to +.Sy feature@xattr_compat +but not compatible with ZFS on other platforms prior to this feature. +See +.Sy feature@xattr_compat +in +.Xr zpool-features 5 +for more information. .It Sy jailed Ns = Ns Sy off Ns | Ns Sy on Controls whether the dataset is managed from a jail. See the .Qq Sx Jails diff --git a/module/os/freebsd/zfs/zfs_vfsops.c b/module/os/freebsd/zfs/zfs_vfsops.c index 0b13780b8a5a..4756a69f97a1 100644 --- a/module/os/freebsd/zfs/zfs_vfsops.c +++ b/module/os/freebsd/zfs/zfs_vfsops.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -478,6 +479,31 @@ xattr_changed_cb(void *arg, uint64_t newval) } } +static void +xattr_compat_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + + /* + * Force the old cross-platform compatible behavior if + * feature@xattr_compat is disabled. This contrasts with + * Linux where the behavior prior to feature@xattr_compat + * was to use the incompatible Linux-only xattr format. + */ + if (!spa_feature_is_enabled(dmu_objset_spa(zfsvfs->z_os), + SPA_FEATURE_XATTR_COMPAT)) + newval = ZFS_XATTR_COMPAT_ALL; + + switch (newval) { + case ZFS_XATTR_COMPAT_ALL: + zfsvfs->z_flags |= ZSB_XATTR_COMPAT; + break; + case ZFS_XATTR_COMPAT_LINUX: + zfsvfs->z_flags &= ~ZSB_XATTR_COMPAT; + break; + } +} + static void blksz_changed_cb(void *arg, uint64_t newval) { @@ -721,6 +747,9 @@ zfs_register_callbacks(vfs_t *vfsp) zfs_prop_to_name(ZFS_PROP_ATIME), atime_changed_cb, zfsvfs); error = error ? error : dsl_prop_register(ds, zfs_prop_to_name(ZFS_PROP_XATTR), xattr_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_XATTR_COMPAT), xattr_compat_changed_cb, + zfsvfs); error = error ? error : dsl_prop_register(ds, zfs_prop_to_name(ZFS_PROP_RECORDSIZE), blksz_changed_cb, zfsvfs); error = error ? error : dsl_prop_register(ds, @@ -1237,6 +1266,10 @@ zfs_domount(vfs_t *vfsp, char *osname) "xattr", &pval, NULL))) goto out; xattr_changed_cb(zfsvfs, pval); + if ((error = dsl_prop_get_integer(osname, + "xattr_compat", &pval, NULL))) + goto out; + xattr_compat_changed_cb(zfsvfs, pval); if ((error = dsl_prop_get_integer(osname, "acltype", &pval, NULL))) goto out; diff --git a/module/os/freebsd/zfs/zfs_vnops_os.c b/module/os/freebsd/zfs/zfs_vnops_os.c index a18ac21dc79f..84353b0f83cf 100644 --- a/module/os/freebsd/zfs/zfs_vnops_os.c +++ b/module/os/freebsd/zfs/zfs_vnops_os.c @@ -5247,43 +5247,54 @@ zfs_freebsd_pathconf(struct vop_pathconf_args *ap) } } +static int +zfs_check_attrname(const char *name) +{ + /* We don't allow '/' character in attribute name. */ + if (strchr(name, '/') != NULL) + return (SET_ERROR(EINVAL)); + /* We don't allow attribute names that start with a namespace prefix. */ + if (ZFS_XA_NS_PREFIX_FORBIDDEN(name)) + return (SET_ERROR(EINVAL)); + return (0); +} + /* * FreeBSD's extended attributes namespace defines file name prefix for ZFS' * extended attribute name: * - * NAMESPACE PREFIX - * system freebsd:system: - * user (none, can be used to access ZFS fsattr(5) attributes - * created on Solaris) + * NAMESPACE XATTR_COMPAT PREFIX + * system * freebsd:system: + * user all (none, can be used to access ZFS + * fsattr(5) attributes created on Solaris) + * user linux user. */ static int zfs_create_attrname(int attrnamespace, const char *name, char *attrname, - size_t size) + size_t size, boolean_t xattr_compat) { const char *namespace, *prefix, *suffix; - /* We don't allow '/' character in attribute name. */ - if (strchr(name, '/') != NULL) - return (SET_ERROR(EINVAL)); - /* We don't allow attribute names that start with "freebsd:" string. */ - if (strncmp(name, "freebsd:", 8) == 0) - return (SET_ERROR(EINVAL)); - bzero(attrname, size); switch (attrnamespace) { case EXTATTR_NAMESPACE_USER: -#if 0 - prefix = "freebsd:"; - namespace = EXTATTR_NAMESPACE_USER_STRING; - suffix = ":"; -#else - /* - * This is the default namespace by which we can access all - * attributes created on Solaris. - */ - prefix = namespace = suffix = ""; -#endif + if (xattr_compat) { + /* + * This is the default namespace by which we can access + * all attributes created on Solaris. + */ + prefix = namespace = suffix = ""; + } else { + /* + * This is compatible with the user namespace encoding + * on Linux prior to feature@xattr_compat, but nothing + * else. + */ + prefix = ""; + namespace = "user"; + suffix = "."; + } break; case EXTATTR_NAMESPACE_SYSTEM: prefix = "freebsd:"; @@ -5351,13 +5362,12 @@ zfs_getextattr_dir(struct vop_getextattr_args *ap, const char *attrname) return (error); flags = FREAD; - NDINIT_ATVP(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, attrname, - xvp, td); + NDINIT_ATVP(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, attrname, xvp, td); error = vn_open_cred(&nd, &flags, 0, VN_OPEN_INVFS, ap->a_cred, NULL); vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); if (error != 0) - return (error); + return (SET_ERROR(error)); if (ap->a_size != NULL) { error = VOP_GETATTR(vp, &va, ap->a_cred); @@ -5365,6 +5375,8 @@ zfs_getextattr_dir(struct vop_getextattr_args *ap, const char *attrname) *ap->a_size = (size_t)va.va_size; } else if (ap->a_uio != NULL) error = VOP_READ(vp, ap->a_uio, IO_UNIT, ap->a_cred); + if (error != 0) + error = SET_ERROR(error); VOP_UNLOCK1(vp); vn_close(vp, flags, ap->a_cred, td); @@ -5388,14 +5400,37 @@ zfs_getextattr_sa(struct vop_getextattr_args *ap, const char *attrname) error = nvlist_lookup_byte_array(zp->z_xattr_cached, attrname, &nv_value, &nv_size); - if (error) - return (error); + if (error != 0) + return (SET_ERROR(error)); if (ap->a_size != NULL) *ap->a_size = nv_size; else if (ap->a_uio != NULL) error = uiomove(nv_value, nv_size, ap->a_uio); + if (error != 0) + return (SET_ERROR(error)); + + return (0); +} + +static int +zfs_getextattr_impl(struct vop_getextattr_args *ap, boolean_t compat) +{ + znode_t *zp = VTOZ(ap->a_vp); + zfsvfs_t *zfsvfs = ZTOZSB(zp); + char attrname[EXTATTR_MAXNAMELEN+1]; + int error; + + error = zfs_create_attrname(ap->a_attrnamespace, ap->a_name, attrname, + sizeof (attrname), compat); + if (error != 0) + return (error); + error = ENOENT; + if (zfsvfs->z_use_sa && zp->z_is_sa) + error = zfs_getextattr_sa(ap, attrname); + if (error == ENOENT) + error = zfs_getextattr_dir(ap, attrname); return (error); } @@ -5407,7 +5442,6 @@ zfs_getextattr(struct vop_getextattr_args *ap) { znode_t *zp = VTOZ(ap->a_vp); zfsvfs_t *zfsvfs = ZTOZSB(zp); - char attrname[EXTATTR_MAXNAMELEN+1]; int error; /* @@ -5419,10 +5453,9 @@ zfs_getextattr(struct vop_getextattr_args *ap) error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, ap->a_td, VREAD); if (error != 0) - return (error); + return (SET_ERROR(error)); - error = zfs_create_attrname(ap->a_attrnamespace, ap->a_name, attrname, - sizeof (attrname)); + error = zfs_check_attrname(ap->a_name); if (error != 0) return (error); @@ -5430,10 +5463,18 @@ zfs_getextattr(struct vop_getextattr_args *ap) ZFS_ENTER(zfsvfs); ZFS_VERIFY_ZP(zp) rw_enter(&zp->z_xattr_lock, RW_READER); - if (zfsvfs->z_use_sa && zp->z_is_sa) - error = zfs_getextattr_sa(ap, attrname); + + boolean_t compat = !!(zfsvfs->z_flags & ZSB_XATTR_COMPAT); + error = zfs_getextattr_impl(ap, compat); + if (error == ENOENT && ap->a_attrnamespace == EXTATTR_NAMESPACE_USER) + /* + * Fall back to the alternate namespace format if we failed to + * find a user xattr. + */ + error = zfs_getextattr_impl(ap, !compat); if (error == ENOENT) - error = zfs_getextattr_dir(ap, attrname); + error = SET_ERROR(ENOATTR); + rw_exit(&zp->z_xattr_lock); ZFS_EXIT(zfsvfs); if (error == ENOENT) @@ -5470,10 +5511,12 @@ zfs_deleteextattr_dir(struct vop_deleteextattr_args *ap, const char *attrname) vp = nd.ni_vp; if (error != 0) { NDFREE(&nd, NDF_ONLY_PNBUF); - return (error); + return (SET_ERROR(error)); } error = VOP_REMOVE(nd.ni_dvp, vp, &nd.ni_cnd); + if (error != 0) + error = SET_ERROR(error); NDFREE(&nd, NDF_ONLY_PNBUF); vput(nd.ni_dvp); @@ -5503,6 +5546,8 @@ zfs_deleteextattr_sa(struct vop_deleteextattr_args *ap, const char *attrname) error = nvlist_remove(nvl, attrname, DATA_TYPE_BYTE_ARRAY); if (error == 0) error = zfs_sa_set_xattr(zp); + else + error = SET_ERROR(error); if (error != 0) { zp->z_xattr_cached = NULL; nvlist_free(nvl); @@ -5510,30 +5555,16 @@ zfs_deleteextattr_sa(struct vop_deleteextattr_args *ap, const char *attrname) return (error); } -/* - * Vnode operation to remove a named attribute. - */ static int -zfs_deleteextattr(struct vop_deleteextattr_args *ap) +zfs_deleteextattr_impl(struct vop_deleteextattr_args *ap, boolean_t compat) { znode_t *zp = VTOZ(ap->a_vp); zfsvfs_t *zfsvfs = ZTOZSB(zp); char attrname[EXTATTR_MAXNAMELEN+1]; int error; - /* - * If the xattr property is off, refuse the request. - */ - if (!(zfsvfs->z_flags & ZSB_XATTR)) - return (SET_ERROR(EOPNOTSUPP)); - - error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, - ap->a_cred, ap->a_td, VWRITE); - if (error != 0) - return (error); - error = zfs_create_attrname(ap->a_attrnamespace, ap->a_name, attrname, - sizeof (attrname)); + sizeof (attrname), compat); if (error != 0) return (error); @@ -5558,6 +5589,45 @@ zfs_deleteextattr(struct vop_deleteextattr_args *ap) if (error == 0) error = zfs_deleteextattr_dir(ap, attrname); } + return (error); +} + +/* + * Vnode operation to remove a named attribute. + */ +static int +zfs_deleteextattr(struct vop_deleteextattr_args *ap) +{ + znode_t *zp = VTOZ(ap->a_vp); + zfsvfs_t *zfsvfs = ZTOZSB(zp); + int error; + + /* + * If the xattr property is off, refuse the request. + */ + if (!(zfsvfs->z_flags & ZSB_XATTR)) + return (SET_ERROR(EOPNOTSUPP)); + + error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, + ap->a_cred, ap->a_td, VWRITE); + if (error != 0) + return (SET_ERROR(error)); + + error = zfs_check_attrname(ap->a_name); + if (error != 0) + return (error); + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + rw_enter(&zp->z_xattr_lock, RW_WRITER); + + boolean_t compat = !!(zfsvfs->z_flags & ZSB_XATTR_COMPAT); + error = zfs_deleteextattr_impl(ap, compat); + if (error == ENOENT && ap->a_attrnamespace == EXTATTR_NAMESPACE_USER) + error = zfs_deleteextattr_impl(ap, !compat); + if (error == ENOENT) + error = SET_ERROR(ENOATTR); + rw_exit(&zp->z_xattr_lock); ZFS_EXIT(zfsvfs); if (error == ENOENT) @@ -5597,13 +5667,15 @@ zfs_setextattr_dir(struct vop_setextattr_args *ap, const char *attrname) vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); if (error != 0) - return (error); + return (SET_ERROR(error)); VATTR_NULL(&va); va.va_size = 0; error = VOP_SETATTR(vp, &va, ap->a_cred); if (error == 0) VOP_WRITE(vp, ap->a_uio, IO_UNIT, ap->a_cred); + else + error = SET_ERROR(error); VOP_UNLOCK1(vp); vn_close(vp, flags, ap->a_cred, td); @@ -5631,13 +5703,15 @@ zfs_setextattr_sa(struct vop_setextattr_args *ap, const char *attrname) return (SET_ERROR(EFBIG)); error = nvlist_size(nvl, &sa_size, NV_ENCODE_XDR); if (error != 0) - return (error); + return (SET_ERROR(error)); if (sa_size > DXATTR_MAX_SA_SIZE) return (SET_ERROR(EFBIG)); uchar_t *buf = kmem_alloc(entry_size, KM_SLEEP); error = uiomove(buf, entry_size, ap->a_uio); if (error == 0) error = nvlist_add_byte_array(nvl, attrname, buf, entry_size); + else + error = SET_ERROR(error); kmem_free(buf, entry_size); if (error == 0) error = zfs_sa_set_xattr(zp); @@ -5648,30 +5722,16 @@ zfs_setextattr_sa(struct vop_setextattr_args *ap, const char *attrname) return (error); } -/* - * Vnode operation to set a named attribute. - */ static int -zfs_setextattr(struct vop_setextattr_args *ap) +zfs_setextattr_impl(struct vop_setextattr_args *ap, boolean_t compat) { znode_t *zp = VTOZ(ap->a_vp); zfsvfs_t *zfsvfs = ZTOZSB(zp); char attrname[EXTATTR_MAXNAMELEN+1]; int error; - /* - * If the xattr property is off, refuse the request. - */ - if (!(zfsvfs->z_flags & ZSB_XATTR)) - return (SET_ERROR(EOPNOTSUPP)); - - error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, - ap->a_cred, ap->a_td, VWRITE); - if (error != 0) - return (error); - error = zfs_create_attrname(ap->a_attrnamespace, ap->a_name, attrname, - sizeof (attrname)); + sizeof (attrname), compat); if (error != 0) return (error); @@ -5681,9 +5741,6 @@ zfs_setextattr(struct vop_setextattr_args *ap) .a_td = ap->a_td, }; error = ENOENT; - ZFS_ENTER(zfsvfs); - ZFS_VERIFY_ZP(zp); - rw_enter(&zp->z_xattr_lock, RW_WRITER); if (zfsvfs->z_use_sa && zp->z_is_sa && zfsvfs->z_xattr_sa) { error = zfs_setextattr_sa(ap, attrname); if (error == 0) @@ -5702,6 +5759,51 @@ zfs_setextattr(struct vop_setextattr_args *ap) */ zfs_deleteextattr_sa(&vda, attrname); } + if (error == 0 && ap->a_attrnamespace == EXTATTR_NAMESPACE_USER) { + /* + * Also clear all versions of the alternate compat name. + */ + if (zfs_create_attrname(ap->a_attrnamespace, ap->a_name, + attrname, sizeof (attrname), !compat) == 0) { + zfs_deleteextattr_sa(&vda, attrname); + zfs_deleteextattr_dir(&vda, attrname); + } + } + return (error); +} + +/* + * Vnode operation to set a named attribute. + */ +static int +zfs_setextattr(struct vop_setextattr_args *ap) +{ + znode_t *zp = VTOZ(ap->a_vp); + zfsvfs_t *zfsvfs = ZTOZSB(zp); + int error; + + /* + * If the xattr property is off, refuse the request. + */ + if (!(zfsvfs->z_flags & ZSB_XATTR)) + return (SET_ERROR(EOPNOTSUPP)); + + error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, + ap->a_cred, ap->a_td, VWRITE); + if (error != 0) + return (SET_ERROR(error)); + + error = zfs_check_attrname(ap->a_name); + if (error != 0) + return (error); + + ZFS_ENTER(zfsvfs); + ZFS_VERIFY_ZP(zp); + rw_enter(&zp->z_xattr_lock, RW_WRITER); + + boolean_t compat = !!(zfsvfs->z_flags & ZSB_XATTR_COMPAT); + error = zfs_setextattr_impl(ap, compat); + rw_exit(&zp->z_xattr_lock); ZFS_EXIT(zfsvfs); return (error); @@ -5747,7 +5849,7 @@ zfs_listextattr_dir(struct vop_listextattr_args *ap, const char *attrprefix) vp = nd.ni_vp; NDFREE(&nd, NDF_ONLY_PNBUF); if (error != 0) - return (error); + return (SET_ERROR(error)); auio.uio_iov = &aiov; auio.uio_iovcnt = 1; @@ -5763,8 +5865,10 @@ zfs_listextattr_dir(struct vop_listextattr_args *ap, const char *attrprefix) aiov.iov_len = sizeof (dirbuf); auio.uio_resid = sizeof (dirbuf); error = VOP_READDIR(vp, &auio, ap->a_cred, &eof, NULL, NULL); - if (error != 0) + if (error != 0) { + error = SET_ERROR(error); break; + } int done = sizeof (dirbuf) - auio.uio_resid; for (int pos = 0; pos < done; ) { struct dirent *dp = (struct dirent *)(dirbuf + pos); @@ -5776,7 +5880,7 @@ zfs_listextattr_dir(struct vop_listextattr_args *ap, const char *attrprefix) if (dp->d_type != DT_REG && dp->d_type != DT_UNKNOWN) continue; else if (plen == 0 && - strncmp(dp->d_name, "freebsd:", 8) == 0) + ZFS_XA_NS_PREFIX_FORBIDDEN(dp->d_name)) continue; else if (strncmp(dp->d_name, attrprefix, plen) != 0) continue; @@ -5793,8 +5897,10 @@ zfs_listextattr_dir(struct vop_listextattr_args *ap, const char *attrprefix) char *namep = dp->d_name + plen; error = uiomove(namep, nlen, ap->a_uio); } - if (error != 0) + if (error != 0) { + error = SET_ERROR(error); break; + } } } } while (!eof && error == 0); @@ -5839,14 +5945,36 @@ zfs_listextattr_sa(struct vop_listextattr_args *ap, const char *attrprefix) char *namep = __DECONST(char *, name) + plen; error = uiomove(namep, nlen, ap->a_uio); } - if (error != 0) + if (error != 0) { + error = SET_ERROR(error); break; + } } } return (error); } +static int +zfs_listextattr_impl(struct vop_listextattr_args *ap, boolean_t compat) +{ + znode_t *zp = VTOZ(ap->a_vp); + zfsvfs_t *zfsvfs = ZTOZSB(zp); + char attrprefix[16]; + int error; + + error = zfs_create_attrname(ap->a_attrnamespace, "", attrprefix, + sizeof (attrprefix), compat); + if (error != 0) + return (error); + + if (zfsvfs->z_use_sa && zp->z_is_sa) + error = zfs_listextattr_sa(ap, attrprefix); + if (error == 0) + error = zfs_listextattr_dir(ap, attrprefix); + return (error); +} + /* * Vnode operation to retrieve extended attributes on a vnode. */ @@ -5855,7 +5983,6 @@ zfs_listextattr(struct vop_listextattr_args *ap) { znode_t *zp = VTOZ(ap->a_vp); zfsvfs_t *zfsvfs = ZTOZSB(zp); - char attrprefix[16]; int error; if (ap->a_size != NULL) @@ -5870,20 +5997,18 @@ zfs_listextattr(struct vop_listextattr_args *ap) error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred, ap->a_td, VREAD); if (error != 0) - return (error); - - error = zfs_create_attrname(ap->a_attrnamespace, "", attrprefix, - sizeof (attrprefix)); - if (error != 0) - return (error); + return (SET_ERROR(error)); ZFS_ENTER(zfsvfs); ZFS_VERIFY_ZP(zp); rw_enter(&zp->z_xattr_lock, RW_READER); - if (zfsvfs->z_use_sa && zp->z_is_sa) - error = zfs_listextattr_sa(ap, attrprefix); - if (error == 0) - error = zfs_listextattr_dir(ap, attrprefix); + + boolean_t compat = !!(zfsvfs->z_flags & ZSB_XATTR_COMPAT); + error = zfs_listextattr_impl(ap, compat); + if (error == 0 && ap->a_attrnamespace == EXTATTR_NAMESPACE_USER) + /* Also list user xattrs with the alternate format. */ + error = zfs_listextattr_impl(ap, !compat); + rw_exit(&zp->z_xattr_lock); ZFS_EXIT(zfsvfs); return (error); diff --git a/module/os/linux/zfs/zfs_vfsops.c b/module/os/linux/zfs/zfs_vfsops.c index 40da975fb2a8..1ad88c313fe5 100644 --- a/module/os/linux/zfs/zfs_vfsops.c +++ b/module/os/linux/zfs/zfs_vfsops.c @@ -58,6 +58,7 @@ #include #include #include +#include #include #include #include "zfs_comutil.h" @@ -352,6 +353,29 @@ xattr_changed_cb(void *arg, uint64_t newval) } } +static void +xattr_compat_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + + /* + * Force the old incompatible Linux behavior + * if feature@xattr_compat is disabled. + */ + if (!spa_feature_is_enabled(dmu_objset_spa(zfsvfs->z_os), + SPA_FEATURE_XATTR_COMPAT)) + newval = ZFS_XATTR_COMPAT_LINUX; + + switch (newval) { + case ZFS_XATTR_COMPAT_ALL: + zfsvfs->z_flags |= ZSB_XATTR_COMPAT; + break; + case ZFS_XATTR_COMPAT_LINUX: + zfsvfs->z_flags &= ~ZSB_XATTR_COMPAT; + break; + } +} + static void acltype_changed_cb(void *arg, uint64_t newval) { @@ -510,6 +534,9 @@ zfs_register_callbacks(vfs_t *vfsp) zfs_prop_to_name(ZFS_PROP_RELATIME), relatime_changed_cb, zfsvfs); error = error ? error : dsl_prop_register(ds, zfs_prop_to_name(ZFS_PROP_XATTR), xattr_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_XATTR_COMPAT), xattr_compat_changed_cb, + zfsvfs); error = error ? error : dsl_prop_register(ds, zfs_prop_to_name(ZFS_PROP_RECORDSIZE), blksz_changed_cb, zfsvfs); error = error ? error : dsl_prop_register(ds, @@ -1528,6 +1555,10 @@ zfs_domount(struct super_block *sb, zfs_mnt_t *zm, int silent) "xattr", &pval, NULL))) goto out; xattr_changed_cb(zfsvfs, pval); + if ((error = dsl_prop_get_integer(osname, + "xattr_compat", &pval, NULL))) + goto out; + xattr_compat_changed_cb(zfsvfs, pval); if ((error = dsl_prop_get_integer(osname, "acltype", &pval, NULL))) goto out; diff --git a/module/os/linux/zfs/zpl_xattr.c b/module/os/linux/zfs/zpl_xattr.c index 78baf1f956f1..9c506c6541d8 100644 --- a/module/os/linux/zfs/zpl_xattr.c +++ b/module/os/linux/zfs/zpl_xattr.c @@ -89,15 +89,6 @@ #define NFS41ACL_XATTR "system.nfs4_acl_xdr" -typedef struct xattr_filldir { - size_t size; - size_t offset; - char *buf; - struct dentry *dentry; -} xattr_filldir_t; - -static const struct xattr_handler *zpl_xattr_handler(const char *); - static const struct { int kmask; int zfsperm; @@ -117,31 +108,21 @@ static const struct { #define GENERIC_MASK(mask) ((mask & ~(MAY_READ | MAY_WRITE | MAY_EXEC)) == 0) -static int -zpl_xattr_permission(xattr_filldir_t *xf, const char *name, int name_len) -{ - static const struct xattr_handler *handler; - struct dentry *d = xf->dentry; - - handler = zpl_xattr_handler(name); - if (!handler) - return (0); +enum xattr_permission { + XAPERM_DENY, + XAPERM_ALLOW, + XAPERM_COMPAT, +}; - if (handler->list) { -#if defined(HAVE_XATTR_LIST_SIMPLE) - if (!handler->list(d)) - return (0); -#elif defined(HAVE_XATTR_LIST_DENTRY) - if (!handler->list(d, NULL, 0, name, name_len, 0)) - return (0); -#elif defined(HAVE_XATTR_LIST_HANDLER) - if (!handler->list(handler, d, NULL, 0, name, name_len)) - return (0); -#endif - } +typedef struct xattr_filldir { + size_t size; + size_t offset; + char *buf; + struct dentry *dentry; +} xattr_filldir_t; - return (1); -} +static enum xattr_permission zpl_xattr_permission(xattr_filldir_t *, + const char *, int); /* * Determine is a given xattr name should be visible and if so copy it @@ -150,10 +131,27 @@ zpl_xattr_permission(xattr_filldir_t *xf, const char *name, int name_len) static int zpl_xattr_filldir(xattr_filldir_t *xf, const char *name, int name_len) { + enum xattr_permission perm; + /* Check permissions using the per-namespace list xattr handler. */ - if (!zpl_xattr_permission(xf, name, name_len)) + perm = zpl_xattr_permission(xf, name, name_len); + if (perm == XAPERM_DENY) return (0); + /* Prefix the name with "user." if it does not have a namespace. */ + if (perm == XAPERM_COMPAT) { + if (xf->buf) { + if (xf->offset + XATTR_USER_PREFIX_LEN + 1 > xf->size) + return (-ERANGE); + + memcpy(xf->buf + xf->offset, XATTR_USER_PREFIX, + XATTR_USER_PREFIX_LEN); + xf->buf[xf->offset + XATTR_USER_PREFIX_LEN] = '\0'; + } + + xf->offset += XATTR_USER_PREFIX_LEN; + } + /* When xf->buf is NULL only calculate the required size. */ if (xf->buf) { if (xf->offset + name_len + 1 > xf->size) @@ -730,19 +728,31 @@ static int __zpl_xattr_user_get(struct inode *ip, const char *name, void *value, size_t size) { - char *xattr_name; + boolean_t compat = !!(ITOZSB(ip)->z_flags & ZSB_XATTR_COMPAT); int error; /* 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 (ZFS_XA_NS_PREFIX_FORBIDDEN(name)) + return (-EINVAL); if (!(ITOZSB(ip)->z_flags & ZSB_XATTR)) return (-EOPNOTSUPP); - xattr_name = kmem_asprintf("%s%s", XATTR_USER_PREFIX, name); - error = zpl_xattr_get(ip, xattr_name, value, size); - kmem_strfree(xattr_name); + /* + * Try to look up the name without the namespace prefix first for + * compatibility with xattrs from other platforms. If that fails, + * try again with the namespace prefix. + */ + if (compat) + error = zpl_xattr_get(ip, name, value, size); + if (!compat || error == -ENODATA) { + char *xattr_name; + xattr_name = kmem_asprintf("%s%s", XATTR_USER_PREFIX, name); + error = zpl_xattr_get(ip, xattr_name, value, size); + kmem_strfree(xattr_name); + } return (error); } @@ -753,19 +763,43 @@ __zpl_xattr_user_set(struct inode *ip, const char *name, const void *value, size_t size, int flags) { char *xattr_name; + boolean_t compat = !!(ITOZSB(ip)->z_flags & ZSB_XATTR_COMPAT); int error; /* 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 (ZFS_XA_NS_PREFIX_FORBIDDEN(name)) + return (-EINVAL); if (!(ITOZSB(ip)->z_flags & ZSB_XATTR)) return (-EOPNOTSUPP); + /* + * Remove any namespaced version of the xattr so we only set the + * version compatible with other platforms. + * + * The following flags must be handled correctly: + * + * XATTR_CREATE: fail if xattr already exists + * XATTR_REPLACE: fail if xattr does not exist + */ xattr_name = kmem_asprintf("%s%s", XATTR_USER_PREFIX, name); - error = zpl_xattr_set(ip, xattr_name, value, size, flags); + if (compat) + error = zpl_xattr_set(ip, xattr_name, NULL, 0, flags); + else + error = zpl_xattr_set(ip, xattr_name, value, size, flags); kmem_strfree(xattr_name); + if (!compat || error == -EEXIST) + return (error); + if (error == 0 && (flags & XATTR_REPLACE)) + flags &= ~XATTR_REPLACE; + error = zpl_xattr_set(ip, name, value, size, flags); + + dsl_dataset_t *ds = dmu_objset_ds(ITOZSB(ip)->z_os); + ds->ds_feature_activation[SPA_FEATURE_XATTR_COMPAT] = (void *)B_TRUE; + return (error); } ZPL_XATTR_SET_WRAPPER(zpl_xattr_user_set); @@ -1867,6 +1901,45 @@ zpl_xattr_handler(const char *name) return (NULL); } +static enum xattr_permission +zpl_xattr_permission(xattr_filldir_t *xf, const char *name, int name_len) +{ + const struct xattr_handler *handler; + struct dentry *d = xf->dentry; + boolean_t compat = !!(ITOZSB(d->d_inode)->z_flags & ZSB_XATTR_COMPAT); + enum xattr_permission perm = XAPERM_ALLOW; + + handler = zpl_xattr_handler(name); + if (handler == NULL) { + if (!compat) + return (XAPERM_DENY); + /* Do not expose FreeBSD system namespace xattrs. */ + if (ZFS_XA_NS_PREFIX_MATCH(FREEBSD, name)) + return (XAPERM_DENY); + /* + * Anything that doesn't match a known namespace gets put in the + * user namespace for compatibility with other platforms. + */ + perm = XAPERM_COMPAT; + handler = &zpl_xattr_user_handler; + } + + if (handler->list) { +#if defined(HAVE_XATTR_LIST_SIMPLE) + if (!handler->list(d)) + return (XAPERM_DENY); +#elif defined(HAVE_XATTR_LIST_DENTRY) + if (!handler->list(d, NULL, 0, name, name_len, 0)) + return (XAPERM_DENY); +#elif defined(HAVE_XATTR_LIST_HANDLER) + if (!handler->list(handler, d, NULL, 0, name, name_len)) + return (XAPERM_DENY); +#endif + } + + return (perm); +} + #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/zfeature_common.c b/module/zcommon/zfeature_common.c index fc0e09605eef..48c90d384ff2 100644 --- a/module/zcommon/zfeature_common.c +++ b/module/zcommon/zfeature_common.c @@ -598,6 +598,19 @@ zpool_feature_init(void) zfeature_register(SPA_FEATURE_DRAID, "org.openzfs:draid", "draid", "Support for distributed spare RAID", ZFEATURE_FLAG_MOS, ZFEATURE_TYPE_BOOLEAN, NULL); + + + { + static const spa_feature_t xattr_compat_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_XATTR_COMPAT, + "com.ixsystems:xattr_compat", "xattr_compat", + "Support for cross-platform compatible xattr namespace encoding", + ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN, + xattr_compat_deps); + } } #if defined(_KERNEL) diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c index 402d749c1aeb..b4eb16158fe8 100644 --- a/module/zcommon/zfs_prop.c +++ b/module/zcommon/zfs_prop.c @@ -357,6 +357,12 @@ zfs_prop_init(void) { NULL } }; + static zprop_index_t xattr_compat_table[] = { + { "linux", ZFS_XATTR_COMPAT_LINUX }, + { "all", ZFS_XATTR_COMPAT_ALL }, + { NULL } + }; + static zprop_index_t dnsize_table[] = { { "legacy", ZFS_DNSIZE_LEGACY }, { "auto", ZFS_DNSIZE_AUTO }, @@ -459,6 +465,10 @@ zfs_prop_init(void) zprop_register_index(ZFS_PROP_XATTR, "xattr", ZFS_XATTR_DIR, PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, "on | off | dir | sa", "XATTR", xattr_table); + zprop_register_index(ZFS_PROP_XATTR_COMPAT, "xattr_compat", + ZFS_XATTR_COMPAT_ALL, PROP_INHERIT, + ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT, + "all | linux", "XATTR_COMPAT", xattr_compat_table); zprop_register_index(ZFS_PROP_DNODESIZE, "dnodesize", ZFS_DNSIZE_LEGACY, PROP_INHERIT, ZFS_TYPE_FILESYSTEM, "legacy | auto | 1k | 2k | 4k | 8k | 16k", "DNSIZE", dnsize_table); diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg b/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg index 6075e1f1abbd..20d29c9169e6 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg +++ b/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg @@ -82,6 +82,7 @@ typeset -a properties=( "feature@log_spacemap" "feature@device_rebuild" "feature@draid" + "feature@xattr_compat" ) if is_linux || is_freebsd; then