// SPDX-License-Identifier: GPL-2.0-or-later 
 | 
/* 
 | 
 * Squashfs - a compressed read only filesystem for Linux 
 | 
 * 
 | 
 * Copyright (c) 2010 
 | 
 * Phillip Lougher <phillip@squashfs.org.uk> 
 | 
 * 
 | 
 * xattr.c 
 | 
 */ 
 | 
  
 | 
#include <linux/init.h> 
 | 
#include <linux/module.h> 
 | 
#include <linux/string.h> 
 | 
#include <linux/fs.h> 
 | 
#include <linux/vfs.h> 
 | 
#include <linux/xattr.h> 
 | 
#include <linux/slab.h> 
 | 
  
 | 
#include "squashfs_fs.h" 
 | 
#include "squashfs_fs_sb.h" 
 | 
#include "squashfs_fs_i.h" 
 | 
#include "squashfs.h" 
 | 
  
 | 
static const struct xattr_handler *squashfs_xattr_handler(int); 
 | 
  
 | 
ssize_t squashfs_listxattr(struct dentry *d, char *buffer, 
 | 
    size_t buffer_size) 
 | 
{ 
 | 
    struct inode *inode = d_inode(d); 
 | 
    struct super_block *sb = inode->i_sb; 
 | 
    struct squashfs_sb_info *msblk = sb->s_fs_info; 
 | 
    u64 start = SQUASHFS_XATTR_BLK(squashfs_i(inode)->xattr) 
 | 
                         + msblk->xattr_table; 
 | 
    int offset = SQUASHFS_XATTR_OFFSET(squashfs_i(inode)->xattr); 
 | 
    int count = squashfs_i(inode)->xattr_count; 
 | 
    size_t rest = buffer_size; 
 | 
    int err; 
 | 
  
 | 
    /* check that the file system has xattrs */ 
 | 
    if (msblk->xattr_id_table == NULL) 
 | 
        return -EOPNOTSUPP; 
 | 
  
 | 
    /* loop reading each xattr name */ 
 | 
    while (count--) { 
 | 
        struct squashfs_xattr_entry entry; 
 | 
        struct squashfs_xattr_val val; 
 | 
        const struct xattr_handler *handler; 
 | 
        int name_size; 
 | 
  
 | 
        err = squashfs_read_metadata(sb, &entry, &start, &offset, 
 | 
                            sizeof(entry)); 
 | 
        if (err < 0) 
 | 
            goto failed; 
 | 
  
 | 
        name_size = le16_to_cpu(entry.size); 
 | 
        handler = squashfs_xattr_handler(le16_to_cpu(entry.type)); 
 | 
        if (handler && (!handler->list || handler->list(d))) { 
 | 
            const char *prefix = handler->prefix ?: handler->name; 
 | 
            size_t prefix_size = strlen(prefix); 
 | 
  
 | 
            if (buffer) { 
 | 
                if (prefix_size + name_size + 1 > rest) { 
 | 
                    err = -ERANGE; 
 | 
                    goto failed; 
 | 
                } 
 | 
                memcpy(buffer, prefix, prefix_size); 
 | 
                buffer += prefix_size; 
 | 
            } 
 | 
            err = squashfs_read_metadata(sb, buffer, &start, 
 | 
                &offset, name_size); 
 | 
            if (err < 0) 
 | 
                goto failed; 
 | 
            if (buffer) { 
 | 
                buffer[name_size] = '\0'; 
 | 
                buffer += name_size + 1; 
 | 
            } 
 | 
            rest -= prefix_size + name_size + 1; 
 | 
        } else  { 
 | 
            /* no handler or insuffficient privileges, so skip */ 
 | 
            err = squashfs_read_metadata(sb, NULL, &start, 
 | 
                &offset, name_size); 
 | 
            if (err < 0) 
 | 
                goto failed; 
 | 
        } 
 | 
  
 | 
  
 | 
        /* skip remaining xattr entry */ 
 | 
        err = squashfs_read_metadata(sb, &val, &start, &offset, 
 | 
                        sizeof(val)); 
 | 
        if (err < 0) 
 | 
            goto failed; 
 | 
  
 | 
        err = squashfs_read_metadata(sb, NULL, &start, &offset, 
 | 
                        le32_to_cpu(val.vsize)); 
 | 
        if (err < 0) 
 | 
            goto failed; 
 | 
    } 
 | 
    err = buffer_size - rest; 
 | 
  
 | 
failed: 
 | 
    return err; 
 | 
} 
 | 
  
 | 
  
 | 
