162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/fs/hfsplus/xattr.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Vyacheslav Dubeyko <slava@dubeyko.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Logic of processing extended attributes
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "hfsplus_fs.h"
1162306a36Sopenharmony_ci#include <linux/nls.h>
1262306a36Sopenharmony_ci#include "xattr.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic int hfsplus_removexattr(struct inode *inode, const char *name);
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ciconst struct xattr_handler *hfsplus_xattr_handlers[] = {
1762306a36Sopenharmony_ci	&hfsplus_xattr_osx_handler,
1862306a36Sopenharmony_ci	&hfsplus_xattr_user_handler,
1962306a36Sopenharmony_ci	&hfsplus_xattr_trusted_handler,
2062306a36Sopenharmony_ci	&hfsplus_xattr_security_handler,
2162306a36Sopenharmony_ci	NULL
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic int strcmp_xattr_finder_info(const char *name)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	if (name) {
2762306a36Sopenharmony_ci		return strncmp(name, HFSPLUS_XATTR_FINDER_INFO_NAME,
2862306a36Sopenharmony_ci				sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME));
2962306a36Sopenharmony_ci	}
3062306a36Sopenharmony_ci	return -1;
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic int strcmp_xattr_acl(const char *name)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	if (name) {
3662306a36Sopenharmony_ci		return strncmp(name, HFSPLUS_XATTR_ACL_NAME,
3762306a36Sopenharmony_ci				sizeof(HFSPLUS_XATTR_ACL_NAME));
3862306a36Sopenharmony_ci	}
3962306a36Sopenharmony_ci	return -1;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic bool is_known_namespace(const char *name)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	if (strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) &&
4562306a36Sopenharmony_ci	    strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) &&
4662306a36Sopenharmony_ci	    strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) &&
4762306a36Sopenharmony_ci	    strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN))
4862306a36Sopenharmony_ci		return false;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	return true;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic void hfsplus_init_header_node(struct inode *attr_file,
5462306a36Sopenharmony_ci					u32 clump_size,
5562306a36Sopenharmony_ci					char *buf, u16 node_size)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	struct hfs_bnode_desc *desc;
5862306a36Sopenharmony_ci	struct hfs_btree_header_rec *head;
5962306a36Sopenharmony_ci	u16 offset;
6062306a36Sopenharmony_ci	__be16 *rec_offsets;
6162306a36Sopenharmony_ci	u32 hdr_node_map_rec_bits;
6262306a36Sopenharmony_ci	char *bmp;
6362306a36Sopenharmony_ci	u32 used_nodes;
6462306a36Sopenharmony_ci	u32 used_bmp_bytes;
6562306a36Sopenharmony_ci	u64 tmp;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	hfs_dbg(ATTR_MOD, "init_hdr_attr_file: clump %u, node_size %u\n",
6862306a36Sopenharmony_ci		clump_size, node_size);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/* The end of the node contains list of record offsets */
7162306a36Sopenharmony_ci	rec_offsets = (__be16 *)(buf + node_size);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	desc = (struct hfs_bnode_desc *)buf;
7462306a36Sopenharmony_ci	desc->type = HFS_NODE_HEADER;
7562306a36Sopenharmony_ci	desc->num_recs = cpu_to_be16(HFSPLUS_BTREE_HDR_NODE_RECS_COUNT);
7662306a36Sopenharmony_ci	offset = sizeof(struct hfs_bnode_desc);
7762306a36Sopenharmony_ci	*--rec_offsets = cpu_to_be16(offset);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	head = (struct hfs_btree_header_rec *)(buf + offset);
8062306a36Sopenharmony_ci	head->node_size = cpu_to_be16(node_size);
8162306a36Sopenharmony_ci	tmp = i_size_read(attr_file);
8262306a36Sopenharmony_ci	do_div(tmp, node_size);
8362306a36Sopenharmony_ci	head->node_count = cpu_to_be32(tmp);
8462306a36Sopenharmony_ci	head->free_nodes = cpu_to_be32(be32_to_cpu(head->node_count) - 1);
8562306a36Sopenharmony_ci	head->clump_size = cpu_to_be32(clump_size);
8662306a36Sopenharmony_ci	head->attributes |= cpu_to_be32(HFS_TREE_BIGKEYS | HFS_TREE_VARIDXKEYS);
8762306a36Sopenharmony_ci	head->max_key_len = cpu_to_be16(HFSPLUS_ATTR_KEYLEN - sizeof(u16));
8862306a36Sopenharmony_ci	offset += sizeof(struct hfs_btree_header_rec);
8962306a36Sopenharmony_ci	*--rec_offsets = cpu_to_be16(offset);
9062306a36Sopenharmony_ci	offset += HFSPLUS_BTREE_HDR_USER_BYTES;
9162306a36Sopenharmony_ci	*--rec_offsets = cpu_to_be16(offset);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	hdr_node_map_rec_bits = 8 * (node_size - offset - (4 * sizeof(u16)));
9462306a36Sopenharmony_ci	if (be32_to_cpu(head->node_count) > hdr_node_map_rec_bits) {
9562306a36Sopenharmony_ci		u32 map_node_bits;
9662306a36Sopenharmony_ci		u32 map_nodes;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci		desc->next = cpu_to_be32(be32_to_cpu(head->leaf_tail) + 1);
9962306a36Sopenharmony_ci		map_node_bits = 8 * (node_size - sizeof(struct hfs_bnode_desc) -
10062306a36Sopenharmony_ci					(2 * sizeof(u16)) - 2);
10162306a36Sopenharmony_ci		map_nodes = (be32_to_cpu(head->node_count) -
10262306a36Sopenharmony_ci				hdr_node_map_rec_bits +
10362306a36Sopenharmony_ci				(map_node_bits - 1)) / map_node_bits;
10462306a36Sopenharmony_ci		be32_add_cpu(&head->free_nodes, 0 - map_nodes);
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	bmp = buf + offset;
10862306a36Sopenharmony_ci	used_nodes =
10962306a36Sopenharmony_ci		be32_to_cpu(head->node_count) - be32_to_cpu(head->free_nodes);
11062306a36Sopenharmony_ci	used_bmp_bytes = used_nodes / 8;
11162306a36Sopenharmony_ci	if (used_bmp_bytes) {
11262306a36Sopenharmony_ci		memset(bmp, 0xFF, used_bmp_bytes);
11362306a36Sopenharmony_ci		bmp += used_bmp_bytes;
11462306a36Sopenharmony_ci		used_nodes %= 8;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci	*bmp = ~(0xFF >> used_nodes);
11762306a36Sopenharmony_ci	offset += hdr_node_map_rec_bits / 8;
11862306a36Sopenharmony_ci	*--rec_offsets = cpu_to_be16(offset);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic int hfsplus_create_attributes_file(struct super_block *sb)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	int err = 0;
12462306a36Sopenharmony_ci	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
12562306a36Sopenharmony_ci	struct inode *attr_file;
12662306a36Sopenharmony_ci	struct hfsplus_inode_info *hip;
12762306a36Sopenharmony_ci	u32 clump_size;
12862306a36Sopenharmony_ci	u16 node_size = HFSPLUS_ATTR_TREE_NODE_SIZE;
12962306a36Sopenharmony_ci	char *buf;
13062306a36Sopenharmony_ci	int index, written;
13162306a36Sopenharmony_ci	struct address_space *mapping;
13262306a36Sopenharmony_ci	struct page *page;
13362306a36Sopenharmony_ci	int old_state = HFSPLUS_EMPTY_ATTR_TREE;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	hfs_dbg(ATTR_MOD, "create_attr_file: ino %d\n", HFSPLUS_ATTR_CNID);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cicheck_attr_tree_state_again:
13862306a36Sopenharmony_ci	switch (atomic_read(&sbi->attr_tree_state)) {
13962306a36Sopenharmony_ci	case HFSPLUS_EMPTY_ATTR_TREE:
14062306a36Sopenharmony_ci		if (old_state != atomic_cmpxchg(&sbi->attr_tree_state,
14162306a36Sopenharmony_ci						old_state,
14262306a36Sopenharmony_ci						HFSPLUS_CREATING_ATTR_TREE))
14362306a36Sopenharmony_ci			goto check_attr_tree_state_again;
14462306a36Sopenharmony_ci		break;
14562306a36Sopenharmony_ci	case HFSPLUS_CREATING_ATTR_TREE:
14662306a36Sopenharmony_ci		/*
14762306a36Sopenharmony_ci		 * This state means that another thread is in process
14862306a36Sopenharmony_ci		 * of AttributesFile creation. Theoretically, it is
14962306a36Sopenharmony_ci		 * possible to be here. But really __setxattr() method
15062306a36Sopenharmony_ci		 * first of all calls hfs_find_init() for lookup in
15162306a36Sopenharmony_ci		 * B-tree of CatalogFile. This method locks mutex of
15262306a36Sopenharmony_ci		 * CatalogFile's B-tree. As a result, if some thread
15362306a36Sopenharmony_ci		 * is inside AttributedFile creation operation then
15462306a36Sopenharmony_ci		 * another threads will be waiting unlocking of
15562306a36Sopenharmony_ci		 * CatalogFile's B-tree's mutex. However, if code will
15662306a36Sopenharmony_ci		 * change then we will return error code (-EAGAIN) from
15762306a36Sopenharmony_ci		 * here. Really, it means that first try to set of xattr
15862306a36Sopenharmony_ci		 * fails with error but second attempt will have success.
15962306a36Sopenharmony_ci		 */
16062306a36Sopenharmony_ci		return -EAGAIN;
16162306a36Sopenharmony_ci	case HFSPLUS_VALID_ATTR_TREE:
16262306a36Sopenharmony_ci		return 0;
16362306a36Sopenharmony_ci	case HFSPLUS_FAILED_ATTR_TREE:
16462306a36Sopenharmony_ci		return -EOPNOTSUPP;
16562306a36Sopenharmony_ci	default:
16662306a36Sopenharmony_ci		BUG();
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	attr_file = hfsplus_iget(sb, HFSPLUS_ATTR_CNID);
17062306a36Sopenharmony_ci	if (IS_ERR(attr_file)) {
17162306a36Sopenharmony_ci		pr_err("failed to load attributes file\n");
17262306a36Sopenharmony_ci		return PTR_ERR(attr_file);
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	BUG_ON(i_size_read(attr_file) != 0);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	hip = HFSPLUS_I(attr_file);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	clump_size = hfsplus_calc_btree_clump_size(sb->s_blocksize,
18062306a36Sopenharmony_ci						    node_size,
18162306a36Sopenharmony_ci						    sbi->sect_count,
18262306a36Sopenharmony_ci						    HFSPLUS_ATTR_CNID);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	mutex_lock(&hip->extents_lock);
18562306a36Sopenharmony_ci	hip->clump_blocks = clump_size >> sbi->alloc_blksz_shift;
18662306a36Sopenharmony_ci	mutex_unlock(&hip->extents_lock);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (sbi->free_blocks <= (hip->clump_blocks << 1)) {
18962306a36Sopenharmony_ci		err = -ENOSPC;
19062306a36Sopenharmony_ci		goto end_attr_file_creation;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	while (hip->alloc_blocks < hip->clump_blocks) {
19462306a36Sopenharmony_ci		err = hfsplus_file_extend(attr_file, false);
19562306a36Sopenharmony_ci		if (unlikely(err)) {
19662306a36Sopenharmony_ci			pr_err("failed to extend attributes file\n");
19762306a36Sopenharmony_ci			goto end_attr_file_creation;
19862306a36Sopenharmony_ci		}
19962306a36Sopenharmony_ci		hip->phys_size = attr_file->i_size =
20062306a36Sopenharmony_ci			(loff_t)hip->alloc_blocks << sbi->alloc_blksz_shift;
20162306a36Sopenharmony_ci		hip->fs_blocks = hip->alloc_blocks << sbi->fs_shift;
20262306a36Sopenharmony_ci		inode_set_bytes(attr_file, attr_file->i_size);
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	buf = kzalloc(node_size, GFP_NOFS);
20662306a36Sopenharmony_ci	if (!buf) {
20762306a36Sopenharmony_ci		err = -ENOMEM;
20862306a36Sopenharmony_ci		goto end_attr_file_creation;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	hfsplus_init_header_node(attr_file, clump_size, buf, node_size);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	mapping = attr_file->i_mapping;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	index = 0;
21662306a36Sopenharmony_ci	written = 0;
21762306a36Sopenharmony_ci	for (; written < node_size; index++, written += PAGE_SIZE) {
21862306a36Sopenharmony_ci		void *kaddr;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		page = read_mapping_page(mapping, index, NULL);
22162306a36Sopenharmony_ci		if (IS_ERR(page)) {
22262306a36Sopenharmony_ci			err = PTR_ERR(page);
22362306a36Sopenharmony_ci			goto failed_header_node_init;
22462306a36Sopenharmony_ci		}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci		kaddr = kmap_atomic(page);
22762306a36Sopenharmony_ci		memcpy(kaddr, buf + written,
22862306a36Sopenharmony_ci			min_t(size_t, PAGE_SIZE, node_size - written));
22962306a36Sopenharmony_ci		kunmap_atomic(kaddr);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci		set_page_dirty(page);
23262306a36Sopenharmony_ci		put_page(page);
23362306a36Sopenharmony_ci	}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	hfsplus_mark_inode_dirty(attr_file, HFSPLUS_I_ATTR_DIRTY);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID);
23862306a36Sopenharmony_ci	if (!sbi->attr_tree)
23962306a36Sopenharmony_ci		pr_err("failed to load attributes file\n");
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cifailed_header_node_init:
24262306a36Sopenharmony_ci	kfree(buf);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ciend_attr_file_creation:
24562306a36Sopenharmony_ci	iput(attr_file);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	if (!err)
24862306a36Sopenharmony_ci		atomic_set(&sbi->attr_tree_state, HFSPLUS_VALID_ATTR_TREE);
24962306a36Sopenharmony_ci	else if (err == -ENOSPC)
25062306a36Sopenharmony_ci		atomic_set(&sbi->attr_tree_state, HFSPLUS_EMPTY_ATTR_TREE);
25162306a36Sopenharmony_ci	else
25262306a36Sopenharmony_ci		atomic_set(&sbi->attr_tree_state, HFSPLUS_FAILED_ATTR_TREE);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return err;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ciint __hfsplus_setxattr(struct inode *inode, const char *name,
25862306a36Sopenharmony_ci			const void *value, size_t size, int flags)
25962306a36Sopenharmony_ci{
26062306a36Sopenharmony_ci	int err;
26162306a36Sopenharmony_ci	struct hfs_find_data cat_fd;
26262306a36Sopenharmony_ci	hfsplus_cat_entry entry;
26362306a36Sopenharmony_ci	u16 cat_entry_flags, cat_entry_type;
26462306a36Sopenharmony_ci	u16 folder_finderinfo_len = sizeof(struct DInfo) +
26562306a36Sopenharmony_ci					sizeof(struct DXInfo);
26662306a36Sopenharmony_ci	u16 file_finderinfo_len = sizeof(struct FInfo) +
26762306a36Sopenharmony_ci					sizeof(struct FXInfo);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	if ((!S_ISREG(inode->i_mode) &&
27062306a36Sopenharmony_ci			!S_ISDIR(inode->i_mode)) ||
27162306a36Sopenharmony_ci				HFSPLUS_IS_RSRC(inode))
27262306a36Sopenharmony_ci		return -EOPNOTSUPP;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	if (value == NULL)
27562306a36Sopenharmony_ci		return hfsplus_removexattr(inode, name);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
27862306a36Sopenharmony_ci	if (err) {
27962306a36Sopenharmony_ci		pr_err("can't init xattr find struct\n");
28062306a36Sopenharmony_ci		return err;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
28462306a36Sopenharmony_ci	if (err) {
28562306a36Sopenharmony_ci		pr_err("catalog searching failed\n");
28662306a36Sopenharmony_ci		goto end_setxattr;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (!strcmp_xattr_finder_info(name)) {
29062306a36Sopenharmony_ci		if (flags & XATTR_CREATE) {
29162306a36Sopenharmony_ci			pr_err("xattr exists yet\n");
29262306a36Sopenharmony_ci			err = -EOPNOTSUPP;
29362306a36Sopenharmony_ci			goto end_setxattr;
29462306a36Sopenharmony_ci		}
29562306a36Sopenharmony_ci		hfs_bnode_read(cat_fd.bnode, &entry, cat_fd.entryoffset,
29662306a36Sopenharmony_ci					sizeof(hfsplus_cat_entry));
29762306a36Sopenharmony_ci		if (be16_to_cpu(entry.type) == HFSPLUS_FOLDER) {
29862306a36Sopenharmony_ci			if (size == folder_finderinfo_len) {
29962306a36Sopenharmony_ci				memcpy(&entry.folder.info, value,
30062306a36Sopenharmony_ci						folder_finderinfo_len);
30162306a36Sopenharmony_ci				hfs_bnode_write(cat_fd.bnode, &entry,
30262306a36Sopenharmony_ci					cat_fd.entryoffset,
30362306a36Sopenharmony_ci					sizeof(struct hfsplus_cat_folder));
30462306a36Sopenharmony_ci				hfsplus_mark_inode_dirty(inode,
30562306a36Sopenharmony_ci						HFSPLUS_I_CAT_DIRTY);
30662306a36Sopenharmony_ci			} else {
30762306a36Sopenharmony_ci				err = -ERANGE;
30862306a36Sopenharmony_ci				goto end_setxattr;
30962306a36Sopenharmony_ci			}
31062306a36Sopenharmony_ci		} else if (be16_to_cpu(entry.type) == HFSPLUS_FILE) {
31162306a36Sopenharmony_ci			if (size == file_finderinfo_len) {
31262306a36Sopenharmony_ci				memcpy(&entry.file.info, value,
31362306a36Sopenharmony_ci						file_finderinfo_len);
31462306a36Sopenharmony_ci				hfs_bnode_write(cat_fd.bnode, &entry,
31562306a36Sopenharmony_ci					cat_fd.entryoffset,
31662306a36Sopenharmony_ci					sizeof(struct hfsplus_cat_file));
31762306a36Sopenharmony_ci				hfsplus_mark_inode_dirty(inode,
31862306a36Sopenharmony_ci						HFSPLUS_I_CAT_DIRTY);
31962306a36Sopenharmony_ci			} else {
32062306a36Sopenharmony_ci				err = -ERANGE;
32162306a36Sopenharmony_ci				goto end_setxattr;
32262306a36Sopenharmony_ci			}
32362306a36Sopenharmony_ci		} else {
32462306a36Sopenharmony_ci			err = -EOPNOTSUPP;
32562306a36Sopenharmony_ci			goto end_setxattr;
32662306a36Sopenharmony_ci		}
32762306a36Sopenharmony_ci		goto end_setxattr;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (!HFSPLUS_SB(inode->i_sb)->attr_tree) {
33162306a36Sopenharmony_ci		err = hfsplus_create_attributes_file(inode->i_sb);
33262306a36Sopenharmony_ci		if (unlikely(err))
33362306a36Sopenharmony_ci			goto end_setxattr;
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (hfsplus_attr_exists(inode, name)) {
33762306a36Sopenharmony_ci		if (flags & XATTR_CREATE) {
33862306a36Sopenharmony_ci			pr_err("xattr exists yet\n");
33962306a36Sopenharmony_ci			err = -EOPNOTSUPP;
34062306a36Sopenharmony_ci			goto end_setxattr;
34162306a36Sopenharmony_ci		}
34262306a36Sopenharmony_ci		err = hfsplus_delete_attr(inode, name);
34362306a36Sopenharmony_ci		if (err)
34462306a36Sopenharmony_ci			goto end_setxattr;
34562306a36Sopenharmony_ci		err = hfsplus_create_attr(inode, name, value, size);
34662306a36Sopenharmony_ci		if (err)
34762306a36Sopenharmony_ci			goto end_setxattr;
34862306a36Sopenharmony_ci	} else {
34962306a36Sopenharmony_ci		if (flags & XATTR_REPLACE) {
35062306a36Sopenharmony_ci			pr_err("cannot replace xattr\n");
35162306a36Sopenharmony_ci			err = -EOPNOTSUPP;
35262306a36Sopenharmony_ci			goto end_setxattr;
35362306a36Sopenharmony_ci		}
35462306a36Sopenharmony_ci		err = hfsplus_create_attr(inode, name, value, size);
35562306a36Sopenharmony_ci		if (err)
35662306a36Sopenharmony_ci			goto end_setxattr;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
36062306a36Sopenharmony_ci	if (cat_entry_type == HFSPLUS_FOLDER) {
36162306a36Sopenharmony_ci		cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
36262306a36Sopenharmony_ci				    cat_fd.entryoffset +
36362306a36Sopenharmony_ci				    offsetof(struct hfsplus_cat_folder, flags));
36462306a36Sopenharmony_ci		cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
36562306a36Sopenharmony_ci		if (!strcmp_xattr_acl(name))
36662306a36Sopenharmony_ci			cat_entry_flags |= HFSPLUS_ACL_EXISTS;
36762306a36Sopenharmony_ci		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
36862306a36Sopenharmony_ci				offsetof(struct hfsplus_cat_folder, flags),
36962306a36Sopenharmony_ci				cat_entry_flags);
37062306a36Sopenharmony_ci		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
37162306a36Sopenharmony_ci	} else if (cat_entry_type == HFSPLUS_FILE) {
37262306a36Sopenharmony_ci		cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
37362306a36Sopenharmony_ci				    cat_fd.entryoffset +
37462306a36Sopenharmony_ci				    offsetof(struct hfsplus_cat_file, flags));
37562306a36Sopenharmony_ci		cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
37662306a36Sopenharmony_ci		if (!strcmp_xattr_acl(name))
37762306a36Sopenharmony_ci			cat_entry_flags |= HFSPLUS_ACL_EXISTS;
37862306a36Sopenharmony_ci		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
37962306a36Sopenharmony_ci				    offsetof(struct hfsplus_cat_file, flags),
38062306a36Sopenharmony_ci				    cat_entry_flags);
38162306a36Sopenharmony_ci		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
38262306a36Sopenharmony_ci	} else {
38362306a36Sopenharmony_ci		pr_err("invalid catalog entry type\n");
38462306a36Sopenharmony_ci		err = -EIO;
38562306a36Sopenharmony_ci		goto end_setxattr;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ciend_setxattr:
38962306a36Sopenharmony_ci	hfs_find_exit(&cat_fd);
39062306a36Sopenharmony_ci	return err;
39162306a36Sopenharmony_ci}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic int name_len(const char *xattr_name, int xattr_name_len)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	int len = xattr_name_len + 1;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	if (!is_known_namespace(xattr_name))
39862306a36Sopenharmony_ci		len += XATTR_MAC_OSX_PREFIX_LEN;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	return len;
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic int copy_name(char *buffer, const char *xattr_name, int name_len)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	int len = name_len;
40662306a36Sopenharmony_ci	int offset = 0;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (!is_known_namespace(xattr_name)) {
40962306a36Sopenharmony_ci		memcpy(buffer, XATTR_MAC_OSX_PREFIX, XATTR_MAC_OSX_PREFIX_LEN);
41062306a36Sopenharmony_ci		offset += XATTR_MAC_OSX_PREFIX_LEN;
41162306a36Sopenharmony_ci		len += XATTR_MAC_OSX_PREFIX_LEN;
41262306a36Sopenharmony_ci	}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	strncpy(buffer + offset, xattr_name, name_len);
41562306a36Sopenharmony_ci	memset(buffer + offset + name_len, 0, 1);
41662306a36Sopenharmony_ci	len += 1;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	return len;
41962306a36Sopenharmony_ci}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ciint hfsplus_setxattr(struct inode *inode, const char *name,
42262306a36Sopenharmony_ci		     const void *value, size_t size, int flags,
42362306a36Sopenharmony_ci		     const char *prefix, size_t prefixlen)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	char *xattr_name;
42662306a36Sopenharmony_ci	int res;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1,
42962306a36Sopenharmony_ci		GFP_KERNEL);
43062306a36Sopenharmony_ci	if (!xattr_name)
43162306a36Sopenharmony_ci		return -ENOMEM;
43262306a36Sopenharmony_ci	strcpy(xattr_name, prefix);
43362306a36Sopenharmony_ci	strcpy(xattr_name + prefixlen, name);
43462306a36Sopenharmony_ci	res = __hfsplus_setxattr(inode, xattr_name, value, size, flags);
43562306a36Sopenharmony_ci	kfree(xattr_name);
43662306a36Sopenharmony_ci	return res;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic ssize_t hfsplus_getxattr_finder_info(struct inode *inode,
44062306a36Sopenharmony_ci						void *value, size_t size)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	ssize_t res = 0;
44362306a36Sopenharmony_ci	struct hfs_find_data fd;
44462306a36Sopenharmony_ci	u16 entry_type;
44562306a36Sopenharmony_ci	u16 folder_rec_len = sizeof(struct DInfo) + sizeof(struct DXInfo);
44662306a36Sopenharmony_ci	u16 file_rec_len = sizeof(struct FInfo) + sizeof(struct FXInfo);
44762306a36Sopenharmony_ci	u16 record_len = max(folder_rec_len, file_rec_len);
44862306a36Sopenharmony_ci	u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
44962306a36Sopenharmony_ci	u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	if (size >= record_len) {
45262306a36Sopenharmony_ci		res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
45362306a36Sopenharmony_ci		if (res) {
45462306a36Sopenharmony_ci			pr_err("can't init xattr find struct\n");
45562306a36Sopenharmony_ci			return res;
45662306a36Sopenharmony_ci		}
45762306a36Sopenharmony_ci		res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
45862306a36Sopenharmony_ci		if (res)
45962306a36Sopenharmony_ci			goto end_getxattr_finder_info;
46062306a36Sopenharmony_ci		entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci		if (entry_type == HFSPLUS_FOLDER) {
46362306a36Sopenharmony_ci			hfs_bnode_read(fd.bnode, folder_finder_info,
46462306a36Sopenharmony_ci				fd.entryoffset +
46562306a36Sopenharmony_ci				offsetof(struct hfsplus_cat_folder, user_info),
46662306a36Sopenharmony_ci				folder_rec_len);
46762306a36Sopenharmony_ci			memcpy(value, folder_finder_info, folder_rec_len);
46862306a36Sopenharmony_ci			res = folder_rec_len;
46962306a36Sopenharmony_ci		} else if (entry_type == HFSPLUS_FILE) {
47062306a36Sopenharmony_ci			hfs_bnode_read(fd.bnode, file_finder_info,
47162306a36Sopenharmony_ci				fd.entryoffset +
47262306a36Sopenharmony_ci				offsetof(struct hfsplus_cat_file, user_info),
47362306a36Sopenharmony_ci				file_rec_len);
47462306a36Sopenharmony_ci			memcpy(value, file_finder_info, file_rec_len);
47562306a36Sopenharmony_ci			res = file_rec_len;
47662306a36Sopenharmony_ci		} else {
47762306a36Sopenharmony_ci			res = -EOPNOTSUPP;
47862306a36Sopenharmony_ci			goto end_getxattr_finder_info;
47962306a36Sopenharmony_ci		}
48062306a36Sopenharmony_ci	} else
48162306a36Sopenharmony_ci		res = size ? -ERANGE : record_len;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ciend_getxattr_finder_info:
48462306a36Sopenharmony_ci	if (size >= record_len)
48562306a36Sopenharmony_ci		hfs_find_exit(&fd);
48662306a36Sopenharmony_ci	return res;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cissize_t __hfsplus_getxattr(struct inode *inode, const char *name,
49062306a36Sopenharmony_ci			 void *value, size_t size)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	struct hfs_find_data fd;
49362306a36Sopenharmony_ci	hfsplus_attr_entry *entry;
49462306a36Sopenharmony_ci	__be32 xattr_record_type;
49562306a36Sopenharmony_ci	u32 record_type;
49662306a36Sopenharmony_ci	u16 record_length = 0;
49762306a36Sopenharmony_ci	ssize_t res;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if ((!S_ISREG(inode->i_mode) &&
50062306a36Sopenharmony_ci			!S_ISDIR(inode->i_mode)) ||
50162306a36Sopenharmony_ci				HFSPLUS_IS_RSRC(inode))
50262306a36Sopenharmony_ci		return -EOPNOTSUPP;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	if (!strcmp_xattr_finder_info(name))
50562306a36Sopenharmony_ci		return hfsplus_getxattr_finder_info(inode, value, size);
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
50862306a36Sopenharmony_ci		return -EOPNOTSUPP;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	entry = hfsplus_alloc_attr_entry();
51162306a36Sopenharmony_ci	if (!entry) {
51262306a36Sopenharmony_ci		pr_err("can't allocate xattr entry\n");
51362306a36Sopenharmony_ci		return -ENOMEM;
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
51762306a36Sopenharmony_ci	if (res) {
51862306a36Sopenharmony_ci		pr_err("can't init xattr find struct\n");
51962306a36Sopenharmony_ci		goto failed_getxattr_init;
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	res = hfsplus_find_attr(inode->i_sb, inode->i_ino, name, &fd);
52362306a36Sopenharmony_ci	if (res) {
52462306a36Sopenharmony_ci		if (res == -ENOENT)
52562306a36Sopenharmony_ci			res = -ENODATA;
52662306a36Sopenharmony_ci		else
52762306a36Sopenharmony_ci			pr_err("xattr searching failed\n");
52862306a36Sopenharmony_ci		goto out;
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	hfs_bnode_read(fd.bnode, &xattr_record_type,
53262306a36Sopenharmony_ci			fd.entryoffset, sizeof(xattr_record_type));
53362306a36Sopenharmony_ci	record_type = be32_to_cpu(xattr_record_type);
53462306a36Sopenharmony_ci	if (record_type == HFSPLUS_ATTR_INLINE_DATA) {
53562306a36Sopenharmony_ci		record_length = hfs_bnode_read_u16(fd.bnode,
53662306a36Sopenharmony_ci				fd.entryoffset +
53762306a36Sopenharmony_ci				offsetof(struct hfsplus_attr_inline_data,
53862306a36Sopenharmony_ci				length));
53962306a36Sopenharmony_ci		if (record_length > HFSPLUS_MAX_INLINE_DATA_SIZE) {
54062306a36Sopenharmony_ci			pr_err("invalid xattr record size\n");
54162306a36Sopenharmony_ci			res = -EIO;
54262306a36Sopenharmony_ci			goto out;
54362306a36Sopenharmony_ci		}
54462306a36Sopenharmony_ci	} else if (record_type == HFSPLUS_ATTR_FORK_DATA ||
54562306a36Sopenharmony_ci			record_type == HFSPLUS_ATTR_EXTENTS) {
54662306a36Sopenharmony_ci		pr_err("only inline data xattr are supported\n");
54762306a36Sopenharmony_ci		res = -EOPNOTSUPP;
54862306a36Sopenharmony_ci		goto out;
54962306a36Sopenharmony_ci	} else {
55062306a36Sopenharmony_ci		pr_err("invalid xattr record\n");
55162306a36Sopenharmony_ci		res = -EIO;
55262306a36Sopenharmony_ci		goto out;
55362306a36Sopenharmony_ci	}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	if (size) {
55662306a36Sopenharmony_ci		hfs_bnode_read(fd.bnode, entry, fd.entryoffset,
55762306a36Sopenharmony_ci				offsetof(struct hfsplus_attr_inline_data,
55862306a36Sopenharmony_ci					raw_bytes) + record_length);
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	if (size >= record_length) {
56262306a36Sopenharmony_ci		memcpy(value, entry->inline_data.raw_bytes, record_length);
56362306a36Sopenharmony_ci		res = record_length;
56462306a36Sopenharmony_ci	} else
56562306a36Sopenharmony_ci		res = size ? -ERANGE : record_length;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ciout:
56862306a36Sopenharmony_ci	hfs_find_exit(&fd);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_cifailed_getxattr_init:
57162306a36Sopenharmony_ci	hfsplus_destroy_attr_entry(entry);
57262306a36Sopenharmony_ci	return res;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cissize_t hfsplus_getxattr(struct inode *inode, const char *name,
57662306a36Sopenharmony_ci			 void *value, size_t size,
57762306a36Sopenharmony_ci			 const char *prefix, size_t prefixlen)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	int res;
58062306a36Sopenharmony_ci	char *xattr_name;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1,
58362306a36Sopenharmony_ci			     GFP_KERNEL);
58462306a36Sopenharmony_ci	if (!xattr_name)
58562306a36Sopenharmony_ci		return -ENOMEM;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	strcpy(xattr_name, prefix);
58862306a36Sopenharmony_ci	strcpy(xattr_name + prefixlen, name);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	res = __hfsplus_getxattr(inode, xattr_name, value, size);
59162306a36Sopenharmony_ci	kfree(xattr_name);
59262306a36Sopenharmony_ci	return res;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cistatic inline int can_list(const char *xattr_name)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	if (!xattr_name)
59962306a36Sopenharmony_ci		return 0;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	return strncmp(xattr_name, XATTR_TRUSTED_PREFIX,
60262306a36Sopenharmony_ci			XATTR_TRUSTED_PREFIX_LEN) ||
60362306a36Sopenharmony_ci				capable(CAP_SYS_ADMIN);
60462306a36Sopenharmony_ci}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cistatic ssize_t hfsplus_listxattr_finder_info(struct dentry *dentry,
60762306a36Sopenharmony_ci						char *buffer, size_t size)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	ssize_t res;
61062306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
61162306a36Sopenharmony_ci	struct hfs_find_data fd;
61262306a36Sopenharmony_ci	u16 entry_type;
61362306a36Sopenharmony_ci	u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
61462306a36Sopenharmony_ci	u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
61562306a36Sopenharmony_ci	unsigned long len, found_bit;
61662306a36Sopenharmony_ci	int xattr_name_len, symbols_count;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
61962306a36Sopenharmony_ci	if (res) {
62062306a36Sopenharmony_ci		pr_err("can't init xattr find struct\n");
62162306a36Sopenharmony_ci		return res;
62262306a36Sopenharmony_ci	}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
62562306a36Sopenharmony_ci	if (res)
62662306a36Sopenharmony_ci		goto end_listxattr_finder_info;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
62962306a36Sopenharmony_ci	if (entry_type == HFSPLUS_FOLDER) {
63062306a36Sopenharmony_ci		len = sizeof(struct DInfo) + sizeof(struct DXInfo);
63162306a36Sopenharmony_ci		hfs_bnode_read(fd.bnode, folder_finder_info,
63262306a36Sopenharmony_ci				fd.entryoffset +
63362306a36Sopenharmony_ci				offsetof(struct hfsplus_cat_folder, user_info),
63462306a36Sopenharmony_ci				len);
63562306a36Sopenharmony_ci		found_bit = find_first_bit((void *)folder_finder_info, len*8);
63662306a36Sopenharmony_ci	} else if (entry_type == HFSPLUS_FILE) {
63762306a36Sopenharmony_ci		len = sizeof(struct FInfo) + sizeof(struct FXInfo);
63862306a36Sopenharmony_ci		hfs_bnode_read(fd.bnode, file_finder_info,
63962306a36Sopenharmony_ci				fd.entryoffset +
64062306a36Sopenharmony_ci				offsetof(struct hfsplus_cat_file, user_info),
64162306a36Sopenharmony_ci				len);
64262306a36Sopenharmony_ci		found_bit = find_first_bit((void *)file_finder_info, len*8);
64362306a36Sopenharmony_ci	} else {
64462306a36Sopenharmony_ci		res = -EOPNOTSUPP;
64562306a36Sopenharmony_ci		goto end_listxattr_finder_info;
64662306a36Sopenharmony_ci	}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	if (found_bit >= (len*8))
64962306a36Sopenharmony_ci		res = 0;
65062306a36Sopenharmony_ci	else {
65162306a36Sopenharmony_ci		symbols_count = sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME) - 1;
65262306a36Sopenharmony_ci		xattr_name_len =
65362306a36Sopenharmony_ci			name_len(HFSPLUS_XATTR_FINDER_INFO_NAME, symbols_count);
65462306a36Sopenharmony_ci		if (!buffer || !size) {
65562306a36Sopenharmony_ci			if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME))
65662306a36Sopenharmony_ci				res = xattr_name_len;
65762306a36Sopenharmony_ci		} else if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME)) {
65862306a36Sopenharmony_ci			if (size < xattr_name_len)
65962306a36Sopenharmony_ci				res = -ERANGE;
66062306a36Sopenharmony_ci			else {
66162306a36Sopenharmony_ci				res = copy_name(buffer,
66262306a36Sopenharmony_ci						HFSPLUS_XATTR_FINDER_INFO_NAME,
66362306a36Sopenharmony_ci						symbols_count);
66462306a36Sopenharmony_ci			}
66562306a36Sopenharmony_ci		}
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ciend_listxattr_finder_info:
66962306a36Sopenharmony_ci	hfs_find_exit(&fd);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	return res;
67262306a36Sopenharmony_ci}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_cissize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	ssize_t err;
67762306a36Sopenharmony_ci	ssize_t res;
67862306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
67962306a36Sopenharmony_ci	struct hfs_find_data fd;
68062306a36Sopenharmony_ci	struct hfsplus_attr_key attr_key;
68162306a36Sopenharmony_ci	char *strbuf;
68262306a36Sopenharmony_ci	int xattr_name_len;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	if ((!S_ISREG(inode->i_mode) &&
68562306a36Sopenharmony_ci			!S_ISDIR(inode->i_mode)) ||
68662306a36Sopenharmony_ci				HFSPLUS_IS_RSRC(inode))
68762306a36Sopenharmony_ci		return -EOPNOTSUPP;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	res = hfsplus_listxattr_finder_info(dentry, buffer, size);
69062306a36Sopenharmony_ci	if (res < 0)
69162306a36Sopenharmony_ci		return res;
69262306a36Sopenharmony_ci	else if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
69362306a36Sopenharmony_ci		return (res == 0) ? -EOPNOTSUPP : res;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
69662306a36Sopenharmony_ci	if (err) {
69762306a36Sopenharmony_ci		pr_err("can't init xattr find struct\n");
69862306a36Sopenharmony_ci		return err;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN +
70262306a36Sopenharmony_ci			XATTR_MAC_OSX_PREFIX_LEN + 1, GFP_KERNEL);
70362306a36Sopenharmony_ci	if (!strbuf) {
70462306a36Sopenharmony_ci		res = -ENOMEM;
70562306a36Sopenharmony_ci		goto out;
70662306a36Sopenharmony_ci	}
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	err = hfsplus_find_attr(inode->i_sb, inode->i_ino, NULL, &fd);
70962306a36Sopenharmony_ci	if (err) {
71062306a36Sopenharmony_ci		if (err == -ENOENT) {
71162306a36Sopenharmony_ci			if (res == 0)
71262306a36Sopenharmony_ci				res = -ENODATA;
71362306a36Sopenharmony_ci			goto end_listxattr;
71462306a36Sopenharmony_ci		} else {
71562306a36Sopenharmony_ci			res = err;
71662306a36Sopenharmony_ci			goto end_listxattr;
71762306a36Sopenharmony_ci		}
71862306a36Sopenharmony_ci	}
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	for (;;) {
72162306a36Sopenharmony_ci		u16 key_len = hfs_bnode_read_u16(fd.bnode, fd.keyoffset);
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci		if (key_len == 0 || key_len > fd.tree->max_key_len) {
72462306a36Sopenharmony_ci			pr_err("invalid xattr key length: %d\n", key_len);
72562306a36Sopenharmony_ci			res = -EIO;
72662306a36Sopenharmony_ci			goto end_listxattr;
72762306a36Sopenharmony_ci		}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci		hfs_bnode_read(fd.bnode, &attr_key,
73062306a36Sopenharmony_ci				fd.keyoffset, key_len + sizeof(key_len));
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci		if (be32_to_cpu(attr_key.cnid) != inode->i_ino)
73362306a36Sopenharmony_ci			goto end_listxattr;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci		xattr_name_len = NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN;
73662306a36Sopenharmony_ci		if (hfsplus_uni2asc(inode->i_sb,
73762306a36Sopenharmony_ci			(const struct hfsplus_unistr *)&fd.key->attr.key_name,
73862306a36Sopenharmony_ci					strbuf, &xattr_name_len)) {
73962306a36Sopenharmony_ci			pr_err("unicode conversion failed\n");
74062306a36Sopenharmony_ci			res = -EIO;
74162306a36Sopenharmony_ci			goto end_listxattr;
74262306a36Sopenharmony_ci		}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci		if (!buffer || !size) {
74562306a36Sopenharmony_ci			if (can_list(strbuf))
74662306a36Sopenharmony_ci				res += name_len(strbuf, xattr_name_len);
74762306a36Sopenharmony_ci		} else if (can_list(strbuf)) {
74862306a36Sopenharmony_ci			if (size < (res + name_len(strbuf, xattr_name_len))) {
74962306a36Sopenharmony_ci				res = -ERANGE;
75062306a36Sopenharmony_ci				goto end_listxattr;
75162306a36Sopenharmony_ci			} else
75262306a36Sopenharmony_ci				res += copy_name(buffer + res,
75362306a36Sopenharmony_ci						strbuf, xattr_name_len);
75462306a36Sopenharmony_ci		}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci		if (hfs_brec_goto(&fd, 1))
75762306a36Sopenharmony_ci			goto end_listxattr;
75862306a36Sopenharmony_ci	}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ciend_listxattr:
76162306a36Sopenharmony_ci	kfree(strbuf);
76262306a36Sopenharmony_ciout:
76362306a36Sopenharmony_ci	hfs_find_exit(&fd);
76462306a36Sopenharmony_ci	return res;
76562306a36Sopenharmony_ci}
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_cistatic int hfsplus_removexattr(struct inode *inode, const char *name)
76862306a36Sopenharmony_ci{
76962306a36Sopenharmony_ci	int err;
77062306a36Sopenharmony_ci	struct hfs_find_data cat_fd;
77162306a36Sopenharmony_ci	u16 flags;
77262306a36Sopenharmony_ci	u16 cat_entry_type;
77362306a36Sopenharmony_ci	int is_xattr_acl_deleted;
77462306a36Sopenharmony_ci	int is_all_xattrs_deleted;
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
77762306a36Sopenharmony_ci		return -EOPNOTSUPP;
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	if (!strcmp_xattr_finder_info(name))
78062306a36Sopenharmony_ci		return -EOPNOTSUPP;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
78362306a36Sopenharmony_ci	if (err) {
78462306a36Sopenharmony_ci		pr_err("can't init xattr find struct\n");
78562306a36Sopenharmony_ci		return err;
78662306a36Sopenharmony_ci	}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
78962306a36Sopenharmony_ci	if (err) {
79062306a36Sopenharmony_ci		pr_err("catalog searching failed\n");
79162306a36Sopenharmony_ci		goto end_removexattr;
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	err = hfsplus_delete_attr(inode, name);
79562306a36Sopenharmony_ci	if (err)
79662306a36Sopenharmony_ci		goto end_removexattr;
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	is_xattr_acl_deleted = !strcmp_xattr_acl(name);
79962306a36Sopenharmony_ci	is_all_xattrs_deleted = !hfsplus_attr_exists(inode, NULL);
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	if (!is_xattr_acl_deleted && !is_all_xattrs_deleted)
80262306a36Sopenharmony_ci		goto end_removexattr;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	if (cat_entry_type == HFSPLUS_FOLDER) {
80762306a36Sopenharmony_ci		flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset +
80862306a36Sopenharmony_ci				offsetof(struct hfsplus_cat_folder, flags));
80962306a36Sopenharmony_ci		if (is_xattr_acl_deleted)
81062306a36Sopenharmony_ci			flags &= ~HFSPLUS_ACL_EXISTS;
81162306a36Sopenharmony_ci		if (is_all_xattrs_deleted)
81262306a36Sopenharmony_ci			flags &= ~HFSPLUS_XATTR_EXISTS;
81362306a36Sopenharmony_ci		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
81462306a36Sopenharmony_ci				offsetof(struct hfsplus_cat_folder, flags),
81562306a36Sopenharmony_ci				flags);
81662306a36Sopenharmony_ci		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
81762306a36Sopenharmony_ci	} else if (cat_entry_type == HFSPLUS_FILE) {
81862306a36Sopenharmony_ci		flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset +
81962306a36Sopenharmony_ci				offsetof(struct hfsplus_cat_file, flags));
82062306a36Sopenharmony_ci		if (is_xattr_acl_deleted)
82162306a36Sopenharmony_ci			flags &= ~HFSPLUS_ACL_EXISTS;
82262306a36Sopenharmony_ci		if (is_all_xattrs_deleted)
82362306a36Sopenharmony_ci			flags &= ~HFSPLUS_XATTR_EXISTS;
82462306a36Sopenharmony_ci		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
82562306a36Sopenharmony_ci				offsetof(struct hfsplus_cat_file, flags),
82662306a36Sopenharmony_ci				flags);
82762306a36Sopenharmony_ci		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
82862306a36Sopenharmony_ci	} else {
82962306a36Sopenharmony_ci		pr_err("invalid catalog entry type\n");
83062306a36Sopenharmony_ci		err = -EIO;
83162306a36Sopenharmony_ci		goto end_removexattr;
83262306a36Sopenharmony_ci	}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ciend_removexattr:
83562306a36Sopenharmony_ci	hfs_find_exit(&cat_fd);
83662306a36Sopenharmony_ci	return err;
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_cistatic int hfsplus_osx_getxattr(const struct xattr_handler *handler,
84062306a36Sopenharmony_ci				struct dentry *unused, struct inode *inode,
84162306a36Sopenharmony_ci				const char *name, void *buffer, size_t size)
84262306a36Sopenharmony_ci{
84362306a36Sopenharmony_ci	/*
84462306a36Sopenharmony_ci	 * Don't allow retrieving properly prefixed attributes
84562306a36Sopenharmony_ci	 * by prepending them with "osx."
84662306a36Sopenharmony_ci	 */
84762306a36Sopenharmony_ci	if (is_known_namespace(name))
84862306a36Sopenharmony_ci		return -EOPNOTSUPP;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	/*
85162306a36Sopenharmony_ci	 * osx is the namespace we use to indicate an unprefixed
85262306a36Sopenharmony_ci	 * attribute on the filesystem (like the ones that OS X
85362306a36Sopenharmony_ci	 * creates), so we pass the name through unmodified (after
85462306a36Sopenharmony_ci	 * ensuring it doesn't conflict with another namespace).
85562306a36Sopenharmony_ci	 */
85662306a36Sopenharmony_ci	return __hfsplus_getxattr(inode, name, buffer, size);
85762306a36Sopenharmony_ci}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_cistatic int hfsplus_osx_setxattr(const struct xattr_handler *handler,
86062306a36Sopenharmony_ci				struct mnt_idmap *idmap,
86162306a36Sopenharmony_ci				struct dentry *unused, struct inode *inode,
86262306a36Sopenharmony_ci				const char *name, const void *buffer,
86362306a36Sopenharmony_ci				size_t size, int flags)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	/*
86662306a36Sopenharmony_ci	 * Don't allow setting properly prefixed attributes
86762306a36Sopenharmony_ci	 * by prepending them with "osx."
86862306a36Sopenharmony_ci	 */
86962306a36Sopenharmony_ci	if (is_known_namespace(name))
87062306a36Sopenharmony_ci		return -EOPNOTSUPP;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	/*
87362306a36Sopenharmony_ci	 * osx is the namespace we use to indicate an unprefixed
87462306a36Sopenharmony_ci	 * attribute on the filesystem (like the ones that OS X
87562306a36Sopenharmony_ci	 * creates), so we pass the name through unmodified (after
87662306a36Sopenharmony_ci	 * ensuring it doesn't conflict with another namespace).
87762306a36Sopenharmony_ci	 */
87862306a36Sopenharmony_ci	return __hfsplus_setxattr(inode, name, buffer, size, flags);
87962306a36Sopenharmony_ci}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ciconst struct xattr_handler hfsplus_xattr_osx_handler = {
88262306a36Sopenharmony_ci	.prefix	= XATTR_MAC_OSX_PREFIX,
88362306a36Sopenharmony_ci	.get	= hfsplus_osx_getxattr,
88462306a36Sopenharmony_ci	.set	= hfsplus_osx_setxattr,
88562306a36Sopenharmony_ci};
886