xref: /kernel/linux/linux-5.10/fs/squashfs/xattr.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Squashfs - a compressed read only filesystem for Linux
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2010
68c2ecf20Sopenharmony_ci * Phillip Lougher <phillip@squashfs.org.uk>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * xattr.c
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/string.h>
148c2ecf20Sopenharmony_ci#include <linux/fs.h>
158c2ecf20Sopenharmony_ci#include <linux/vfs.h>
168c2ecf20Sopenharmony_ci#include <linux/xattr.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "squashfs_fs.h"
208c2ecf20Sopenharmony_ci#include "squashfs_fs_sb.h"
218c2ecf20Sopenharmony_ci#include "squashfs_fs_i.h"
228c2ecf20Sopenharmony_ci#include "squashfs.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic const struct xattr_handler *squashfs_xattr_handler(int);
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cissize_t squashfs_listxattr(struct dentry *d, char *buffer,
278c2ecf20Sopenharmony_ci	size_t buffer_size)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct inode *inode = d_inode(d);
308c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
318c2ecf20Sopenharmony_ci	struct squashfs_sb_info *msblk = sb->s_fs_info;
328c2ecf20Sopenharmony_ci	u64 start = SQUASHFS_XATTR_BLK(squashfs_i(inode)->xattr)
338c2ecf20Sopenharmony_ci						 + msblk->xattr_table;
348c2ecf20Sopenharmony_ci	int offset = SQUASHFS_XATTR_OFFSET(squashfs_i(inode)->xattr);
358c2ecf20Sopenharmony_ci	int count = squashfs_i(inode)->xattr_count;
368c2ecf20Sopenharmony_ci	size_t rest = buffer_size;
378c2ecf20Sopenharmony_ci	int err;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	/* check that the file system has xattrs */
408c2ecf20Sopenharmony_ci	if (msblk->xattr_id_table == NULL)
418c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	/* loop reading each xattr name */
448c2ecf20Sopenharmony_ci	while (count--) {
458c2ecf20Sopenharmony_ci		struct squashfs_xattr_entry entry;
468c2ecf20Sopenharmony_ci		struct squashfs_xattr_val val;
478c2ecf20Sopenharmony_ci		const struct xattr_handler *handler;
488c2ecf20Sopenharmony_ci		int name_size;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci		err = squashfs_read_metadata(sb, &entry, &start, &offset,
518c2ecf20Sopenharmony_ci							sizeof(entry));
528c2ecf20Sopenharmony_ci		if (err < 0)
538c2ecf20Sopenharmony_ci			goto failed;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci		name_size = le16_to_cpu(entry.size);
568c2ecf20Sopenharmony_ci		handler = squashfs_xattr_handler(le16_to_cpu(entry.type));
578c2ecf20Sopenharmony_ci		if (handler && (!handler->list || handler->list(d))) {
588c2ecf20Sopenharmony_ci			const char *prefix = handler->prefix ?: handler->name;
598c2ecf20Sopenharmony_ci			size_t prefix_size = strlen(prefix);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci			if (buffer) {
628c2ecf20Sopenharmony_ci				if (prefix_size + name_size + 1 > rest) {
638c2ecf20Sopenharmony_ci					err = -ERANGE;
648c2ecf20Sopenharmony_ci					goto failed;
658c2ecf20Sopenharmony_ci				}
668c2ecf20Sopenharmony_ci				memcpy(buffer, prefix, prefix_size);
678c2ecf20Sopenharmony_ci				buffer += prefix_size;
688c2ecf20Sopenharmony_ci			}
698c2ecf20Sopenharmony_ci			err = squashfs_read_metadata(sb, buffer, &start,
708c2ecf20Sopenharmony_ci				&offset, name_size);
718c2ecf20Sopenharmony_ci			if (err < 0)
728c2ecf20Sopenharmony_ci				goto failed;
738c2ecf20Sopenharmony_ci			if (buffer) {
748c2ecf20Sopenharmony_ci				buffer[name_size] = '\0';
758c2ecf20Sopenharmony_ci				buffer += name_size + 1;
768c2ecf20Sopenharmony_ci			}
778c2ecf20Sopenharmony_ci			rest -= prefix_size + name_size + 1;
788c2ecf20Sopenharmony_ci		} else  {
798c2ecf20Sopenharmony_ci			/* no handler or insuffficient privileges, so skip */
808c2ecf20Sopenharmony_ci			err = squashfs_read_metadata(sb, NULL, &start,
818c2ecf20Sopenharmony_ci				&offset, name_size);
828c2ecf20Sopenharmony_ci			if (err < 0)
838c2ecf20Sopenharmony_ci				goto failed;
848c2ecf20Sopenharmony_ci		}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci		/* skip remaining xattr entry */
888c2ecf20Sopenharmony_ci		err = squashfs_read_metadata(sb, &val, &start, &offset,
898c2ecf20Sopenharmony_ci						sizeof(val));
908c2ecf20Sopenharmony_ci		if (err < 0)
918c2ecf20Sopenharmony_ci			goto failed;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci		err = squashfs_read_metadata(sb, NULL, &start, &offset,
948c2ecf20Sopenharmony_ci						le32_to_cpu(val.vsize));
958c2ecf20Sopenharmony_ci		if (err < 0)
968c2ecf20Sopenharmony_ci			goto failed;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci	err = buffer_size - rest;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cifailed:
1018c2ecf20Sopenharmony_ci	return err;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic int squashfs_xattr_get(struct inode *inode, int name_index,
1068c2ecf20Sopenharmony_ci	const char *name, void *buffer, size_t buffer_size)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
1098c2ecf20Sopenharmony_ci	struct squashfs_sb_info *msblk = sb->s_fs_info;
1108c2ecf20Sopenharmony_ci	u64 start = SQUASHFS_XATTR_BLK(squashfs_i(inode)->xattr)
1118c2ecf20Sopenharmony_ci						 + msblk->xattr_table;
1128c2ecf20Sopenharmony_ci	int offset = SQUASHFS_XATTR_OFFSET(squashfs_i(inode)->xattr);
1138c2ecf20Sopenharmony_ci	int count = squashfs_i(inode)->xattr_count;
1148c2ecf20Sopenharmony_ci	int name_len = strlen(name);
1158c2ecf20Sopenharmony_ci	int err, vsize;
1168c2ecf20Sopenharmony_ci	char *target = kmalloc(name_len, GFP_KERNEL);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (target == NULL)
1198c2ecf20Sopenharmony_ci		return  -ENOMEM;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/* loop reading each xattr name */
1228c2ecf20Sopenharmony_ci	for (; count; count--) {
1238c2ecf20Sopenharmony_ci		struct squashfs_xattr_entry entry;
1248c2ecf20Sopenharmony_ci		struct squashfs_xattr_val val;
1258c2ecf20Sopenharmony_ci		int type, prefix, name_size;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci		err = squashfs_read_metadata(sb, &entry, &start, &offset,
1288c2ecf20Sopenharmony_ci							sizeof(entry));
1298c2ecf20Sopenharmony_ci		if (err < 0)
1308c2ecf20Sopenharmony_ci			goto failed;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci		name_size = le16_to_cpu(entry.size);
1338c2ecf20Sopenharmony_ci		type = le16_to_cpu(entry.type);
1348c2ecf20Sopenharmony_ci		prefix = type & SQUASHFS_XATTR_PREFIX_MASK;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci		if (prefix == name_index && name_size == name_len)
1378c2ecf20Sopenharmony_ci			err = squashfs_read_metadata(sb, target, &start,
1388c2ecf20Sopenharmony_ci						&offset, name_size);
1398c2ecf20Sopenharmony_ci		else
1408c2ecf20Sopenharmony_ci			err = squashfs_read_metadata(sb, NULL, &start,
1418c2ecf20Sopenharmony_ci						&offset, name_size);
1428c2ecf20Sopenharmony_ci		if (err < 0)
1438c2ecf20Sopenharmony_ci			goto failed;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci		if (prefix == name_index && name_size == name_len &&
1468c2ecf20Sopenharmony_ci					strncmp(target, name, name_size) == 0) {
1478c2ecf20Sopenharmony_ci			/* found xattr */
1488c2ecf20Sopenharmony_ci			if (type & SQUASHFS_XATTR_VALUE_OOL) {
1498c2ecf20Sopenharmony_ci				__le64 xattr_val;
1508c2ecf20Sopenharmony_ci				u64 xattr;
1518c2ecf20Sopenharmony_ci				/* val is a reference to the real location */
1528c2ecf20Sopenharmony_ci				err = squashfs_read_metadata(sb, &val, &start,
1538c2ecf20Sopenharmony_ci						&offset, sizeof(val));
1548c2ecf20Sopenharmony_ci				if (err < 0)
1558c2ecf20Sopenharmony_ci					goto failed;
1568c2ecf20Sopenharmony_ci				err = squashfs_read_metadata(sb, &xattr_val,
1578c2ecf20Sopenharmony_ci					&start, &offset, sizeof(xattr_val));
1588c2ecf20Sopenharmony_ci				if (err < 0)
1598c2ecf20Sopenharmony_ci					goto failed;
1608c2ecf20Sopenharmony_ci				xattr = le64_to_cpu(xattr_val);
1618c2ecf20Sopenharmony_ci				start = SQUASHFS_XATTR_BLK(xattr) +
1628c2ecf20Sopenharmony_ci							msblk->xattr_table;
1638c2ecf20Sopenharmony_ci				offset = SQUASHFS_XATTR_OFFSET(xattr);
1648c2ecf20Sopenharmony_ci			}
1658c2ecf20Sopenharmony_ci			/* read xattr value */
1668c2ecf20Sopenharmony_ci			err = squashfs_read_metadata(sb, &val, &start, &offset,
1678c2ecf20Sopenharmony_ci							sizeof(val));
1688c2ecf20Sopenharmony_ci			if (err < 0)
1698c2ecf20Sopenharmony_ci				goto failed;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci			vsize = le32_to_cpu(val.vsize);
1728c2ecf20Sopenharmony_ci			if (buffer) {
1738c2ecf20Sopenharmony_ci				if (vsize > buffer_size) {
1748c2ecf20Sopenharmony_ci					err = -ERANGE;
1758c2ecf20Sopenharmony_ci					goto failed;
1768c2ecf20Sopenharmony_ci				}
1778c2ecf20Sopenharmony_ci				err = squashfs_read_metadata(sb, buffer, &start,
1788c2ecf20Sopenharmony_ci					 &offset, vsize);
1798c2ecf20Sopenharmony_ci				if (err < 0)
1808c2ecf20Sopenharmony_ci					goto failed;
1818c2ecf20Sopenharmony_ci			}
1828c2ecf20Sopenharmony_ci			break;
1838c2ecf20Sopenharmony_ci		}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		/* no match, skip remaining xattr entry */
1868c2ecf20Sopenharmony_ci		err = squashfs_read_metadata(sb, &val, &start, &offset,
1878c2ecf20Sopenharmony_ci							sizeof(val));
1888c2ecf20Sopenharmony_ci		if (err < 0)
1898c2ecf20Sopenharmony_ci			goto failed;
1908c2ecf20Sopenharmony_ci		err = squashfs_read_metadata(sb, NULL, &start, &offset,
1918c2ecf20Sopenharmony_ci						le32_to_cpu(val.vsize));
1928c2ecf20Sopenharmony_ci		if (err < 0)
1938c2ecf20Sopenharmony_ci			goto failed;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci	err = count ? vsize : -ENODATA;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cifailed:
1988c2ecf20Sopenharmony_ci	kfree(target);
1998c2ecf20Sopenharmony_ci	return err;
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic int squashfs_xattr_handler_get(const struct xattr_handler *handler,
2048c2ecf20Sopenharmony_ci				      struct dentry *unused,
2058c2ecf20Sopenharmony_ci				      struct inode *inode,
2068c2ecf20Sopenharmony_ci				      const char *name,
2078c2ecf20Sopenharmony_ci				      void *buffer, size_t size)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	return squashfs_xattr_get(inode, handler->flags, name,
2108c2ecf20Sopenharmony_ci		buffer, size);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci/*
2148c2ecf20Sopenharmony_ci * User namespace support
2158c2ecf20Sopenharmony_ci */
2168c2ecf20Sopenharmony_cistatic const struct xattr_handler squashfs_xattr_user_handler = {
2178c2ecf20Sopenharmony_ci	.prefix	= XATTR_USER_PREFIX,
2188c2ecf20Sopenharmony_ci	.flags	= SQUASHFS_XATTR_USER,
2198c2ecf20Sopenharmony_ci	.get	= squashfs_xattr_handler_get
2208c2ecf20Sopenharmony_ci};
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci/*
2238c2ecf20Sopenharmony_ci * Trusted namespace support
2248c2ecf20Sopenharmony_ci */
2258c2ecf20Sopenharmony_cistatic bool squashfs_trusted_xattr_handler_list(struct dentry *d)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	return capable(CAP_SYS_ADMIN);
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic const struct xattr_handler squashfs_xattr_trusted_handler = {
2318c2ecf20Sopenharmony_ci	.prefix	= XATTR_TRUSTED_PREFIX,
2328c2ecf20Sopenharmony_ci	.flags	= SQUASHFS_XATTR_TRUSTED,
2338c2ecf20Sopenharmony_ci	.list	= squashfs_trusted_xattr_handler_list,
2348c2ecf20Sopenharmony_ci	.get	= squashfs_xattr_handler_get
2358c2ecf20Sopenharmony_ci};
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci/*
2388c2ecf20Sopenharmony_ci * Security namespace support
2398c2ecf20Sopenharmony_ci */
2408c2ecf20Sopenharmony_cistatic const struct xattr_handler squashfs_xattr_security_handler = {
2418c2ecf20Sopenharmony_ci	.prefix	= XATTR_SECURITY_PREFIX,
2428c2ecf20Sopenharmony_ci	.flags	= SQUASHFS_XATTR_SECURITY,
2438c2ecf20Sopenharmony_ci	.get	= squashfs_xattr_handler_get
2448c2ecf20Sopenharmony_ci};
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic const struct xattr_handler *squashfs_xattr_handler(int type)
2478c2ecf20Sopenharmony_ci{
2488c2ecf20Sopenharmony_ci	if (type & ~(SQUASHFS_XATTR_PREFIX_MASK | SQUASHFS_XATTR_VALUE_OOL))
2498c2ecf20Sopenharmony_ci		/* ignore unrecognised type */
2508c2ecf20Sopenharmony_ci		return NULL;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	switch (type & SQUASHFS_XATTR_PREFIX_MASK) {
2538c2ecf20Sopenharmony_ci	case SQUASHFS_XATTR_USER:
2548c2ecf20Sopenharmony_ci		return &squashfs_xattr_user_handler;
2558c2ecf20Sopenharmony_ci	case SQUASHFS_XATTR_TRUSTED:
2568c2ecf20Sopenharmony_ci		return &squashfs_xattr_trusted_handler;
2578c2ecf20Sopenharmony_ci	case SQUASHFS_XATTR_SECURITY:
2588c2ecf20Sopenharmony_ci		return &squashfs_xattr_security_handler;
2598c2ecf20Sopenharmony_ci	default:
2608c2ecf20Sopenharmony_ci		/* ignore unrecognised type */
2618c2ecf20Sopenharmony_ci		return NULL;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ciconst struct xattr_handler *squashfs_xattr_handlers[] = {
2668c2ecf20Sopenharmony_ci	&squashfs_xattr_user_handler,
2678c2ecf20Sopenharmony_ci	&squashfs_xattr_trusted_handler,
2688c2ecf20Sopenharmony_ci	&squashfs_xattr_security_handler,
2698c2ecf20Sopenharmony_ci	NULL
2708c2ecf20Sopenharmony_ci};
2718c2ecf20Sopenharmony_ci
272