static int squashfs_xattr_get(struct inode *inode, int name_index, 
 | 
    const char *name, void *buffer, size_t buffer_size) 
 | 
{ 
 | 
    struct super_block *sb = inode->i_sb; 
 | 
    struct squashfs_sb_info *msblk = sb->s_fs_info; 
 | 
    u64 start = SQUASHFS_XATTR_BLK(squashfs_i(inode)->xattr) 
 | 
                         + msblk->xattr_table; 
 | 
    int offset = SQUASHFS_XATTR_OFFSET(squashfs_i(inode)->xattr); 
 | 
    int count = squashfs_i(inode)->xattr_count; 
 | 
    int name_len = strlen(name); 
 | 
    int err, vsize; 
 | 
    char *target = kmalloc(name_len, GFP_KERNEL); 
 | 
  
 | 
    if (target == NULL) 
 | 
        return  -ENOMEM; 
 | 
  
 | 
    /* loop reading each xattr name */ 
 | 
    for (; count; count--) { 
 | 
        struct squashfs_xattr_entry entry; 
 | 
        struct squashfs_xattr_val val; 
 | 
        int type, prefix, name_size; 
 | 
  
 | 
        err = squashfs_read_metadata(sb, &entry, &start, &offset, 
 | 
                            sizeof(entry)); 
 | 
        if (err < 0) 
 | 
            goto failed; 
 | 
  
 | 
        name_size = le16_to_cpu(entry.size); 
 | 
        type = le16_to_cpu(entry.type); 
 | 
        prefix = type & SQUASHFS_XATTR_PREFIX_MASK; 
 | 
  
 | 
        if (prefix == name_index && name_size == name_len) 
 | 
            err = squashfs_read_metadata(sb, target, &start, 
 | 
                        &offset, name_size); 
 | 
        else 
 | 
            err = squashfs_read_metadata(sb, NULL, &start, 
 | 
                        &offset, name_size); 
 | 
        if (err < 0) 
 | 
            goto failed; 
 | 
  
 | 
        if (prefix == name_index && name_size == name_len && 
 | 
                    strncmp(target, name, name_size) == 0) { 
 | 
            /* found xattr */ 
 | 
            if (type & SQUASHFS_XATTR_VALUE_OOL) { 
 | 
                __le64 xattr_val; 
 | 
                u64 xattr; 
 | 
                /* val is a reference to the real location */ 
 | 
                err = squashfs_read_metadata(sb, &val, &start, 
 | 
                        &offset, sizeof(val)); 
 | 
                if (err < 0) 
 | 
                    goto failed; 
 | 
                err = squashfs_read_metadata(sb, &xattr_val, 
 | 
                    &start, &offset, sizeof(xattr_val)); 
 | 
                if (err < 0) 
 | 
                    goto failed; 
 | 
                xattr = le64_to_cpu(xattr_val); 
 | 
                start = SQUASHFS_XATTR_BLK(xattr) + 
 | 
                            msblk->xattr_table; 
 | 
                offset = SQUASHFS_XATTR_OFFSET(xattr); 
 | 
            } 
 | 
            /* read xattr value */ 
 | 
            err = squashfs_read_metadata(sb, &val, &start, &offset, 
 | 
                            sizeof(val)); 
 | 
            if (err < 0) 
 | 
                goto failed; 
 | 
  
 | 
            vsize = le32_to_cpu(val.vsize); 
 | 
            if (buffer) { 
 | 
                if (vsize > buffer_size) { 
 | 
                    err = -ERANGE; 
 | 
                    goto failed; 
 | 
                } 
 | 
                err = squashfs_read_metadata(sb, buffer, &start, 
 | 
                     &offset, vsize); 
 | 
                if (err < 0) 
 | 
                    goto failed; 
 | 
            } 
 | 
            break; 
 | 
        } 
 | 
  
 | 
        /* no match, skip remaining xattr entry */ 
 | 
        err = squashfs_read_metadata(sb, &val, &start, &offset, 
 | 
                            sizeof(val)); 
 | 
        if (err < 0) 
 | 
            goto failed; 
 | 
        err = squashfs_read_metadata(sb, NULL, &start, &offset, 
 | 
                        le32_to_cpu(val.vsize)); 
 | 
        if (err < 0) 
 | 
            goto failed; 
 | 
    } 
 | 
    err = count ? vsize : -ENODATA; 
 | 
  
 | 
failed: 
 | 
    kfree(target); 
 | 
    return err; 
 | 
} 
 | 
  
 | 
  
 | 
