summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/kernel-xattr-handler.m4228
-rw-r--r--include/linux/xattr_compat.h120
-rw-r--r--module/zfs/zpl_xattr.c452
3 files changed, 518 insertions, 282 deletions
diff --git a/config/kernel-xattr-handler.m4 b/config/kernel-xattr-handler.m4
index d79a6e47b..e1881f68b 100644
--- a/config/kernel-xattr-handler.m4
+++ b/config/kernel-xattr-handler.m4
@@ -3,8 +3,8 @@ dnl # 2.6.35 API change,
dnl # The 'struct xattr_handler' was constified in the generic
dnl # super_block structure.
dnl #
-AC_DEFUN([ZFS_AC_KERNEL_CONST_XATTR_HANDLER],
- [AC_MSG_CHECKING([whether super_block uses const struct xattr_hander])
+AC_DEFUN([ZFS_AC_KERNEL_CONST_XATTR_HANDLER], [
+ AC_MSG_CHECKING([whether super_block uses const struct xattr_handler])
ZFS_LINUX_TRY_COMPILE([
#include <linux/fs.h>
#include <linux/xattr.h>
@@ -26,29 +26,29 @@ AC_DEFUN([ZFS_AC_KERNEL_CONST_XATTR_HANDLER],
],[
AC_MSG_RESULT([yes])
AC_DEFINE(HAVE_CONST_XATTR_HANDLER, 1,
- [super_block uses const struct xattr_hander])
+ [super_block uses const struct xattr_handler])
],[
AC_MSG_RESULT([no])
])
])
dnl #
-dnl # 2.6.33 API change,
-dnl # The xattr_hander->get() callback was changed to take a dentry
-dnl # instead of an inode, and a handler_flags argument was added.
-dnl #
-dnl # 4.4 API change,
-dnl # The xattr_hander->get() callback was changed to take a xattr_handler,
-dnl # and handler_flags argument was removed and should be accessed by
-dnl # handler->flags.
+dnl # Supported xattr handler get() interfaces checked newest to oldest.
dnl #
AC_DEFUN([ZFS_AC_KERNEL_XATTR_HANDLER_GET], [
- AC_MSG_CHECKING([whether xattr_handler->get() wants dentry])
+ dnl #
+ dnl # 4.4 API change,
+ dnl # The xattr_handler->get() callback was changed to take a
+ dnl # attr_handler, and handler_flags argument was removed and
+ dnl # should be accessed by handler->flags.
+ dnl #
+ AC_MSG_CHECKING([whether xattr_handler->get() wants xattr_handler])
ZFS_LINUX_TRY_COMPILE([
#include <linux/xattr.h>
- int get(struct dentry *dentry, const char *name,
- void *buffer, size_t size, int handler_flags) { return 0; }
+ int get(const struct xattr_handler *handler,
+ struct dentry *dentry, const char *name,
+ void *buffer, size_t size) { return 0; }
static const struct xattr_handler
xops __attribute__ ((unused)) = {
.get = get,
@@ -56,16 +56,23 @@ AC_DEFUN([ZFS_AC_KERNEL_XATTR_HANDLER_GET], [
],[
],[
AC_MSG_RESULT(yes)
- AC_DEFINE(HAVE_DENTRY_XATTR_GET, 1,
- [xattr_handler->get() wants dentry])
+ AC_DEFINE(HAVE_XATTR_GET_HANDLER, 1,
+ [xattr_handler->get() wants xattr_handler])
],[
+ dnl #
+ dnl # 2.6.33 API change,
+ dnl # The xattr_handler->get() callback was changed to take
+ dnl # a dentry instead of an inode, and a handler_flags
+ dnl # argument was added.
+ dnl #
AC_MSG_RESULT(no)
- AC_MSG_CHECKING([whether xattr_handler->get() wants xattr_handler])
+ AC_MSG_CHECKING([whether xattr_handler->get() wants dentry])
ZFS_LINUX_TRY_COMPILE([
#include <linux/xattr.h>
- int get(const struct xattr_handler *handler, struct dentry *dentry,
- const char *name, void *buffer, size_t size) { return 0; }
+ int get(struct dentry *dentry, const char *name,
+ void *buffer, size_t size, int handler_flags)
+ { return 0; }
static const struct xattr_handler
xops __attribute__ ((unused)) = {
.get = get,
@@ -73,32 +80,54 @@ AC_DEFUN([ZFS_AC_KERNEL_XATTR_HANDLER_GET], [
],[
],[
AC_MSG_RESULT(yes)
- AC_DEFINE(HAVE_HANDLER_XATTR_GET, 1,
- [xattr_handler->get() wants xattr_handler])
+ AC_DEFINE(HAVE_XATTR_GET_DENTRY, 1,
+ [xattr_handler->get() wants dentry])
],[
+ dnl #
+ dnl # 2.6.32 API
+ dnl #
AC_MSG_RESULT(no)
+ AC_MSG_CHECKING(
+ [whether xattr_handler->get() wants inode])
+ ZFS_LINUX_TRY_COMPILE([
+ #include <linux/xattr.h>
+
+ int get(struct inode *ip, const char *name,
+ void *buffer, size_t size) { return 0; }
+ static const struct xattr_handler
+ xops __attribute__ ((unused)) = {
+ .get = get,
+ };
+ ],[
+ ],[
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_XATTR_GET_INODE, 1,
+ [xattr_handler->get() wants inode])
+ ],[
+ AC_MSG_ERROR([no; please file a bug report])
+ ])
])
])
])
dnl #
-dnl # 2.6.33 API change,
-dnl # The xattr_hander->set() callback was changed to take a dentry
-dnl # instead of an inode, and a handler_flags argument was added.
-dnl #
-dnl # 4.4 API change,
-dnl # The xattr_hander->set() callback was changed to take a xattr_handler,
-dnl # and handler_flags argument was removed and should be accessed by
-dnl # handler->flags.
+dnl # Supported xattr handler set() interfaces checked newest to oldest.
dnl #
AC_DEFUN([ZFS_AC_KERNEL_XATTR_HANDLER_SET], [
- AC_MSG_CHECKING([whether xattr_handler->set() wants dentry])
+ dnl #
+ dnl # 4.4 API change,
+ dnl # The xattr_handler->set() callback was changed to take a
+ dnl # xattr_handler, and handler_flags argument was removed and
+ dnl # should be accessed by handler->flags.
+ dnl #
+ AC_MSG_CHECKING([whether xattr_handler->set() wants xattr_handler])
ZFS_LINUX_TRY_COMPILE([
#include <linux/xattr.h>
- int set(struct dentry *dentry, const char *name,
- const void *buffer, size_t size, int flags,
- int handler_flags) { return 0; }
+ int set(const struct xattr_handler *handler,
+ struct dentry *dentry, const char *name,
+ const void *buffer, size_t size, int flags)
+ { return 0; }
static const struct xattr_handler
xops __attribute__ ((unused)) = {
.set = set,
@@ -106,16 +135,23 @@ AC_DEFUN([ZFS_AC_KERNEL_XATTR_HANDLER_SET], [
],[
],[
AC_MSG_RESULT(yes)
- AC_DEFINE(HAVE_DENTRY_XATTR_SET, 1,
- [xattr_handler->set() wants dentry])
+ AC_DEFINE(HAVE_XATTR_SET_HANDLER, 1,
+ [xattr_handler->set() wants xattr_handler])
],[
+ dnl #
+ dnl # 2.6.33 API change,
+ dnl # The xattr_handler->set() callback was changed to take a
+ dnl # dentry instead of an inode, and a handler_flags
+ dnl # argument was added.
+ dnl #
AC_MSG_RESULT(no)
- AC_MSG_CHECKING([whether xattr_handler->set() wants xattr_handler])
+ AC_MSG_CHECKING([whether xattr_handler->set() wants dentry])
ZFS_LINUX_TRY_COMPILE([
#include <linux/xattr.h>
- int set(const struct xattr_handler *handler, struct dentry *dentry,
- const char *name, const void *buffer, size_t size, int flags) { return 0; }
+ int set(struct dentry *dentry, const char *name,
+ const void *buffer, size_t size, int flags,
+ int handler_flags) { return 0; }
static const struct xattr_handler
xops __attribute__ ((unused)) = {
.set = set,
@@ -123,32 +159,49 @@ AC_DEFUN([ZFS_AC_KERNEL_XATTR_HANDLER_SET], [
],[
],[
AC_MSG_RESULT(yes)
- AC_DEFINE(HAVE_HANDLER_XATTR_SET, 1,
- [xattr_handler->set() wants xattr_handler])
+ AC_DEFINE(HAVE_XATTR_SET_DENTRY, 1,
+ [xattr_handler->set() wants dentry])
],[
+ dnl #
+ dnl # 2.6.32 API
+ dnl #
AC_MSG_RESULT(no)
+ AC_MSG_CHECKING(
+ [whether xattr_handler->set() wants inode])
+ ZFS_LINUX_TRY_COMPILE([
+ #include <linux/xattr.h>
+
+ int set(struct inode *ip, const char *name,
+ const void *buffer, size_t size, int flags)
+ { return 0; }
+ static const struct xattr_handler
+ xops __attribute__ ((unused)) = {
+ .set = set,
+ };
+ ],[
+ ],[
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_XATTR_SET_INODE, 1,
+ [xattr_handler->set() wants inode])
+ ],[
+ AC_MSG_ERROR([no; please file a bug report])
+ ])
])
])
])
dnl #
-dnl # 2.6.33 API change,
-dnl # The xattr_hander->list() callback was changed to take a dentry
-dnl # instead of an inode, and a handler_flags argument was added.
-dnl #
-dnl # 4.4 API change,
-dnl # The xattr_hander->list() callback was changed to take a xattr_handler,
-dnl # and handler_flags argument was removed and should be accessed by
-dnl # handler->flags.
+dnl # Supported xattr handler list() interfaces checked newest to oldest.
dnl #
AC_DEFUN([ZFS_AC_KERNEL_XATTR_HANDLER_LIST], [
- AC_MSG_CHECKING([whether xattr_handler->list() wants dentry])
+ dnl # 4.5 API change,
+ dnl # The xattr_handler->list() callback was changed to take only a
+ dnl # dentry and it only needs to return if it's accessable.
+ AC_MSG_CHECKING([whether xattr_handler->list() wants simple])
ZFS_LINUX_TRY_COMPILE([
#include <linux/xattr.h>
- size_t list(struct dentry *dentry, char *list, size_t list_size,
- const char *name, size_t name_len, int handler_flags)
- { return 0; }
+ bool list(struct dentry *dentry) { return 0; }
static const struct xattr_handler
xops __attribute__ ((unused)) = {
.list = list,
@@ -156,16 +209,24 @@ AC_DEFUN([ZFS_AC_KERNEL_XATTR_HANDLER_LIST], [
],[
],[
AC_MSG_RESULT(yes)
- AC_DEFINE(HAVE_DENTRY_XATTR_LIST, 1,
- [xattr_handler->list() wants dentry])
+ AC_DEFINE(HAVE_XATTR_LIST_SIMPLE, 1,
+ [xattr_handler->list() wants simple])
],[
+ dnl #
+ dnl # 4.4 API change,
+ dnl # The xattr_handler->list() callback was changed to take a
+ dnl # xattr_handler, and handler_flags argument was removed
+ dnl # and should be accessed by handler->flags.
+ dnl #
AC_MSG_RESULT(no)
- AC_MSG_CHECKING([whether xattr_handler->list() wants xattr_handler])
+ AC_MSG_CHECKING(
+ [whether xattr_handler->list() wants xattr_handler])
ZFS_LINUX_TRY_COMPILE([
#include <linux/xattr.h>
- size_t list(const struct xattr_handler *handler, struct dentry *dentry,
- char *list, size_t list_size, const char *name, size_t name_len) { return 0; }
+ size_t list(const struct xattr_handler *handler,
+ struct dentry *dentry, char *list, size_t list_size,
+ const char *name, size_t name_len) { return 0; }
static const struct xattr_handler
xops __attribute__ ((unused)) = {
.list = list,
@@ -173,10 +234,61 @@ AC_DEFUN([ZFS_AC_KERNEL_XATTR_HANDLER_LIST], [
],[
],[
AC_MSG_RESULT(yes)
- AC_DEFINE(HAVE_HANDLER_XATTR_LIST, 1,
+ AC_DEFINE(HAVE_XATTR_LIST_HANDLER, 1,
[xattr_handler->list() wants xattr_handler])
],[
+ dnl #
+ dnl # 2.6.33 API change,
+ dnl # The xattr_handler->list() callback was changed
+ dnl # to take a dentry instead of an inode, and a
+ dnl # handler_flags argument was added.
+ dnl #
AC_MSG_RESULT(no)
+ AC_MSG_CHECKING(
+ [whether xattr_handler->list() wants dentry])
+ ZFS_LINUX_TRY_COMPILE([
+ #include <linux/xattr.h>
+
+ size_t list(struct dentry *dentry,
+ char *list, size_t list_size,
+ const char *name, size_t name_len,
+ int handler_flags) { return 0; }
+ static const struct xattr_handler
+ xops __attribute__ ((unused)) = {
+ .list = list,
+ };
+ ],[
+ ],[
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_XATTR_LIST_DENTRY, 1,
+ [xattr_handler->list() wants dentry])
+ ],[
+ dnl #
+ dnl # 2.6.32 API
+ dnl #
+ AC_MSG_RESULT(no)
+ AC_MSG_CHECKING(
+ [whether xattr_handler->list() wants inode])
+ ZFS_LINUX_TRY_COMPILE([
+ #include <linux/xattr.h>
+
+ size_t list(struct inode *ip, char *lst,
+ size_t list_size, const char *name,
+ size_t name_len) { return 0; }
+ static const struct xattr_handler
+ xops __attribute__ ((unused)) = {
+ .list = list,
+ };
+ ],[
+ ],[
+ AC_MSG_RESULT(yes)
+ AC_DEFINE(HAVE_XATTR_LIST_INODE, 1,
+ [xattr_handler->list() wants inode])
+ ],[
+ AC_MSG_ERROR(
+ [no; please file a bug report])
+ ])
+ ])
])
])
])
diff --git a/include/linux/xattr_compat.h b/include/linux/xattr_compat.h
index 28eff95fa..eee6c1f94 100644
--- a/include/linux/xattr_compat.h
+++ b/include/linux/xattr_compat.h
@@ -42,25 +42,72 @@ typedef struct xattr_handler xattr_handler_t;
#endif
/*
+ * 3.7 API change,
+ * Preferred XATTR_NAME_* definitions introduced, these are mapped to
+ * the previous definitions for older kernels.
+ */
+#ifndef XATTR_NAME_POSIX_ACL_DEFAULT
+#define XATTR_NAME_POSIX_ACL_DEFAULT POSIX_ACL_XATTR_DEFAULT
+#endif
+
+#ifndef XATTR_NAME_POSIX_ACL_ACCESS
+#define XATTR_NAME_POSIX_ACL_ACCESS POSIX_ACL_XATTR_ACCESS
+#endif
+
+/*
+ * 4.5 API change,
+ */
+#if defined(HAVE_XATTR_LIST_SIMPLE)
+#define ZPL_XATTR_LIST_WRAPPER(fn) \
+static bool \
+fn(struct dentry *dentry) \
+{ \
+ return (!!__ ## fn(dentry->d_inode, NULL, 0, NULL, 0)); \
+}
+/*
+ * 4.4 API change,
+ */
+#elif defined(HAVE_XATTR_LIST_DENTRY)
+#define ZPL_XATTR_LIST_WRAPPER(fn) \
+static size_t \
+fn(struct dentry *dentry, char *list, size_t list_size, \
+ const char *name, size_t name_len, int type) \
+{ \
+ return (__ ## fn(dentry->d_inode, \
+ list, list_size, name, name_len)); \
+}
+/*
* 2.6.33 API change,
- * The xattr_hander->get() callback was changed to take a dentry
- * instead of an inode, and a handler_flags argument was added.
*/
-#ifdef HAVE_DENTRY_XATTR_GET
-#define ZPL_XATTR_GET_WRAPPER(fn) \
-static int \
-fn(struct dentry *dentry, const char *name, void *buffer, size_t size, \
- int unused_handler_flags) \
+#elif defined(HAVE_XATTR_LIST_HANDLER)
+#define ZPL_XATTR_LIST_WRAPPER(fn) \
+static size_t \
+fn(const struct xattr_handler *handler, struct dentry *dentry, \
+ char *list, size_t list_size, const char *name, size_t name_len) \
{ \
- return (__ ## fn(dentry->d_inode, name, buffer, size)); \
+ return (__ ## fn(dentry->d_inode, \
+ list, list_size, name, name_len)); \
+}
+/*
+ * 2.6.32 API
+ */
+#elif defined(HAVE_XATTR_LIST_INODE)
+#define ZPL_XATTR_LIST_WRAPPER(fn) \
+static size_t \
+fn(struct inode *ip, char *list, size_t list_size, \
+ const char *name, size_t name_len) \
+{ \
+ return (__ ## fn(ip, list, list_size, name, name_len)); \
}
+#endif
+
/*
* 4.4 API change,
- * The xattr_hander->get() callback was changed to take a xattr_handler,
+ * The xattr_handler->get() callback was changed to take a xattr_handler,
* and handler_flags argument was removed and should be accessed by
* handler->flags.
*/
-#elif defined(HAVE_HANDLER_XATTR_GET)
+#if defined(HAVE_XATTR_GET_HANDLER)
#define ZPL_XATTR_GET_WRAPPER(fn) \
static int \
fn(const struct xattr_handler *handler, struct dentry *dentry, \
@@ -68,35 +115,38 @@ fn(const struct xattr_handler *handler, struct dentry *dentry, \
{ \
return (__ ## fn(dentry->d_inode, name, buffer, size)); \
}
-#else
+/*
+ * 2.6.33 API change,
+ * The xattr_handler->get() callback was changed to take a dentry
+ * instead of an inode, and a handler_flags argument was added.
+ */
+#elif defined(HAVE_XATTR_GET_DENTRY)
#define ZPL_XATTR_GET_WRAPPER(fn) \
static int \
-fn(struct inode *ip, const char *name, void *buffer, size_t size) \
+fn(struct dentry *dentry, const char *name, void *buffer, size_t size, \
+ int unused_handler_flags) \
{ \
- return (__ ## fn(ip, name, buffer, size)); \
+ return (__ ## fn(dentry->d_inode, name, buffer, size)); \
}
-#endif /* HAVE_DENTRY_XATTR_GET */
-
/*
- * 2.6.33 API change,
- * The xattr_hander->set() callback was changed to take a dentry
- * instead of an inode, and a handler_flags argument was added.
+ * 2.6.32 API
*/
-#ifdef HAVE_DENTRY_XATTR_SET
-#define ZPL_XATTR_SET_WRAPPER(fn) \
+#elif defined(HAVE_XATTR_GET_INODE)
+#define ZPL_XATTR_GET_WRAPPER(fn) \
static int \
-fn(struct dentry *dentry, const char *name, const void *buffer, \
- size_t size, int flags, int unused_handler_flags) \
+fn(struct inode *ip, const char *name, void *buffer, size_t size) \
{ \
- return (__ ## fn(dentry->d_inode, name, buffer, size, flags)); \
+ return (__ ## fn(ip, name, buffer, size)); \
}
+#endif
+
/*
* 4.4 API change,
- * The xattr_hander->set() callback was changed to take a xattr_handler,
+ * The xattr_handler->set() callback was changed to take a xattr_handler,
* and handler_flags argument was removed and should be accessed by
* handler->flags.
*/
-#elif defined(HAVE_HANDLER_XATTR_SET)
+#if defined(HAVE_XATTR_SET_HANDLER)
#define ZPL_XATTR_SET_WRAPPER(fn) \
static int \
fn(const struct xattr_handler *handler, struct dentry *dentry, \
@@ -104,7 +154,23 @@ fn(const struct xattr_handler *handler, struct dentry *dentry, \
{ \
return (__ ## fn(dentry->d_inode, name, buffer, size, flags)); \
}
-#else
+/*
+ * 2.6.33 API change,
+ * The xattr_handler->set() callback was changed to take a dentry
+ * instead of an inode, and a handler_flags argument was added.
+ */
+#elif defined(HAVE_XATTR_SET_DENTRY)
+#define ZPL_XATTR_SET_WRAPPER(fn) \
+static int \
+fn(struct dentry *dentry, const char *name, const void *buffer, \
+ size_t size, int flags, int unused_handler_flags) \
+{ \
+ return (__ ## fn(dentry->d_inode, name, buffer, size, flags)); \
+}
+/*
+ * 2.6.32 API
+ */
+#elif defined(HAVE_XATTR_SET_INODE)
#define ZPL_XATTR_SET_WRAPPER(fn) \
static int \
fn(struct inode *ip, const char *name, const void *buffer, \
@@ -112,7 +178,7 @@ fn(struct inode *ip, const char *name, const void *buffer, \
{ \
return (__ ## fn(ip, name, buffer, size, flags)); \
}
-#endif /* HAVE_DENTRY_XATTR_SET */
+#endif
#ifdef HAVE_6ARGS_SECURITY_INODE_INIT_SECURITY
#define zpl_security_inode_init_security(ip, dip, qstr, nm, val, len) \
diff --git a/module/zfs/zpl_xattr.c b/module/zfs/zpl_xattr.c
index e39d94eaa..6a1acd7f4 100644
--- a/module/zfs/zpl_xattr.c
+++ b/module/zfs/zpl_xattr.c
@@ -88,19 +88,50 @@ typedef struct xattr_filldir {
size_t size;
size_t offset;
char *buf;
- struct inode *inode;
+ struct dentry *dentry;
} xattr_filldir_t;
+static const struct xattr_handler *zpl_xattr_handler(const char *);
+
static int
-zpl_xattr_filldir(xattr_filldir_t *xf, const char *name, int name_len)
+zpl_xattr_permission(xattr_filldir_t *xf, const char *name, int name_len)
{
- if (strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) == 0)
- if (!(ITOZSB(xf->inode)->z_flags & ZSB_XATTR))
- return (0);
+ static const struct xattr_handler *handler;
+ struct dentry *d = xf->dentry;
- if (strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) == 0)
- if (!capable(CAP_SYS_ADMIN))
+ handler = zpl_xattr_handler(name);
+ if (!handler)
+ return (0);
+
+ 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);
+#elif defined(HAVE_XATTR_LIST_INODE)
+ if (!handler->list(d->d_inode, NULL, 0, name, name_len))
+ return (0);
+#endif
+ }
+
+ return (1);
+}
+
+/*
+ * Determine is a given xattr name should be visible and if so copy it
+ * in to the provided buffer (xf->buf).
+ */
+static int
+zpl_xattr_filldir(xattr_filldir_t *xf, const char *name, int name_len)
+{
+ /* Check permissions using the per-namespace list xattr handler. */
+ if (!zpl_xattr_permission(xf, name, name_len))
+ return (0);
/* When xf->buf is NULL only calculate the required size. */
if (xf->buf) {
@@ -154,7 +185,7 @@ zpl_xattr_readdir(struct inode *dxip, xattr_filldir_t *xf)
static ssize_t
zpl_xattr_list_dir(xattr_filldir_t *xf, cred_t *cr)
{
- struct inode *ip = xf->inode;
+ struct inode *ip = xf->dentry->d_inode;
struct inode *dxip = NULL;
int error;
@@ -176,7 +207,7 @@ zpl_xattr_list_dir(xattr_filldir_t *xf, cred_t *cr)
static ssize_t
zpl_xattr_list_sa(xattr_filldir_t *xf)
{
- znode_t *zp = ITOZ(xf->inode);
+ znode_t *zp = ITOZ(xf->dentry->d_inode);
nvpair_t *nvp = NULL;
int error = 0;
@@ -207,7 +238,7 @@ zpl_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size)
{
znode_t *zp = ITOZ(dentry->d_inode);
zfs_sb_t *zsb = ZTOZSB(zp);
- xattr_filldir_t xf = { buffer_size, 0, buffer, dentry->d_inode };
+ xattr_filldir_t xf = { buffer_size, 0, buffer, dentry };
cred_t *cr = CRED();
fstrans_cookie_t cookie;
int error = 0;
@@ -614,6 +645,43 @@ out:
return (error);
}
+/*
+ * Extended user attributes
+ *
+ * "Extended user attributes may be assigned to files and directories for
+ * storing arbitrary additional information such as the mime type,
+ * character set or encoding of a file. The access permissions for user
+ * attributes are defined by the file permission bits: read permission
+ * is required to retrieve the attribute value, and writer permission is
+ * required to change it.
+ *
+ * The file permission bits of regular files and directories are
+ * interpreted differently from the file permission bits of special
+ * files and symbolic links. For regular files and directories the file
+ * permission bits define access to the file's contents, while for
+ * device special files they define access to the device described by
+ * the special file. The file permissions of symbolic links are not
+ * used in access checks. These differences would allow users to
+ * consume filesystem resources in a way not controllable by disk quotas
+ * for group or world writable special files and directories.
+ *
+ * For this reason, extended user attributes are allowed only for
+ * regular files and directories, and access to extended user attributes
+ * is restricted to the owner and to users with appropriate capabilities
+ * for directories with the sticky bit set (see the chmod(1) manual page
+ * for an explanation of the sticky bit)." - xattr(7)
+ *
+ * ZFS allows extended user attributes to be disabled administratively
+ * by setting the 'xattr=off' property on the dataset.
+ */
+static int
+__zpl_xattr_user_list(struct inode *ip, char *list, size_t list_size,
+ const char *name, size_t name_len)
+{
+ return (ITOZSB(ip)->z_flags & ZSB_XATTR);
+}
+ZPL_XATTR_LIST_WRAPPER(zpl_xattr_user_list);
+
static int
__zpl_xattr_user_get(struct inode *ip, const char *name,
void *value, size_t size)
@@ -656,12 +724,31 @@ __zpl_xattr_user_set(struct inode *ip, const char *name,
}
ZPL_XATTR_SET_WRAPPER(zpl_xattr_user_set);
-xattr_handler_t zpl_xattr_user_handler = {
+xattr_handler_t zpl_xattr_user_handler =
+{
.prefix = XATTR_USER_PREFIX,
+ .list = zpl_xattr_user_list,
.get = zpl_xattr_user_get,
.set = zpl_xattr_user_set,
};
+/*
+ * Trusted extended attributes
+ *
+ * "Trusted extended attributes are visible and accessible only to
+ * processes that have the CAP_SYS_ADMIN capability. Attributes in this
+ * class are used to implement mechanisms in user space (i.e., outside
+ * the kernel) which keep information in extended attributes to which
+ * ordinary processes should not have access." - xattr(7)
+ */
+static int
+__zpl_xattr_trusted_list(struct inode *ip, char *list, size_t list_size,
+ const char *name, size_t name_len)
+{
+ return (capable(CAP_SYS_ADMIN));
+}
+ZPL_XATTR_LIST_WRAPPER(zpl_xattr_trusted_list);
+
static int
__zpl_xattr_trusted_get(struct inode *ip, const char *name,
void *value, size_t size)
@@ -704,12 +791,34 @@ __zpl_xattr_trusted_set(struct inode *ip, const char *name,
}
ZPL_XATTR_SET_WRAPPER(zpl_xattr_trusted_set);
-xattr_handler_t zpl_xattr_trusted_handler = {
+xattr_handler_t zpl_xattr_trusted_handler =
+{
.prefix = XATTR_TRUSTED_PREFIX,
+ .list = zpl_xattr_trusted_list,
.get = zpl_xattr_trusted_get,
.set = zpl_xattr_trusted_set,
};
+/*
+ * Extended security attributes
+ *
+ * "The security attribute namespace is used by kernel security modules,
+ * such as Security Enhanced Linux, and also to implement file
+ * capabilities (see capabilities(7)). Read and write access
+ * permissions to security attributes depend on the policy implemented
+ * for each security attribute by the security module. When no security
+ * module is loaded, all processes have read access to extended security
+ * attributes, and write access is limited to processes that have the
+ * CAP_SYS_ADMIN capability." - xattr(7)
+ */
+static int
+__zpl_xattr_security_list(struct inode *ip, char *list, size_t list_size,
+ const char *name, size_t name_len)
+{
+ return (1);
+}
+ZPL_XATTR_LIST_WRAPPER(zpl_xattr_security_list);
+
static int
__zpl_xattr_security_get(struct inode *ip, const char *name,
void *value, size_t size)
@@ -801,14 +910,25 @@ zpl_xattr_security_init(struct inode *ip, struct inode *dip,
}
#endif /* HAVE_CALLBACK_SECURITY_INODE_INIT_SECURITY */
+/*
+ * Security xattr namespace handlers.
+ */
xattr_handler_t zpl_xattr_security_handler = {
.prefix = XATTR_SECURITY_PREFIX,
+ .list = zpl_xattr_security_list,
.get = zpl_xattr_security_get,
.set = zpl_xattr_security_set,
};
+/*
+ * Extended system attributes
+ *
+ * "Extended system attributes are used by the kernel to store system
+ * objects such as Access Control Lists. Read and write access permissions
+ * to system attributes depend on the policy implemented for each system
+ * attribute implemented by filesystems in the kernel." - xattr(7)
+ */
#ifdef CONFIG_FS_POSIX_ACL
-
int
zpl_set_acl(struct inode *ip, int type, struct posix_acl *acl)
{
@@ -822,7 +942,7 @@ zpl_set_acl(struct inode *ip, int type, struct posix_acl *acl)
switch (type) {
case ACL_TYPE_ACCESS:
- name = POSIX_ACL_XATTR_ACCESS;
+ name = XATTR_NAME_POSIX_ACL_ACCESS;
if (acl) {
zpl_equivmode_t mode = ip->i_mode;
error = posix_acl_equiv_mode(acl, &mode);
@@ -849,7 +969,7 @@ zpl_set_acl(struct inode *ip, int type, struct posix_acl *acl)
break;
case ACL_TYPE_DEFAULT:
- name = POSIX_ACL_XATTR_DEFAULT;
+ name = XATTR_NAME_POSIX_ACL_ACCESS;
if (!S_ISDIR(ip->i_mode))
return (acl ? -EACCES : 0);
break;
@@ -899,10 +1019,10 @@ zpl_get_acl(struct inode *ip, int type)
switch (type) {
case ACL_TYPE_ACCESS:
- name = POSIX_ACL_XATTR_ACCESS;
+ name = XATTR_NAME_POSIX_ACL_ACCESS;
break;
case ACL_TYPE_DEFAULT:
- name = POSIX_ACL_XATTR_DEFAULT;
+ name = XATTR_NAME_POSIX_ACL_DEFAULT;
break;
default:
return (ERR_PTR(-EINVAL));
@@ -1051,101 +1171,46 @@ zpl_chmod_acl(struct inode *ip)
return (error);
}
-static size_t
-zpl_xattr_acl_list(struct inode *ip, char *list, size_t list_size,
- const char *name, size_t name_len, int type)
+static int
+__zpl_xattr_acl_list_access(struct inode *ip, char *list, size_t list_size,
+ const char *name, size_t name_len)
{
- char *xattr_name;
- size_t xattr_size;
+ char *xattr_name = XATTR_NAME_POSIX_ACL_ACCESS;
+ size_t xattr_size = sizeof (XATTR_NAME_POSIX_ACL_ACCESS);
if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_POSIXACL)
return (0);
- switch (type) {
- case ACL_TYPE_ACCESS:
- xattr_name = POSIX_ACL_XATTR_ACCESS;
- xattr_size = sizeof (xattr_name);
- break;
- case ACL_TYPE_DEFAULT:
- xattr_name = POSIX_ACL_XATTR_DEFAULT;
- xattr_size = sizeof (xattr_name);
- break;
- default:
- return (0);
- }
-
if (list && xattr_size <= list_size)
memcpy(list, xattr_name, xattr_size);
return (xattr_size);
}
+ZPL_XATTR_LIST_WRAPPER(zpl_xattr_acl_list_access);
-#ifdef HAVE_DENTRY_XATTR_LIST
-static size_t
-zpl_xattr_acl_list_access(struct dentry *dentry, char *list,
- size_t list_size, const char *name, size_t name_len, int type)
-{
- ASSERT3S(type, ==, ACL_TYPE_ACCESS);
- return zpl_xattr_acl_list(dentry->d_inode,
- list, list_size, name, name_len, type);
-}
-
-static size_t
-zpl_xattr_acl_list_default(struct dentry *dentry, char *list,
- size_t list_size, const char *name, size_t name_len, int type)
-{
- ASSERT3S(type, ==, ACL_TYPE_DEFAULT);
- return zpl_xattr_acl_list(dentry->d_inode,
- list, list_size, name, name_len, type);
-}
-
-#elif defined(HAVE_HANDLER_XATTR_LIST)
-static size_t
-zpl_xattr_acl_list_access(const struct xattr_handler *handler,
- struct dentry *dentry, char *list, size_t list_size, const char *name,
- size_t name_len)
-{
- int type = handler->flags;
- ASSERT3S(type, ==, ACL_TYPE_ACCESS);
- return zpl_xattr_acl_list(dentry->d_inode,
- list, list_size, name, name_len, type);
-}
-
-static size_t
-zpl_xattr_acl_list_default(const struct xattr_handler *handler,
- struct dentry *dentry, char *list, size_t list_size, const char *name,
- size_t name_len)
+static int
+__zpl_xattr_acl_list_default(struct inode *ip, char *list, size_t list_size,
+ const char *name, size_t name_len)
{
- int type = handler->flags;
- ASSERT3S(type, ==, ACL_TYPE_DEFAULT);
- return zpl_xattr_acl_list(dentry->d_inode,
- list, list_size, name, name_len, type);
-}
+ char *xattr_name = XATTR_NAME_POSIX_ACL_DEFAULT;
+ size_t xattr_size = sizeof (XATTR_NAME_POSIX_ACL_DEFAULT);
-#else
+ if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_POSIXACL)
+ return (0);
-static size_t
-zpl_xattr_acl_list_access(struct inode *ip, char *list, size_t list_size,
- const char *name, size_t name_len)
-{
- return zpl_xattr_acl_list(ip,
- list, list_size, name, name_len, ACL_TYPE_ACCESS);
-}
+ if (list && xattr_size <= list_size)
+ memcpy(list, xattr_name, xattr_size);
-static size_t
-zpl_xattr_acl_list_default(struct inode *ip, char *list, size_t list_size,
- const char *name, size_t name_len)
-{
- return zpl_xattr_acl_list(ip,
- list, list_size, name, name_len, ACL_TYPE_DEFAULT);
+ return (xattr_size);
}
-#endif /* HAVE_DENTRY_XATTR_LIST */
+ZPL_XATTR_LIST_WRAPPER(zpl_xattr_acl_list_default);
static int
-zpl_xattr_acl_get(struct inode *ip, const char *name,
- void *buffer, size_t size, int type)
+__zpl_xattr_acl_get_access(struct inode *ip, const char *name,
+ void *buffer, size_t size)
{
struct posix_acl *acl;
+ int type = ACL_TYPE_ACCESS;
int error;
if (strcmp(name, "") != 0)
@@ -1165,65 +1230,41 @@ zpl_xattr_acl_get(struct inode *ip, const char *name,
return (error);
}
-
-#ifdef HAVE_DENTRY_XATTR_GET
-static int
-zpl_xattr_acl_get_access(struct dentry *dentry, const char *name,
- void *buffer, size_t size, int type)
-{
- ASSERT3S(type, ==, ACL_TYPE_ACCESS);
- return (zpl_xattr_acl_get(dentry->d_inode, name, buffer, size, type));
-}
+ZPL_XATTR_GET_WRAPPER(zpl_xattr_acl_get_access);
static int
-zpl_xattr_acl_get_default(struct dentry *dentry, const char *name,
- void *buffer, size_t size, int type)
+__zpl_xattr_acl_get_default(struct inode *ip, const char *name,
+ void *buffer, size_t size)
{
- ASSERT3S(type, ==, ACL_TYPE_DEFAULT);
- return (zpl_xattr_acl_get(dentry->d_inode, name, buffer, size, type));
-}
+ struct posix_acl *acl;
+ int type = ACL_TYPE_DEFAULT;
+ int error;
-#elif defined(HAVE_HANDLER_XATTR_GET)
-static int
-zpl_xattr_acl_get_access(const struct xattr_handler *handler,
- struct dentry *dentry, const char *name, void *buffer, size_t size)
-{
- int type = handler->flags;
- ASSERT3S(type, ==, ACL_TYPE_ACCESS);
- return (zpl_xattr_acl_get(dentry->d_inode, name, buffer, size, type));
-}
+ if (strcmp(name, "") != 0)
+ return (-EINVAL);
-static int
-zpl_xattr_acl_get_default(const struct xattr_handler *handler,
- struct dentry *dentry, const char *name, void *buffer, size_t size)
-{
- int type = handler->flags;
- ASSERT3S(type, ==, ACL_TYPE_DEFAULT);
- return (zpl_xattr_acl_get(dentry->d_inode, name, buffer, size, type));
-}
+ if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_POSIXACL)
+ return (-EOPNOTSUPP);
-#else
+ acl = zpl_get_acl(ip, type);
+ if (IS_ERR(acl))
+ return (PTR_ERR(acl));
+ if (acl == NULL)
+ return (-ENODATA);
-static int
-zpl_xattr_acl_get_access(struct inode *ip, const char *name,
- void *buffer, size_t size)
-{
- return (zpl_xattr_acl_get(ip, name, buffer, size, ACL_TYPE_ACCESS));
-}
+ error = zpl_acl_to_xattr(acl, buffer, size);
+ zpl_posix_acl_release(acl);
-static int
-zpl_xattr_acl_get_default(struct inode *ip, const char *name,
- void *buffer, size_t size)
-{
- return (zpl_xattr_acl_get(ip, name, buffer, size, ACL_TYPE_DEFAULT));
+ return (error);
}
-#endif /* HAVE_DENTRY_XATTR_GET */
+ZPL_XATTR_GET_WRAPPER(zpl_xattr_acl_get_default);
static int
-zpl_xattr_acl_set(struct inode *ip, const char *name,
- const void *value, size_t size, int flags, int type)
+__zpl_xattr_acl_set_access(struct inode *ip, const char *name,
+ const void *value, size_t size, int flags)
{
struct posix_acl *acl;
+ int type = ACL_TYPE_ACCESS;
int error = 0;
if (strcmp(name, "") != 0)
@@ -1255,88 +1296,77 @@ zpl_xattr_acl_set(struct inode *ip, const char *name,
return (error);
}
+ZPL_XATTR_SET_WRAPPER(zpl_xattr_acl_set_access);
-#ifdef HAVE_DENTRY_XATTR_SET
static int
-zpl_xattr_acl_set_access(struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags, int type)
+__zpl_xattr_acl_set_default(struct inode *ip, const char *name,
+ const void *value, size_t size, int flags)
{
- ASSERT3S(type, ==, ACL_TYPE_ACCESS);
- return (zpl_xattr_acl_set(dentry->d_inode,
- name, value, size, flags, type));
-}
+ struct posix_acl *acl;
+ int type = ACL_TYPE_DEFAULT;
+ int error = 0;
-static int
-zpl_xattr_acl_set_default(struct dentry *dentry, const char *name,
- const void *value, size_t size, int flags, int type)
-{
- ASSERT3S(type, ==, ACL_TYPE_DEFAULT);
- return zpl_xattr_acl_set(dentry->d_inode,
- name, value, size, flags, type);
-}
+ if (strcmp(name, "") != 0)
+ return (-EINVAL);
-#elif defined(HAVE_HANDLER_XATTR_SET)
-static int
-zpl_xattr_acl_set_access(const struct xattr_handler *handler,
- struct dentry *dentry, const char *name, const void *value, size_t size,
- int flags)
-{
- int type = handler->flags;
- ASSERT3S(type, ==, ACL_TYPE_ACCESS);
- return (zpl_xattr_acl_set(dentry->d_inode,
- name, value, size, flags, type));
-}
+ if (ITOZSB(ip)->z_acl_type != ZFS_ACLTYPE_POSIXACL)
+ return (-EOPNOTSUPP);
-static int
-zpl_xattr_acl_set_default(const struct xattr_handler *handler,
- struct dentry *dentry, const char *name, const void *value, size_t size,
- int flags)
-{
- int type = handler->flags;
- ASSERT3S(type, ==, ACL_TYPE_DEFAULT);
- return zpl_xattr_acl_set(dentry->d_inode,
- name, value, size, flags, type);
-}
+ if (!zpl_inode_owner_or_capable(ip))
+ return (-EPERM);
-#else
+ if (value) {
+ acl = zpl_acl_from_xattr(value, size);
+ if (IS_ERR(acl))
+ return (PTR_ERR(acl));
+ else if (acl) {
+ error = posix_acl_valid(acl);
+ if (error) {
+ zpl_posix_acl_release(acl);
+ return (error);
+ }
+ }
+ } else {
+ acl = NULL;
+ }
-static int
-zpl_xattr_acl_set_access(struct inode *ip, const char *name,
- const void *value, size_t size, int flags)
-{
- return zpl_xattr_acl_set(ip,
- name, value, size, flags, ACL_TYPE_ACCESS);
-}
+ error = zpl_set_acl(ip, type, acl);
+ zpl_posix_acl_release(acl);
-static int
-zpl_xattr_acl_set_default(struct inode *ip, const char *name,
- const void *value, size_t size, int flags)
-{
- return zpl_xattr_acl_set(ip,
- name, value, size, flags, ACL_TYPE_DEFAULT);
+ return (error);
}
-#endif /* HAVE_DENTRY_XATTR_SET */
+ZPL_XATTR_SET_WRAPPER(zpl_xattr_acl_set_default);
-struct xattr_handler zpl_xattr_acl_access_handler =
+/*
+ * ACL access xattr namespace handlers.
+ */
+xattr_handler_t zpl_xattr_acl_access_handler =
{
- .prefix = POSIX_ACL_XATTR_ACCESS,
+ .prefix = XATTR_NAME_POSIX_ACL_ACCESS,
.list = zpl_xattr_acl_list_access,
.get = zpl_xattr_acl_get_access,
.set = zpl_xattr_acl_set_access,
-#if defined(HAVE_DENTRY_XATTR_LIST) || defined(HAVE_HANDLER_XATTR_LIST)
+#if defined(HAVE_XATTR_LIST_SIMPLE) || \
+ defined(HAVE_XATTR_LIST_DENTRY) || \
+ defined(HAVE_XATTR_LIST_HANDLER)
.flags = ACL_TYPE_ACCESS,
-#endif /* HAVE_DENTRY_XATTR_LIST */
+#endif
};
-struct xattr_handler zpl_xattr_acl_default_handler =
+/*
+ * ACL default xattr namespace handlers.
+ */
+xattr_handler_t zpl_xattr_acl_default_handler =
{
- .prefix = POSIX_ACL_XATTR_DEFAULT,
+ .prefix = XATTR_NAME_POSIX_ACL_DEFAULT,
.list = zpl_xattr_acl_list_default,
.get = zpl_xattr_acl_get_default,
.set = zpl_xattr_acl_set_default,
-#if defined(HAVE_DENTRY_XATTR_LIST) || defined(HAVE_HANDLER_XATTR_LIST)
+#if defined(HAVE_XATTR_LIST_SIMPLE) || \
+ defined(HAVE_XATTR_LIST_DENTRY) || \
+ defined(HAVE_XATTR_LIST_HANDLER)
.flags = ACL_TYPE_DEFAULT,
-#endif /* HAVE_DENTRY_XATTR_LIST */
+#endif
};
#endif /* CONFIG_FS_POSIX_ACL */
@@ -1351,3 +1381,31 @@ xattr_handler_t *zpl_xattr_handlers[] = {
#endif /* CONFIG_FS_POSIX_ACL */
NULL
};
+
+static const struct xattr_handler *
+zpl_xattr_handler(const char *name)
+{
+ if (strncmp(name, XATTR_USER_PREFIX,
+ XATTR_USER_PREFIX_LEN) == 0)
+ return (&zpl_xattr_user_handler);
+
+ if (strncmp(name, XATTR_TRUSTED_PREFIX,
+ XATTR_TRUSTED_PREFIX_LEN) == 0)
+ return (&zpl_xattr_trusted_handler);
+
+ if (strncmp(name, XATTR_SECURITY_PREFIX,
+ XATTR_SECURITY_PREFIX_LEN) == 0)
+ return (&zpl_xattr_security_handler);
+
+#ifdef CONFIG_FS_POSIX_ACL
+ if (strncmp(name, XATTR_NAME_POSIX_ACL_ACCESS,
+ sizeof (XATTR_NAME_POSIX_ACL_ACCESS)) == 0)
+ return (&zpl_xattr_acl_access_handler);
+
+ if (strncmp(name, XATTR_NAME_POSIX_ACL_DEFAULT,
+ sizeof (XATTR_NAME_POSIX_ACL_DEFAULT)) == 0)
+ return (&zpl_xattr_acl_default_handler);
+#endif /* CONFIG_FS_POSIX_ACL */
+
+ return (NULL);
+}