linux/fs/xfs/xfs_xattr.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2008 Christoph Hellwig.
   4 * Portions Copyright (C) 2000-2008 Silicon Graphics, Inc.
   5 */
   6
   7#include "xfs.h"
   8#include "xfs_shared.h"
   9#include "xfs_format.h"
  10#include "xfs_log_format.h"
  11#include "xfs_da_format.h"
  12#include "xfs_inode.h"
  13#include "xfs_attr.h"
  14
  15#include <linux/posix_acl_xattr.h>
  16#include <linux/xattr.h>
  17
  18
  19static int
  20xfs_xattr_get(const struct xattr_handler *handler, struct dentry *unused,
  21                struct inode *inode, const char *name, void *value, size_t size)
  22{
  23        int xflags = handler->flags;
  24        struct xfs_inode *ip = XFS_I(inode);
  25        int error, asize = size;
  26
  27        /* Convert Linux syscall to XFS internal ATTR flags */
  28        if (!size) {
  29                xflags |= ATTR_KERNOVAL;
  30                value = NULL;
  31        }
  32
  33        error = xfs_attr_get(ip, (unsigned char *)name, value, &asize, xflags);
  34        if (error)
  35                return error;
  36        return asize;
  37}
  38
  39void
  40xfs_forget_acl(
  41        struct inode            *inode,
  42        const char              *name,
  43        int                     xflags)
  44{
  45        /*
  46         * Invalidate any cached ACLs if the user has bypassed the ACL
  47         * interface. We don't validate the content whatsoever so it is caller
  48         * responsibility to provide data in valid format and ensure i_mode is
  49         * consistent.
  50         */
  51        if (xflags & ATTR_ROOT) {
  52#ifdef CONFIG_XFS_POSIX_ACL
  53                if (!strcmp(name, SGI_ACL_FILE))
  54                        forget_cached_acl(inode, ACL_TYPE_ACCESS);
  55                else if (!strcmp(name, SGI_ACL_DEFAULT))
  56                        forget_cached_acl(inode, ACL_TYPE_DEFAULT);
  57#endif
  58        }
  59}
  60
  61static int
  62xfs_xattr_set(const struct xattr_handler *handler, struct dentry *unused,
  63                struct inode *inode, const char *name, const void *value,
  64                size_t size, int flags)
  65{
  66        int                     xflags = handler->flags;
  67        struct xfs_inode        *ip = XFS_I(inode);
  68        int                     error;
  69
  70        /* Convert Linux syscall to XFS internal ATTR flags */
  71        if (flags & XATTR_CREATE)
  72                xflags |= ATTR_CREATE;
  73        if (flags & XATTR_REPLACE)
  74                xflags |= ATTR_REPLACE;
  75
  76        if (!value)
  77                return xfs_attr_remove(ip, (unsigned char *)name, xflags);
  78        error = xfs_attr_set(ip, (unsigned char *)name,
  79                                (void *)value, size, xflags);
  80        if (!error)
  81                xfs_forget_acl(inode, name, xflags);
  82
  83        return error;
  84}
  85
  86static const struct xattr_handler xfs_xattr_user_handler = {
  87        .prefix = XATTR_USER_PREFIX,
  88        .flags  = 0, /* no flags implies user namespace */
  89        .get    = xfs_xattr_get,
  90        .set    = xfs_xattr_set,
  91};
  92
  93static const struct xattr_handler xfs_xattr_trusted_handler = {
  94        .prefix = XATTR_TRUSTED_PREFIX,
  95        .flags  = ATTR_ROOT,
  96        .get    = xfs_xattr_get,
  97        .set    = xfs_xattr_set,
  98};
  99
 100static const struct xattr_handler xfs_xattr_security_handler = {
 101        .prefix = XATTR_SECURITY_PREFIX,
 102        .flags  = ATTR_SECURE,
 103        .get    = xfs_xattr_get,
 104        .set    = xfs_xattr_set,
 105};
 106
 107const struct xattr_handler *xfs_xattr_handlers[] = {
 108        &xfs_xattr_user_handler,
 109        &xfs_xattr_trusted_handler,
 110        &xfs_xattr_security_handler,
 111#ifdef CONFIG_XFS_POSIX_ACL
 112        &posix_acl_access_xattr_handler,
 113        &posix_acl_default_xattr_handler,
 114#endif
 115        NULL
 116};
 117
 118static void
 119__xfs_xattr_put_listent(
 120        struct xfs_attr_list_context *context,
 121        char *prefix,
 122        int prefix_len,
 123        unsigned char *name,
 124        int namelen)
 125{
 126        char *offset;
 127        int arraytop;
 128
 129        if (context->count < 0 || context->seen_enough)
 130                return;
 131
 132        if (!context->alist)
 133                goto compute_size;
 134
 135        arraytop = context->count + prefix_len + namelen + 1;
 136        if (arraytop > context->firstu) {
 137                context->count = -1;    /* insufficient space */
 138                context->seen_enough = 1;
 139                return;
 140        }
 141        offset = (char *)context->alist + context->count;
 142        strncpy(offset, prefix, prefix_len);
 143        offset += prefix_len;
 144        strncpy(offset, (char *)name, namelen);                 /* real name */
 145        offset += namelen;
 146        *offset = '\0';
 147
 148compute_size:
 149        context->count += prefix_len + namelen + 1;
 150        return;
 151}
 152
 153static void
 154xfs_xattr_put_listent(
 155        struct xfs_attr_list_context *context,
 156        int             flags,
 157        unsigned char   *name,
 158        int             namelen,
 159        int             valuelen)
 160{
 161        char *prefix;
 162        int prefix_len;
 163
 164        ASSERT(context->count >= 0);
 165
 166        if (flags & XFS_ATTR_ROOT) {
 167#ifdef CONFIG_XFS_POSIX_ACL
 168                if (namelen == SGI_ACL_FILE_SIZE &&
 169                    strncmp(name, SGI_ACL_FILE,
 170                            SGI_ACL_FILE_SIZE) == 0) {
 171                        __xfs_xattr_put_listent(
 172                                        context, XATTR_SYSTEM_PREFIX,
 173                                        XATTR_SYSTEM_PREFIX_LEN,
 174                                        XATTR_POSIX_ACL_ACCESS,
 175                                        strlen(XATTR_POSIX_ACL_ACCESS));
 176                } else if (namelen == SGI_ACL_DEFAULT_SIZE &&
 177                         strncmp(name, SGI_ACL_DEFAULT,
 178                                 SGI_ACL_DEFAULT_SIZE) == 0) {
 179                        __xfs_xattr_put_listent(
 180                                        context, XATTR_SYSTEM_PREFIX,
 181                                        XATTR_SYSTEM_PREFIX_LEN,
 182                                        XATTR_POSIX_ACL_DEFAULT,
 183                                        strlen(XATTR_POSIX_ACL_DEFAULT));
 184                }
 185#endif
 186
 187                /*
 188                 * Only show root namespace entries if we are actually allowed to
 189                 * see them.
 190                 */
 191                if (!capable(CAP_SYS_ADMIN))
 192                        return;
 193
 194                prefix = XATTR_TRUSTED_PREFIX;
 195                prefix_len = XATTR_TRUSTED_PREFIX_LEN;
 196        } else if (flags & XFS_ATTR_SECURE) {
 197                prefix = XATTR_SECURITY_PREFIX;
 198                prefix_len = XATTR_SECURITY_PREFIX_LEN;
 199        } else {
 200                prefix = XATTR_USER_PREFIX;
 201                prefix_len = XATTR_USER_PREFIX_LEN;
 202        }
 203
 204        __xfs_xattr_put_listent(context, prefix, prefix_len, name,
 205                                namelen);
 206        return;
 207}
 208
 209ssize_t
 210xfs_vn_listxattr(
 211        struct dentry   *dentry,
 212        char            *data,
 213        size_t          size)
 214{
 215        struct xfs_attr_list_context context;
 216        struct attrlist_cursor_kern cursor = { 0 };
 217        struct inode    *inode = d_inode(dentry);
 218        int             error;
 219
 220        /*
 221         * First read the regular on-disk attributes.
 222         */
 223        memset(&context, 0, sizeof(context));
 224        context.dp = XFS_I(inode);
 225        context.cursor = &cursor;
 226        context.resynch = 1;
 227        context.alist = size ? data : NULL;
 228        context.bufsize = size;
 229        context.firstu = context.bufsize;
 230        context.put_listent = xfs_xattr_put_listent;
 231
 232        error = xfs_attr_list_int(&context);
 233        if (error)
 234                return error;
 235        if (context.count < 0)
 236                return -ERANGE;
 237
 238        return context.count;
 239}
 240