static int squashfs_xattr_handler_get(const struct xattr_handler *handler, 
 | 
                      struct dentry *unused, 
 | 
                      struct inode *inode, 
 | 
                      const char *name, 
 | 
                      void *buffer, size_t size, int flags) 
 | 
{ 
 | 
    return squashfs_xattr_get(inode, handler->flags, name, 
 | 
        buffer, size); 
 | 
} 
 | 
  
 | 
/* 
 | 
 * User namespace support 
 | 
 */ 
 | 
static const struct xattr_handler squashfs_xattr_user_handler = { 
 | 
    .prefix    = XATTR_USER_PREFIX, 
 | 
    .flags    = SQUASHFS_XATTR_USER, 
 | 
    .get    = squashfs_xattr_handler_get 
 | 
}; 
 | 
  
 | 
/* 
 | 
 * Trusted namespace support 
 | 
 */ 
 | 
static bool squashfs_trusted_xattr_handler_list(struct dentry *d) 
 | 
{ 
 | 
    return capable(CAP_SYS_ADMIN); 
 | 
} 
 | 
  
 | 
static const struct xattr_handler squashfs_xattr_trusted_handler = { 
 | 
    .prefix    = XATTR_TRUSTED_PREFIX, 
 | 
    .flags    = SQUASHFS_XATTR_TRUSTED, 
 | 
    .list    = squashfs_trusted_xattr_handler_list, 
 | 
    .get    = squashfs_xattr_handler_get 
 | 
}; 
 | 
  
 | 
/* 
 | 
 * Security namespace support 
 | 
 */ 
 | 
static const struct xattr_handler squashfs_xattr_security_handler = { 
 | 
    .prefix    = XATTR_SECURITY_PREFIX, 
 | 
    .flags    = SQUASHFS_XATTR_SECURITY, 
 | 
    .get    = squashfs_xattr_handler_get 
 | 
}; 
 | 
  
 | 
static const struct xattr_handler *squashfs_xattr_handler(int type) 
 | 
{ 
 | 
    if (type & ~(SQUASHFS_XATTR_PREFIX_MASK | SQUASHFS_XATTR_VALUE_OOL)) 
 | 
        /* ignore unrecognised type */ 
 | 
        return NULL; 
 | 
  
 | 
    switch (type & SQUASHFS_XATTR_PREFIX_MASK) { 
 | 
    case SQUASHFS_XATTR_USER: 
 | 
        return &squashfs_xattr_user_handler; 
 | 
    case SQUASHFS_XATTR_TRUSTED: 
 | 
        return &squashfs_xattr_trusted_handler; 
 | 
    case SQUASHFS_XATTR_SECURITY: 
 | 
        return &squashfs_xattr_security_handler; 
 | 
    default: 
 | 
        /* ignore unrecognised type */ 
 | 
        return NULL; 
 | 
    } 
 | 
} 
 | 
  
 | 
const struct xattr_handler *squashfs_xattr_handlers[] = { 
 | 
    &squashfs_xattr_user_handler, 
 | 
    &squashfs_xattr_trusted_handler, 
 | 
    &squashfs_xattr_security_handler, 
 | 
    NULL 
 | 
}; 
 |