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