162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Squashfs - a compressed read only filesystem for Linux 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2010 662306a36Sopenharmony_ci * Phillip Lougher <phillip@squashfs.org.uk> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * xattr.c 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/string.h> 1462306a36Sopenharmony_ci#include <linux/fs.h> 1562306a36Sopenharmony_ci#include <linux/vfs.h> 1662306a36Sopenharmony_ci#include <linux/xattr.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "squashfs_fs.h" 2062306a36Sopenharmony_ci#include "squashfs_fs_sb.h" 2162306a36Sopenharmony_ci#include "squashfs_fs_i.h" 2262306a36Sopenharmony_ci#include "squashfs.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic const struct xattr_handler *squashfs_xattr_handler(int); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cissize_t squashfs_listxattr(struct dentry *d, char *buffer, 2762306a36Sopenharmony_ci size_t buffer_size) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci struct inode *inode = d_inode(d); 3062306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 3162306a36Sopenharmony_ci struct squashfs_sb_info *msblk = sb->s_fs_info; 3262306a36Sopenharmony_ci u64 start = SQUASHFS_XATTR_BLK(squashfs_i(inode)->xattr) 3362306a36Sopenharmony_ci + msblk->xattr_table; 3462306a36Sopenharmony_ci int offset = SQUASHFS_XATTR_OFFSET(squashfs_i(inode)->xattr); 3562306a36Sopenharmony_ci int count = squashfs_i(inode)->xattr_count; 3662306a36Sopenharmony_ci size_t rest = buffer_size; 3762306a36Sopenharmony_ci int err; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci /* check that the file system has xattrs */ 4062306a36Sopenharmony_ci if (msblk->xattr_id_table == NULL) 4162306a36Sopenharmony_ci return -EOPNOTSUPP; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci /* loop reading each xattr name */ 4462306a36Sopenharmony_ci while (count--) { 4562306a36Sopenharmony_ci struct squashfs_xattr_entry entry; 4662306a36Sopenharmony_ci struct squashfs_xattr_val val; 4762306a36Sopenharmony_ci const struct xattr_handler *handler; 4862306a36Sopenharmony_ci int name_size; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci err = squashfs_read_metadata(sb, &entry, &start, &offset, 5162306a36Sopenharmony_ci sizeof(entry)); 5262306a36Sopenharmony_ci if (err < 0) 5362306a36Sopenharmony_ci goto failed; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci name_size = le16_to_cpu(entry.size); 5662306a36Sopenharmony_ci handler = squashfs_xattr_handler(le16_to_cpu(entry.type)); 5762306a36Sopenharmony_ci if (handler && (!handler->list || handler->list(d))) { 5862306a36Sopenharmony_ci const char *prefix = handler->prefix ?: handler->name; 5962306a36Sopenharmony_ci size_t prefix_size = strlen(prefix); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (buffer) { 6262306a36Sopenharmony_ci if (prefix_size + name_size + 1 > rest) { 6362306a36Sopenharmony_ci err = -ERANGE; 6462306a36Sopenharmony_ci goto failed; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci memcpy(buffer, prefix, prefix_size); 6762306a36Sopenharmony_ci buffer += prefix_size; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci err = squashfs_read_metadata(sb, buffer, &start, 7062306a36Sopenharmony_ci &offset, name_size); 7162306a36Sopenharmony_ci if (err < 0) 7262306a36Sopenharmony_ci goto failed; 7362306a36Sopenharmony_ci if (buffer) { 7462306a36Sopenharmony_ci buffer[name_size] = '\0'; 7562306a36Sopenharmony_ci buffer += name_size + 1; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci rest -= prefix_size + name_size + 1; 7862306a36Sopenharmony_ci } else { 7962306a36Sopenharmony_ci /* no handler or insuffficient privileges, so skip */ 8062306a36Sopenharmony_ci err = squashfs_read_metadata(sb, NULL, &start, 8162306a36Sopenharmony_ci &offset, name_size); 8262306a36Sopenharmony_ci if (err < 0) 8362306a36Sopenharmony_ci goto failed; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* skip remaining xattr entry */ 8862306a36Sopenharmony_ci err = squashfs_read_metadata(sb, &val, &start, &offset, 8962306a36Sopenharmony_ci sizeof(val)); 9062306a36Sopenharmony_ci if (err < 0) 9162306a36Sopenharmony_ci goto failed; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci err = squashfs_read_metadata(sb, NULL, &start, &offset, 9462306a36Sopenharmony_ci le32_to_cpu(val.vsize)); 9562306a36Sopenharmony_ci if (err < 0) 9662306a36Sopenharmony_ci goto failed; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci err = buffer_size - rest; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cifailed: 10162306a36Sopenharmony_ci return err; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int squashfs_xattr_get(struct inode *inode, int name_index, 10662306a36Sopenharmony_ci const char *name, void *buffer, size_t buffer_size) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct super_block *sb = inode->i_sb; 10962306a36Sopenharmony_ci struct squashfs_sb_info *msblk = sb->s_fs_info; 11062306a36Sopenharmony_ci u64 start = SQUASHFS_XATTR_BLK(squashfs_i(inode)->xattr) 11162306a36Sopenharmony_ci + msblk->xattr_table; 11262306a36Sopenharmony_ci int offset = SQUASHFS_XATTR_OFFSET(squashfs_i(inode)->xattr); 11362306a36Sopenharmony_ci int count = squashfs_i(inode)->xattr_count; 11462306a36Sopenharmony_ci int name_len = strlen(name); 11562306a36Sopenharmony_ci int err, vsize; 11662306a36Sopenharmony_ci char *target = kmalloc(name_len, GFP_KERNEL); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (target == NULL) 11962306a36Sopenharmony_ci return -ENOMEM; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* loop reading each xattr name */ 12262306a36Sopenharmony_ci for (; count; count--) { 12362306a36Sopenharmony_ci struct squashfs_xattr_entry entry; 12462306a36Sopenharmony_ci struct squashfs_xattr_val val; 12562306a36Sopenharmony_ci int type, prefix, name_size; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci err = squashfs_read_metadata(sb, &entry, &start, &offset, 12862306a36Sopenharmony_ci sizeof(entry)); 12962306a36Sopenharmony_ci if (err < 0) 13062306a36Sopenharmony_ci goto failed; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci name_size = le16_to_cpu(entry.size); 13362306a36Sopenharmony_ci type = le16_to_cpu(entry.type); 13462306a36Sopenharmony_ci prefix = type & SQUASHFS_XATTR_PREFIX_MASK; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (prefix == name_index && name_size == name_len) 13762306a36Sopenharmony_ci err = squashfs_read_metadata(sb, target, &start, 13862306a36Sopenharmony_ci &offset, name_size); 13962306a36Sopenharmony_ci else 14062306a36Sopenharmony_ci err = squashfs_read_metadata(sb, NULL, &start, 14162306a36Sopenharmony_ci &offset, name_size); 14262306a36Sopenharmony_ci if (err < 0) 14362306a36Sopenharmony_ci goto failed; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (prefix == name_index && name_size == name_len && 14662306a36Sopenharmony_ci strncmp(target, name, name_size) == 0) { 14762306a36Sopenharmony_ci /* found xattr */ 14862306a36Sopenharmony_ci if (type & SQUASHFS_XATTR_VALUE_OOL) { 14962306a36Sopenharmony_ci __le64 xattr_val; 15062306a36Sopenharmony_ci u64 xattr; 15162306a36Sopenharmony_ci /* val is a reference to the real location */ 15262306a36Sopenharmony_ci err = squashfs_read_metadata(sb, &val, &start, 15362306a36Sopenharmony_ci &offset, sizeof(val)); 15462306a36Sopenharmony_ci if (err < 0) 15562306a36Sopenharmony_ci goto failed; 15662306a36Sopenharmony_ci err = squashfs_read_metadata(sb, &xattr_val, 15762306a36Sopenharmony_ci &start, &offset, sizeof(xattr_val)); 15862306a36Sopenharmony_ci if (err < 0) 15962306a36Sopenharmony_ci goto failed; 16062306a36Sopenharmony_ci xattr = le64_to_cpu(xattr_val); 16162306a36Sopenharmony_ci start = SQUASHFS_XATTR_BLK(xattr) + 16262306a36Sopenharmony_ci msblk->xattr_table; 16362306a36Sopenharmony_ci offset = SQUASHFS_XATTR_OFFSET(xattr); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci /* read xattr value */ 16662306a36Sopenharmony_ci err = squashfs_read_metadata(sb, &val, &start, &offset, 16762306a36Sopenharmony_ci sizeof(val)); 16862306a36Sopenharmony_ci if (err < 0) 16962306a36Sopenharmony_ci goto failed; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci vsize = le32_to_cpu(val.vsize); 17262306a36Sopenharmony_ci if (buffer) { 17362306a36Sopenharmony_ci if (vsize > buffer_size) { 17462306a36Sopenharmony_ci err = -ERANGE; 17562306a36Sopenharmony_ci goto failed; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci err = squashfs_read_metadata(sb, buffer, &start, 17862306a36Sopenharmony_ci &offset, vsize); 17962306a36Sopenharmony_ci if (err < 0) 18062306a36Sopenharmony_ci goto failed; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* no match, skip remaining xattr entry */ 18662306a36Sopenharmony_ci err = squashfs_read_metadata(sb, &val, &start, &offset, 18762306a36Sopenharmony_ci sizeof(val)); 18862306a36Sopenharmony_ci if (err < 0) 18962306a36Sopenharmony_ci goto failed; 19062306a36Sopenharmony_ci err = squashfs_read_metadata(sb, NULL, &start, &offset, 19162306a36Sopenharmony_ci le32_to_cpu(val.vsize)); 19262306a36Sopenharmony_ci if (err < 0) 19362306a36Sopenharmony_ci goto failed; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci err = count ? vsize : -ENODATA; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cifailed: 19862306a36Sopenharmony_ci kfree(target); 19962306a36Sopenharmony_ci return err; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic int squashfs_xattr_handler_get(const struct xattr_handler *handler, 20462306a36Sopenharmony_ci struct dentry *unused, 20562306a36Sopenharmony_ci struct inode *inode, 20662306a36Sopenharmony_ci const char *name, 20762306a36Sopenharmony_ci void *buffer, size_t size) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci return squashfs_xattr_get(inode, handler->flags, name, 21062306a36Sopenharmony_ci buffer, size); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/* 21462306a36Sopenharmony_ci * User namespace support 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_cistatic const struct xattr_handler squashfs_xattr_user_handler = { 21762306a36Sopenharmony_ci .prefix = XATTR_USER_PREFIX, 21862306a36Sopenharmony_ci .flags = SQUASHFS_XATTR_USER, 21962306a36Sopenharmony_ci .get = squashfs_xattr_handler_get 22062306a36Sopenharmony_ci}; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci/* 22362306a36Sopenharmony_ci * Trusted namespace support 22462306a36Sopenharmony_ci */ 22562306a36Sopenharmony_cistatic bool squashfs_trusted_xattr_handler_list(struct dentry *d) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci return capable(CAP_SYS_ADMIN); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic const struct xattr_handler squashfs_xattr_trusted_handler = { 23162306a36Sopenharmony_ci .prefix = XATTR_TRUSTED_PREFIX, 23262306a36Sopenharmony_ci .flags = SQUASHFS_XATTR_TRUSTED, 23362306a36Sopenharmony_ci .list = squashfs_trusted_xattr_handler_list, 23462306a36Sopenharmony_ci .get = squashfs_xattr_handler_get 23562306a36Sopenharmony_ci}; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/* 23862306a36Sopenharmony_ci * Security namespace support 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_cistatic const struct xattr_handler squashfs_xattr_security_handler = { 24162306a36Sopenharmony_ci .prefix = XATTR_SECURITY_PREFIX, 24262306a36Sopenharmony_ci .flags = SQUASHFS_XATTR_SECURITY, 24362306a36Sopenharmony_ci .get = squashfs_xattr_handler_get 24462306a36Sopenharmony_ci}; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic const struct xattr_handler *squashfs_xattr_handler(int type) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci if (type & ~(SQUASHFS_XATTR_PREFIX_MASK | SQUASHFS_XATTR_VALUE_OOL)) 24962306a36Sopenharmony_ci /* ignore unrecognised type */ 25062306a36Sopenharmony_ci return NULL; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci switch (type & SQUASHFS_XATTR_PREFIX_MASK) { 25362306a36Sopenharmony_ci case SQUASHFS_XATTR_USER: 25462306a36Sopenharmony_ci return &squashfs_xattr_user_handler; 25562306a36Sopenharmony_ci case SQUASHFS_XATTR_TRUSTED: 25662306a36Sopenharmony_ci return &squashfs_xattr_trusted_handler; 25762306a36Sopenharmony_ci case SQUASHFS_XATTR_SECURITY: 25862306a36Sopenharmony_ci return &squashfs_xattr_security_handler; 25962306a36Sopenharmony_ci default: 26062306a36Sopenharmony_ci /* ignore unrecognised type */ 26162306a36Sopenharmony_ci return NULL; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ciconst struct xattr_handler *squashfs_xattr_handlers[] = { 26662306a36Sopenharmony_ci &squashfs_xattr_user_handler, 26762306a36Sopenharmony_ci &squashfs_xattr_trusted_handler, 26862306a36Sopenharmony_ci &squashfs_xattr_security_handler, 26962306a36Sopenharmony_ci NULL 27062306a36Sopenharmony_ci}; 27162306a36Sopenharmony_ci 272