18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/fs/hfsplus/xattr.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Vyacheslav Dubeyko <slava@dubeyko.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Logic of processing extended attributes
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include "hfsplus_fs.h"
118c2ecf20Sopenharmony_ci#include <linux/nls.h>
128c2ecf20Sopenharmony_ci#include "xattr.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cistatic int hfsplus_removexattr(struct inode *inode, const char *name);
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciconst struct xattr_handler *hfsplus_xattr_handlers[] = {
178c2ecf20Sopenharmony_ci	&hfsplus_xattr_osx_handler,
188c2ecf20Sopenharmony_ci	&hfsplus_xattr_user_handler,
198c2ecf20Sopenharmony_ci	&hfsplus_xattr_trusted_handler,
208c2ecf20Sopenharmony_ci	&hfsplus_xattr_security_handler,
218c2ecf20Sopenharmony_ci	NULL
228c2ecf20Sopenharmony_ci};
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic int strcmp_xattr_finder_info(const char *name)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	if (name) {
278c2ecf20Sopenharmony_ci		return strncmp(name, HFSPLUS_XATTR_FINDER_INFO_NAME,
288c2ecf20Sopenharmony_ci				sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME));
298c2ecf20Sopenharmony_ci	}
308c2ecf20Sopenharmony_ci	return -1;
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic int strcmp_xattr_acl(const char *name)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	if (name) {
368c2ecf20Sopenharmony_ci		return strncmp(name, HFSPLUS_XATTR_ACL_NAME,
378c2ecf20Sopenharmony_ci				sizeof(HFSPLUS_XATTR_ACL_NAME));
388c2ecf20Sopenharmony_ci	}
398c2ecf20Sopenharmony_ci	return -1;
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cistatic bool is_known_namespace(const char *name)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	if (strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) &&
458c2ecf20Sopenharmony_ci	    strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) &&
468c2ecf20Sopenharmony_ci	    strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) &&
478c2ecf20Sopenharmony_ci	    strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN))
488c2ecf20Sopenharmony_ci		return false;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	return true;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void hfsplus_init_header_node(struct inode *attr_file,
548c2ecf20Sopenharmony_ci					u32 clump_size,
558c2ecf20Sopenharmony_ci					char *buf, u16 node_size)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	struct hfs_bnode_desc *desc;
588c2ecf20Sopenharmony_ci	struct hfs_btree_header_rec *head;
598c2ecf20Sopenharmony_ci	u16 offset;
608c2ecf20Sopenharmony_ci	__be16 *rec_offsets;
618c2ecf20Sopenharmony_ci	u32 hdr_node_map_rec_bits;
628c2ecf20Sopenharmony_ci	char *bmp;
638c2ecf20Sopenharmony_ci	u32 used_nodes;
648c2ecf20Sopenharmony_ci	u32 used_bmp_bytes;
658c2ecf20Sopenharmony_ci	u64 tmp;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	hfs_dbg(ATTR_MOD, "init_hdr_attr_file: clump %u, node_size %u\n",
688c2ecf20Sopenharmony_ci		clump_size, node_size);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	/* The end of the node contains list of record offsets */
718c2ecf20Sopenharmony_ci	rec_offsets = (__be16 *)(buf + node_size);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	desc = (struct hfs_bnode_desc *)buf;
748c2ecf20Sopenharmony_ci	desc->type = HFS_NODE_HEADER;
758c2ecf20Sopenharmony_ci	desc->num_recs = cpu_to_be16(HFSPLUS_BTREE_HDR_NODE_RECS_COUNT);
768c2ecf20Sopenharmony_ci	offset = sizeof(struct hfs_bnode_desc);
778c2ecf20Sopenharmony_ci	*--rec_offsets = cpu_to_be16(offset);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	head = (struct hfs_btree_header_rec *)(buf + offset);
808c2ecf20Sopenharmony_ci	head->node_size = cpu_to_be16(node_size);
818c2ecf20Sopenharmony_ci	tmp = i_size_read(attr_file);
828c2ecf20Sopenharmony_ci	do_div(tmp, node_size);
838c2ecf20Sopenharmony_ci	head->node_count = cpu_to_be32(tmp);
848c2ecf20Sopenharmony_ci	head->free_nodes = cpu_to_be32(be32_to_cpu(head->node_count) - 1);
858c2ecf20Sopenharmony_ci	head->clump_size = cpu_to_be32(clump_size);
868c2ecf20Sopenharmony_ci	head->attributes |= cpu_to_be32(HFS_TREE_BIGKEYS | HFS_TREE_VARIDXKEYS);
878c2ecf20Sopenharmony_ci	head->max_key_len = cpu_to_be16(HFSPLUS_ATTR_KEYLEN - sizeof(u16));
888c2ecf20Sopenharmony_ci	offset += sizeof(struct hfs_btree_header_rec);
898c2ecf20Sopenharmony_ci	*--rec_offsets = cpu_to_be16(offset);
908c2ecf20Sopenharmony_ci	offset += HFSPLUS_BTREE_HDR_USER_BYTES;
918c2ecf20Sopenharmony_ci	*--rec_offsets = cpu_to_be16(offset);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	hdr_node_map_rec_bits = 8 * (node_size - offset - (4 * sizeof(u16)));
948c2ecf20Sopenharmony_ci	if (be32_to_cpu(head->node_count) > hdr_node_map_rec_bits) {
958c2ecf20Sopenharmony_ci		u32 map_node_bits;
968c2ecf20Sopenharmony_ci		u32 map_nodes;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci		desc->next = cpu_to_be32(be32_to_cpu(head->leaf_tail) + 1);
998c2ecf20Sopenharmony_ci		map_node_bits = 8 * (node_size - sizeof(struct hfs_bnode_desc) -
1008c2ecf20Sopenharmony_ci					(2 * sizeof(u16)) - 2);
1018c2ecf20Sopenharmony_ci		map_nodes = (be32_to_cpu(head->node_count) -
1028c2ecf20Sopenharmony_ci				hdr_node_map_rec_bits +
1038c2ecf20Sopenharmony_ci				(map_node_bits - 1)) / map_node_bits;
1048c2ecf20Sopenharmony_ci		be32_add_cpu(&head->free_nodes, 0 - map_nodes);
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	bmp = buf + offset;
1088c2ecf20Sopenharmony_ci	used_nodes =
1098c2ecf20Sopenharmony_ci		be32_to_cpu(head->node_count) - be32_to_cpu(head->free_nodes);
1108c2ecf20Sopenharmony_ci	used_bmp_bytes = used_nodes / 8;
1118c2ecf20Sopenharmony_ci	if (used_bmp_bytes) {
1128c2ecf20Sopenharmony_ci		memset(bmp, 0xFF, used_bmp_bytes);
1138c2ecf20Sopenharmony_ci		bmp += used_bmp_bytes;
1148c2ecf20Sopenharmony_ci		used_nodes %= 8;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci	*bmp = ~(0xFF >> used_nodes);
1178c2ecf20Sopenharmony_ci	offset += hdr_node_map_rec_bits / 8;
1188c2ecf20Sopenharmony_ci	*--rec_offsets = cpu_to_be16(offset);
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic int hfsplus_create_attributes_file(struct super_block *sb)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	int err = 0;
1248c2ecf20Sopenharmony_ci	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
1258c2ecf20Sopenharmony_ci	struct inode *attr_file;
1268c2ecf20Sopenharmony_ci	struct hfsplus_inode_info *hip;
1278c2ecf20Sopenharmony_ci	u32 clump_size;
1288c2ecf20Sopenharmony_ci	u16 node_size = HFSPLUS_ATTR_TREE_NODE_SIZE;
1298c2ecf20Sopenharmony_ci	char *buf;
1308c2ecf20Sopenharmony_ci	int index, written;
1318c2ecf20Sopenharmony_ci	struct address_space *mapping;
1328c2ecf20Sopenharmony_ci	struct page *page;
1338c2ecf20Sopenharmony_ci	int old_state = HFSPLUS_EMPTY_ATTR_TREE;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	hfs_dbg(ATTR_MOD, "create_attr_file: ino %d\n", HFSPLUS_ATTR_CNID);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cicheck_attr_tree_state_again:
1388c2ecf20Sopenharmony_ci	switch (atomic_read(&sbi->attr_tree_state)) {
1398c2ecf20Sopenharmony_ci	case HFSPLUS_EMPTY_ATTR_TREE:
1408c2ecf20Sopenharmony_ci		if (old_state != atomic_cmpxchg(&sbi->attr_tree_state,
1418c2ecf20Sopenharmony_ci						old_state,
1428c2ecf20Sopenharmony_ci						HFSPLUS_CREATING_ATTR_TREE))
1438c2ecf20Sopenharmony_ci			goto check_attr_tree_state_again;
1448c2ecf20Sopenharmony_ci		break;
1458c2ecf20Sopenharmony_ci	case HFSPLUS_CREATING_ATTR_TREE:
1468c2ecf20Sopenharmony_ci		/*
1478c2ecf20Sopenharmony_ci		 * This state means that another thread is in process
1488c2ecf20Sopenharmony_ci		 * of AttributesFile creation. Theoretically, it is
1498c2ecf20Sopenharmony_ci		 * possible to be here. But really __setxattr() method
1508c2ecf20Sopenharmony_ci		 * first of all calls hfs_find_init() for lookup in
1518c2ecf20Sopenharmony_ci		 * B-tree of CatalogFile. This method locks mutex of
1528c2ecf20Sopenharmony_ci		 * CatalogFile's B-tree. As a result, if some thread
1538c2ecf20Sopenharmony_ci		 * is inside AttributedFile creation operation then
1548c2ecf20Sopenharmony_ci		 * another threads will be waiting unlocking of
1558c2ecf20Sopenharmony_ci		 * CatalogFile's B-tree's mutex. However, if code will
1568c2ecf20Sopenharmony_ci		 * change then we will return error code (-EAGAIN) from
1578c2ecf20Sopenharmony_ci		 * here. Really, it means that first try to set of xattr
1588c2ecf20Sopenharmony_ci		 * fails with error but second attempt will have success.
1598c2ecf20Sopenharmony_ci		 */
1608c2ecf20Sopenharmony_ci		return -EAGAIN;
1618c2ecf20Sopenharmony_ci	case HFSPLUS_VALID_ATTR_TREE:
1628c2ecf20Sopenharmony_ci		return 0;
1638c2ecf20Sopenharmony_ci	case HFSPLUS_FAILED_ATTR_TREE:
1648c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
1658c2ecf20Sopenharmony_ci	default:
1668c2ecf20Sopenharmony_ci		BUG();
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	attr_file = hfsplus_iget(sb, HFSPLUS_ATTR_CNID);
1708c2ecf20Sopenharmony_ci	if (IS_ERR(attr_file)) {
1718c2ecf20Sopenharmony_ci		pr_err("failed to load attributes file\n");
1728c2ecf20Sopenharmony_ci		return PTR_ERR(attr_file);
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	BUG_ON(i_size_read(attr_file) != 0);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	hip = HFSPLUS_I(attr_file);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	clump_size = hfsplus_calc_btree_clump_size(sb->s_blocksize,
1808c2ecf20Sopenharmony_ci						    node_size,
1818c2ecf20Sopenharmony_ci						    sbi->sect_count,
1828c2ecf20Sopenharmony_ci						    HFSPLUS_ATTR_CNID);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	mutex_lock(&hip->extents_lock);
1858c2ecf20Sopenharmony_ci	hip->clump_blocks = clump_size >> sbi->alloc_blksz_shift;
1868c2ecf20Sopenharmony_ci	mutex_unlock(&hip->extents_lock);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (sbi->free_blocks <= (hip->clump_blocks << 1)) {
1898c2ecf20Sopenharmony_ci		err = -ENOSPC;
1908c2ecf20Sopenharmony_ci		goto end_attr_file_creation;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	while (hip->alloc_blocks < hip->clump_blocks) {
1948c2ecf20Sopenharmony_ci		err = hfsplus_file_extend(attr_file, false);
1958c2ecf20Sopenharmony_ci		if (unlikely(err)) {
1968c2ecf20Sopenharmony_ci			pr_err("failed to extend attributes file\n");
1978c2ecf20Sopenharmony_ci			goto end_attr_file_creation;
1988c2ecf20Sopenharmony_ci		}
1998c2ecf20Sopenharmony_ci		hip->phys_size = attr_file->i_size =
2008c2ecf20Sopenharmony_ci			(loff_t)hip->alloc_blocks << sbi->alloc_blksz_shift;
2018c2ecf20Sopenharmony_ci		hip->fs_blocks = hip->alloc_blocks << sbi->fs_shift;
2028c2ecf20Sopenharmony_ci		inode_set_bytes(attr_file, attr_file->i_size);
2038c2ecf20Sopenharmony_ci	}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	buf = kzalloc(node_size, GFP_NOFS);
2068c2ecf20Sopenharmony_ci	if (!buf) {
2078c2ecf20Sopenharmony_ci		pr_err("failed to allocate memory for header node\n");
2088c2ecf20Sopenharmony_ci		err = -ENOMEM;
2098c2ecf20Sopenharmony_ci		goto end_attr_file_creation;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	hfsplus_init_header_node(attr_file, clump_size, buf, node_size);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	mapping = attr_file->i_mapping;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	index = 0;
2178c2ecf20Sopenharmony_ci	written = 0;
2188c2ecf20Sopenharmony_ci	for (; written < node_size; index++, written += PAGE_SIZE) {
2198c2ecf20Sopenharmony_ci		void *kaddr;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci		page = read_mapping_page(mapping, index, NULL);
2228c2ecf20Sopenharmony_ci		if (IS_ERR(page)) {
2238c2ecf20Sopenharmony_ci			err = PTR_ERR(page);
2248c2ecf20Sopenharmony_ci			goto failed_header_node_init;
2258c2ecf20Sopenharmony_ci		}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci		kaddr = kmap_atomic(page);
2288c2ecf20Sopenharmony_ci		memcpy(kaddr, buf + written,
2298c2ecf20Sopenharmony_ci			min_t(size_t, PAGE_SIZE, node_size - written));
2308c2ecf20Sopenharmony_ci		kunmap_atomic(kaddr);
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci		set_page_dirty(page);
2338c2ecf20Sopenharmony_ci		put_page(page);
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	hfsplus_mark_inode_dirty(attr_file, HFSPLUS_I_ATTR_DIRTY);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID);
2398c2ecf20Sopenharmony_ci	if (!sbi->attr_tree)
2408c2ecf20Sopenharmony_ci		pr_err("failed to load attributes file\n");
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cifailed_header_node_init:
2438c2ecf20Sopenharmony_ci	kfree(buf);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ciend_attr_file_creation:
2468c2ecf20Sopenharmony_ci	iput(attr_file);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (!err)
2498c2ecf20Sopenharmony_ci		atomic_set(&sbi->attr_tree_state, HFSPLUS_VALID_ATTR_TREE);
2508c2ecf20Sopenharmony_ci	else if (err == -ENOSPC)
2518c2ecf20Sopenharmony_ci		atomic_set(&sbi->attr_tree_state, HFSPLUS_EMPTY_ATTR_TREE);
2528c2ecf20Sopenharmony_ci	else
2538c2ecf20Sopenharmony_ci		atomic_set(&sbi->attr_tree_state, HFSPLUS_FAILED_ATTR_TREE);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	return err;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ciint __hfsplus_setxattr(struct inode *inode, const char *name,
2598c2ecf20Sopenharmony_ci			const void *value, size_t size, int flags)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	int err = 0;
2628c2ecf20Sopenharmony_ci	struct hfs_find_data cat_fd;
2638c2ecf20Sopenharmony_ci	hfsplus_cat_entry entry;
2648c2ecf20Sopenharmony_ci	u16 cat_entry_flags, cat_entry_type;
2658c2ecf20Sopenharmony_ci	u16 folder_finderinfo_len = sizeof(struct DInfo) +
2668c2ecf20Sopenharmony_ci					sizeof(struct DXInfo);
2678c2ecf20Sopenharmony_ci	u16 file_finderinfo_len = sizeof(struct FInfo) +
2688c2ecf20Sopenharmony_ci					sizeof(struct FXInfo);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if ((!S_ISREG(inode->i_mode) &&
2718c2ecf20Sopenharmony_ci			!S_ISDIR(inode->i_mode)) ||
2728c2ecf20Sopenharmony_ci				HFSPLUS_IS_RSRC(inode))
2738c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	if (value == NULL)
2768c2ecf20Sopenharmony_ci		return hfsplus_removexattr(inode, name);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
2798c2ecf20Sopenharmony_ci	if (err) {
2808c2ecf20Sopenharmony_ci		pr_err("can't init xattr find struct\n");
2818c2ecf20Sopenharmony_ci		return err;
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
2858c2ecf20Sopenharmony_ci	if (err) {
2868c2ecf20Sopenharmony_ci		pr_err("catalog searching failed\n");
2878c2ecf20Sopenharmony_ci		goto end_setxattr;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (!strcmp_xattr_finder_info(name)) {
2918c2ecf20Sopenharmony_ci		if (flags & XATTR_CREATE) {
2928c2ecf20Sopenharmony_ci			pr_err("xattr exists yet\n");
2938c2ecf20Sopenharmony_ci			err = -EOPNOTSUPP;
2948c2ecf20Sopenharmony_ci			goto end_setxattr;
2958c2ecf20Sopenharmony_ci		}
2968c2ecf20Sopenharmony_ci		hfs_bnode_read(cat_fd.bnode, &entry, cat_fd.entryoffset,
2978c2ecf20Sopenharmony_ci					sizeof(hfsplus_cat_entry));
2988c2ecf20Sopenharmony_ci		if (be16_to_cpu(entry.type) == HFSPLUS_FOLDER) {
2998c2ecf20Sopenharmony_ci			if (size == folder_finderinfo_len) {
3008c2ecf20Sopenharmony_ci				memcpy(&entry.folder.user_info, value,
3018c2ecf20Sopenharmony_ci						folder_finderinfo_len);
3028c2ecf20Sopenharmony_ci				hfs_bnode_write(cat_fd.bnode, &entry,
3038c2ecf20Sopenharmony_ci					cat_fd.entryoffset,
3048c2ecf20Sopenharmony_ci					sizeof(struct hfsplus_cat_folder));
3058c2ecf20Sopenharmony_ci				hfsplus_mark_inode_dirty(inode,
3068c2ecf20Sopenharmony_ci						HFSPLUS_I_CAT_DIRTY);
3078c2ecf20Sopenharmony_ci			} else {
3088c2ecf20Sopenharmony_ci				err = -ERANGE;
3098c2ecf20Sopenharmony_ci				goto end_setxattr;
3108c2ecf20Sopenharmony_ci			}
3118c2ecf20Sopenharmony_ci		} else if (be16_to_cpu(entry.type) == HFSPLUS_FILE) {
3128c2ecf20Sopenharmony_ci			if (size == file_finderinfo_len) {
3138c2ecf20Sopenharmony_ci				memcpy(&entry.file.user_info, value,
3148c2ecf20Sopenharmony_ci						file_finderinfo_len);
3158c2ecf20Sopenharmony_ci				hfs_bnode_write(cat_fd.bnode, &entry,
3168c2ecf20Sopenharmony_ci					cat_fd.entryoffset,
3178c2ecf20Sopenharmony_ci					sizeof(struct hfsplus_cat_file));
3188c2ecf20Sopenharmony_ci				hfsplus_mark_inode_dirty(inode,
3198c2ecf20Sopenharmony_ci						HFSPLUS_I_CAT_DIRTY);
3208c2ecf20Sopenharmony_ci			} else {
3218c2ecf20Sopenharmony_ci				err = -ERANGE;
3228c2ecf20Sopenharmony_ci				goto end_setxattr;
3238c2ecf20Sopenharmony_ci			}
3248c2ecf20Sopenharmony_ci		} else {
3258c2ecf20Sopenharmony_ci			err = -EOPNOTSUPP;
3268c2ecf20Sopenharmony_ci			goto end_setxattr;
3278c2ecf20Sopenharmony_ci		}
3288c2ecf20Sopenharmony_ci		goto end_setxattr;
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	if (!HFSPLUS_SB(inode->i_sb)->attr_tree) {
3328c2ecf20Sopenharmony_ci		err = hfsplus_create_attributes_file(inode->i_sb);
3338c2ecf20Sopenharmony_ci		if (unlikely(err))
3348c2ecf20Sopenharmony_ci			goto end_setxattr;
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	if (hfsplus_attr_exists(inode, name)) {
3388c2ecf20Sopenharmony_ci		if (flags & XATTR_CREATE) {
3398c2ecf20Sopenharmony_ci			pr_err("xattr exists yet\n");
3408c2ecf20Sopenharmony_ci			err = -EOPNOTSUPP;
3418c2ecf20Sopenharmony_ci			goto end_setxattr;
3428c2ecf20Sopenharmony_ci		}
3438c2ecf20Sopenharmony_ci		err = hfsplus_delete_attr(inode, name);
3448c2ecf20Sopenharmony_ci		if (err)
3458c2ecf20Sopenharmony_ci			goto end_setxattr;
3468c2ecf20Sopenharmony_ci		err = hfsplus_create_attr(inode, name, value, size);
3478c2ecf20Sopenharmony_ci		if (err)
3488c2ecf20Sopenharmony_ci			goto end_setxattr;
3498c2ecf20Sopenharmony_ci	} else {
3508c2ecf20Sopenharmony_ci		if (flags & XATTR_REPLACE) {
3518c2ecf20Sopenharmony_ci			pr_err("cannot replace xattr\n");
3528c2ecf20Sopenharmony_ci			err = -EOPNOTSUPP;
3538c2ecf20Sopenharmony_ci			goto end_setxattr;
3548c2ecf20Sopenharmony_ci		}
3558c2ecf20Sopenharmony_ci		err = hfsplus_create_attr(inode, name, value, size);
3568c2ecf20Sopenharmony_ci		if (err)
3578c2ecf20Sopenharmony_ci			goto end_setxattr;
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
3618c2ecf20Sopenharmony_ci	if (cat_entry_type == HFSPLUS_FOLDER) {
3628c2ecf20Sopenharmony_ci		cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
3638c2ecf20Sopenharmony_ci				    cat_fd.entryoffset +
3648c2ecf20Sopenharmony_ci				    offsetof(struct hfsplus_cat_folder, flags));
3658c2ecf20Sopenharmony_ci		cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
3668c2ecf20Sopenharmony_ci		if (!strcmp_xattr_acl(name))
3678c2ecf20Sopenharmony_ci			cat_entry_flags |= HFSPLUS_ACL_EXISTS;
3688c2ecf20Sopenharmony_ci		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
3698c2ecf20Sopenharmony_ci				offsetof(struct hfsplus_cat_folder, flags),
3708c2ecf20Sopenharmony_ci				cat_entry_flags);
3718c2ecf20Sopenharmony_ci		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
3728c2ecf20Sopenharmony_ci	} else if (cat_entry_type == HFSPLUS_FILE) {
3738c2ecf20Sopenharmony_ci		cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
3748c2ecf20Sopenharmony_ci				    cat_fd.entryoffset +
3758c2ecf20Sopenharmony_ci				    offsetof(struct hfsplus_cat_file, flags));
3768c2ecf20Sopenharmony_ci		cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
3778c2ecf20Sopenharmony_ci		if (!strcmp_xattr_acl(name))
3788c2ecf20Sopenharmony_ci			cat_entry_flags |= HFSPLUS_ACL_EXISTS;
3798c2ecf20Sopenharmony_ci		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
3808c2ecf20Sopenharmony_ci				    offsetof(struct hfsplus_cat_file, flags),
3818c2ecf20Sopenharmony_ci				    cat_entry_flags);
3828c2ecf20Sopenharmony_ci		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
3838c2ecf20Sopenharmony_ci	} else {
3848c2ecf20Sopenharmony_ci		pr_err("invalid catalog entry type\n");
3858c2ecf20Sopenharmony_ci		err = -EIO;
3868c2ecf20Sopenharmony_ci		goto end_setxattr;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ciend_setxattr:
3908c2ecf20Sopenharmony_ci	hfs_find_exit(&cat_fd);
3918c2ecf20Sopenharmony_ci	return err;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic int name_len(const char *xattr_name, int xattr_name_len)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	int len = xattr_name_len + 1;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	if (!is_known_namespace(xattr_name))
3998c2ecf20Sopenharmony_ci		len += XATTR_MAC_OSX_PREFIX_LEN;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	return len;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic int copy_name(char *buffer, const char *xattr_name, int name_len)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	int len = name_len;
4078c2ecf20Sopenharmony_ci	int offset = 0;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	if (!is_known_namespace(xattr_name)) {
4108c2ecf20Sopenharmony_ci		memcpy(buffer, XATTR_MAC_OSX_PREFIX, XATTR_MAC_OSX_PREFIX_LEN);
4118c2ecf20Sopenharmony_ci		offset += XATTR_MAC_OSX_PREFIX_LEN;
4128c2ecf20Sopenharmony_ci		len += XATTR_MAC_OSX_PREFIX_LEN;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	strncpy(buffer + offset, xattr_name, name_len);
4168c2ecf20Sopenharmony_ci	memset(buffer + offset + name_len, 0, 1);
4178c2ecf20Sopenharmony_ci	len += 1;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	return len;
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ciint hfsplus_setxattr(struct inode *inode, const char *name,
4238c2ecf20Sopenharmony_ci		     const void *value, size_t size, int flags,
4248c2ecf20Sopenharmony_ci		     const char *prefix, size_t prefixlen)
4258c2ecf20Sopenharmony_ci{
4268c2ecf20Sopenharmony_ci	char *xattr_name;
4278c2ecf20Sopenharmony_ci	int res;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1,
4308c2ecf20Sopenharmony_ci		GFP_KERNEL);
4318c2ecf20Sopenharmony_ci	if (!xattr_name)
4328c2ecf20Sopenharmony_ci		return -ENOMEM;
4338c2ecf20Sopenharmony_ci	strcpy(xattr_name, prefix);
4348c2ecf20Sopenharmony_ci	strcpy(xattr_name + prefixlen, name);
4358c2ecf20Sopenharmony_ci	res = __hfsplus_setxattr(inode, xattr_name, value, size, flags);
4368c2ecf20Sopenharmony_ci	kfree(xattr_name);
4378c2ecf20Sopenharmony_ci	return res;
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cistatic ssize_t hfsplus_getxattr_finder_info(struct inode *inode,
4418c2ecf20Sopenharmony_ci						void *value, size_t size)
4428c2ecf20Sopenharmony_ci{
4438c2ecf20Sopenharmony_ci	ssize_t res = 0;
4448c2ecf20Sopenharmony_ci	struct hfs_find_data fd;
4458c2ecf20Sopenharmony_ci	u16 entry_type;
4468c2ecf20Sopenharmony_ci	u16 folder_rec_len = sizeof(struct DInfo) + sizeof(struct DXInfo);
4478c2ecf20Sopenharmony_ci	u16 file_rec_len = sizeof(struct FInfo) + sizeof(struct FXInfo);
4488c2ecf20Sopenharmony_ci	u16 record_len = max(folder_rec_len, file_rec_len);
4498c2ecf20Sopenharmony_ci	u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
4508c2ecf20Sopenharmony_ci	u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	if (size >= record_len) {
4538c2ecf20Sopenharmony_ci		res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
4548c2ecf20Sopenharmony_ci		if (res) {
4558c2ecf20Sopenharmony_ci			pr_err("can't init xattr find struct\n");
4568c2ecf20Sopenharmony_ci			return res;
4578c2ecf20Sopenharmony_ci		}
4588c2ecf20Sopenharmony_ci		res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
4598c2ecf20Sopenharmony_ci		if (res)
4608c2ecf20Sopenharmony_ci			goto end_getxattr_finder_info;
4618c2ecf20Sopenharmony_ci		entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci		if (entry_type == HFSPLUS_FOLDER) {
4648c2ecf20Sopenharmony_ci			hfs_bnode_read(fd.bnode, folder_finder_info,
4658c2ecf20Sopenharmony_ci				fd.entryoffset +
4668c2ecf20Sopenharmony_ci				offsetof(struct hfsplus_cat_folder, user_info),
4678c2ecf20Sopenharmony_ci				folder_rec_len);
4688c2ecf20Sopenharmony_ci			memcpy(value, folder_finder_info, folder_rec_len);
4698c2ecf20Sopenharmony_ci			res = folder_rec_len;
4708c2ecf20Sopenharmony_ci		} else if (entry_type == HFSPLUS_FILE) {
4718c2ecf20Sopenharmony_ci			hfs_bnode_read(fd.bnode, file_finder_info,
4728c2ecf20Sopenharmony_ci				fd.entryoffset +
4738c2ecf20Sopenharmony_ci				offsetof(struct hfsplus_cat_file, user_info),
4748c2ecf20Sopenharmony_ci				file_rec_len);
4758c2ecf20Sopenharmony_ci			memcpy(value, file_finder_info, file_rec_len);
4768c2ecf20Sopenharmony_ci			res = file_rec_len;
4778c2ecf20Sopenharmony_ci		} else {
4788c2ecf20Sopenharmony_ci			res = -EOPNOTSUPP;
4798c2ecf20Sopenharmony_ci			goto end_getxattr_finder_info;
4808c2ecf20Sopenharmony_ci		}
4818c2ecf20Sopenharmony_ci	} else
4828c2ecf20Sopenharmony_ci		res = size ? -ERANGE : record_len;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ciend_getxattr_finder_info:
4858c2ecf20Sopenharmony_ci	if (size >= record_len)
4868c2ecf20Sopenharmony_ci		hfs_find_exit(&fd);
4878c2ecf20Sopenharmony_ci	return res;
4888c2ecf20Sopenharmony_ci}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_cissize_t __hfsplus_getxattr(struct inode *inode, const char *name,
4918c2ecf20Sopenharmony_ci			 void *value, size_t size)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	struct hfs_find_data fd;
4948c2ecf20Sopenharmony_ci	hfsplus_attr_entry *entry;
4958c2ecf20Sopenharmony_ci	__be32 xattr_record_type;
4968c2ecf20Sopenharmony_ci	u32 record_type;
4978c2ecf20Sopenharmony_ci	u16 record_length = 0;
4988c2ecf20Sopenharmony_ci	ssize_t res = 0;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	if ((!S_ISREG(inode->i_mode) &&
5018c2ecf20Sopenharmony_ci			!S_ISDIR(inode->i_mode)) ||
5028c2ecf20Sopenharmony_ci				HFSPLUS_IS_RSRC(inode))
5038c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	if (!strcmp_xattr_finder_info(name))
5068c2ecf20Sopenharmony_ci		return hfsplus_getxattr_finder_info(inode, value, size);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
5098c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	entry = hfsplus_alloc_attr_entry();
5128c2ecf20Sopenharmony_ci	if (!entry) {
5138c2ecf20Sopenharmony_ci		pr_err("can't allocate xattr entry\n");
5148c2ecf20Sopenharmony_ci		return -ENOMEM;
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
5188c2ecf20Sopenharmony_ci	if (res) {
5198c2ecf20Sopenharmony_ci		pr_err("can't init xattr find struct\n");
5208c2ecf20Sopenharmony_ci		goto failed_getxattr_init;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	res = hfsplus_find_attr(inode->i_sb, inode->i_ino, name, &fd);
5248c2ecf20Sopenharmony_ci	if (res) {
5258c2ecf20Sopenharmony_ci		if (res == -ENOENT)
5268c2ecf20Sopenharmony_ci			res = -ENODATA;
5278c2ecf20Sopenharmony_ci		else
5288c2ecf20Sopenharmony_ci			pr_err("xattr searching failed\n");
5298c2ecf20Sopenharmony_ci		goto out;
5308c2ecf20Sopenharmony_ci	}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	hfs_bnode_read(fd.bnode, &xattr_record_type,
5338c2ecf20Sopenharmony_ci			fd.entryoffset, sizeof(xattr_record_type));
5348c2ecf20Sopenharmony_ci	record_type = be32_to_cpu(xattr_record_type);
5358c2ecf20Sopenharmony_ci	if (record_type == HFSPLUS_ATTR_INLINE_DATA) {
5368c2ecf20Sopenharmony_ci		record_length = hfs_bnode_read_u16(fd.bnode,
5378c2ecf20Sopenharmony_ci				fd.entryoffset +
5388c2ecf20Sopenharmony_ci				offsetof(struct hfsplus_attr_inline_data,
5398c2ecf20Sopenharmony_ci				length));
5408c2ecf20Sopenharmony_ci		if (record_length > HFSPLUS_MAX_INLINE_DATA_SIZE) {
5418c2ecf20Sopenharmony_ci			pr_err("invalid xattr record size\n");
5428c2ecf20Sopenharmony_ci			res = -EIO;
5438c2ecf20Sopenharmony_ci			goto out;
5448c2ecf20Sopenharmony_ci		}
5458c2ecf20Sopenharmony_ci	} else if (record_type == HFSPLUS_ATTR_FORK_DATA ||
5468c2ecf20Sopenharmony_ci			record_type == HFSPLUS_ATTR_EXTENTS) {
5478c2ecf20Sopenharmony_ci		pr_err("only inline data xattr are supported\n");
5488c2ecf20Sopenharmony_ci		res = -EOPNOTSUPP;
5498c2ecf20Sopenharmony_ci		goto out;
5508c2ecf20Sopenharmony_ci	} else {
5518c2ecf20Sopenharmony_ci		pr_err("invalid xattr record\n");
5528c2ecf20Sopenharmony_ci		res = -EIO;
5538c2ecf20Sopenharmony_ci		goto out;
5548c2ecf20Sopenharmony_ci	}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	if (size) {
5578c2ecf20Sopenharmony_ci		hfs_bnode_read(fd.bnode, entry, fd.entryoffset,
5588c2ecf20Sopenharmony_ci				offsetof(struct hfsplus_attr_inline_data,
5598c2ecf20Sopenharmony_ci					raw_bytes) + record_length);
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	if (size >= record_length) {
5638c2ecf20Sopenharmony_ci		memcpy(value, entry->inline_data.raw_bytes, record_length);
5648c2ecf20Sopenharmony_ci		res = record_length;
5658c2ecf20Sopenharmony_ci	} else
5668c2ecf20Sopenharmony_ci		res = size ? -ERANGE : record_length;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ciout:
5698c2ecf20Sopenharmony_ci	hfs_find_exit(&fd);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_cifailed_getxattr_init:
5728c2ecf20Sopenharmony_ci	hfsplus_destroy_attr_entry(entry);
5738c2ecf20Sopenharmony_ci	return res;
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_cissize_t hfsplus_getxattr(struct inode *inode, const char *name,
5778c2ecf20Sopenharmony_ci			 void *value, size_t size,
5788c2ecf20Sopenharmony_ci			 const char *prefix, size_t prefixlen)
5798c2ecf20Sopenharmony_ci{
5808c2ecf20Sopenharmony_ci	int res;
5818c2ecf20Sopenharmony_ci	char *xattr_name;
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci	xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1,
5848c2ecf20Sopenharmony_ci			     GFP_KERNEL);
5858c2ecf20Sopenharmony_ci	if (!xattr_name)
5868c2ecf20Sopenharmony_ci		return -ENOMEM;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	strcpy(xattr_name, prefix);
5898c2ecf20Sopenharmony_ci	strcpy(xattr_name + prefixlen, name);
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	res = __hfsplus_getxattr(inode, xattr_name, value, size);
5928c2ecf20Sopenharmony_ci	kfree(xattr_name);
5938c2ecf20Sopenharmony_ci	return res;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_cistatic inline int can_list(const char *xattr_name)
5988c2ecf20Sopenharmony_ci{
5998c2ecf20Sopenharmony_ci	if (!xattr_name)
6008c2ecf20Sopenharmony_ci		return 0;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	return strncmp(xattr_name, XATTR_TRUSTED_PREFIX,
6038c2ecf20Sopenharmony_ci			XATTR_TRUSTED_PREFIX_LEN) ||
6048c2ecf20Sopenharmony_ci				capable(CAP_SYS_ADMIN);
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_cistatic ssize_t hfsplus_listxattr_finder_info(struct dentry *dentry,
6088c2ecf20Sopenharmony_ci						char *buffer, size_t size)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	ssize_t res = 0;
6118c2ecf20Sopenharmony_ci	struct inode *inode = d_inode(dentry);
6128c2ecf20Sopenharmony_ci	struct hfs_find_data fd;
6138c2ecf20Sopenharmony_ci	u16 entry_type;
6148c2ecf20Sopenharmony_ci	u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
6158c2ecf20Sopenharmony_ci	u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
6168c2ecf20Sopenharmony_ci	unsigned long len, found_bit;
6178c2ecf20Sopenharmony_ci	int xattr_name_len, symbols_count;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
6208c2ecf20Sopenharmony_ci	if (res) {
6218c2ecf20Sopenharmony_ci		pr_err("can't init xattr find struct\n");
6228c2ecf20Sopenharmony_ci		return res;
6238c2ecf20Sopenharmony_ci	}
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
6268c2ecf20Sopenharmony_ci	if (res)
6278c2ecf20Sopenharmony_ci		goto end_listxattr_finder_info;
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
6308c2ecf20Sopenharmony_ci	if (entry_type == HFSPLUS_FOLDER) {
6318c2ecf20Sopenharmony_ci		len = sizeof(struct DInfo) + sizeof(struct DXInfo);
6328c2ecf20Sopenharmony_ci		hfs_bnode_read(fd.bnode, folder_finder_info,
6338c2ecf20Sopenharmony_ci				fd.entryoffset +
6348c2ecf20Sopenharmony_ci				offsetof(struct hfsplus_cat_folder, user_info),
6358c2ecf20Sopenharmony_ci				len);
6368c2ecf20Sopenharmony_ci		found_bit = find_first_bit((void *)folder_finder_info, len*8);
6378c2ecf20Sopenharmony_ci	} else if (entry_type == HFSPLUS_FILE) {
6388c2ecf20Sopenharmony_ci		len = sizeof(struct FInfo) + sizeof(struct FXInfo);
6398c2ecf20Sopenharmony_ci		hfs_bnode_read(fd.bnode, file_finder_info,
6408c2ecf20Sopenharmony_ci				fd.entryoffset +
6418c2ecf20Sopenharmony_ci				offsetof(struct hfsplus_cat_file, user_info),
6428c2ecf20Sopenharmony_ci				len);
6438c2ecf20Sopenharmony_ci		found_bit = find_first_bit((void *)file_finder_info, len*8);
6448c2ecf20Sopenharmony_ci	} else {
6458c2ecf20Sopenharmony_ci		res = -EOPNOTSUPP;
6468c2ecf20Sopenharmony_ci		goto end_listxattr_finder_info;
6478c2ecf20Sopenharmony_ci	}
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci	if (found_bit >= (len*8))
6508c2ecf20Sopenharmony_ci		res = 0;
6518c2ecf20Sopenharmony_ci	else {
6528c2ecf20Sopenharmony_ci		symbols_count = sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME) - 1;
6538c2ecf20Sopenharmony_ci		xattr_name_len =
6548c2ecf20Sopenharmony_ci			name_len(HFSPLUS_XATTR_FINDER_INFO_NAME, symbols_count);
6558c2ecf20Sopenharmony_ci		if (!buffer || !size) {
6568c2ecf20Sopenharmony_ci			if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME))
6578c2ecf20Sopenharmony_ci				res = xattr_name_len;
6588c2ecf20Sopenharmony_ci		} else if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME)) {
6598c2ecf20Sopenharmony_ci			if (size < xattr_name_len)
6608c2ecf20Sopenharmony_ci				res = -ERANGE;
6618c2ecf20Sopenharmony_ci			else {
6628c2ecf20Sopenharmony_ci				res = copy_name(buffer,
6638c2ecf20Sopenharmony_ci						HFSPLUS_XATTR_FINDER_INFO_NAME,
6648c2ecf20Sopenharmony_ci						symbols_count);
6658c2ecf20Sopenharmony_ci			}
6668c2ecf20Sopenharmony_ci		}
6678c2ecf20Sopenharmony_ci	}
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ciend_listxattr_finder_info:
6708c2ecf20Sopenharmony_ci	hfs_find_exit(&fd);
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci	return res;
6738c2ecf20Sopenharmony_ci}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_cissize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
6768c2ecf20Sopenharmony_ci{
6778c2ecf20Sopenharmony_ci	ssize_t err;
6788c2ecf20Sopenharmony_ci	ssize_t res = 0;
6798c2ecf20Sopenharmony_ci	struct inode *inode = d_inode(dentry);
6808c2ecf20Sopenharmony_ci	struct hfs_find_data fd;
6818c2ecf20Sopenharmony_ci	u16 key_len = 0;
6828c2ecf20Sopenharmony_ci	struct hfsplus_attr_key attr_key;
6838c2ecf20Sopenharmony_ci	char *strbuf;
6848c2ecf20Sopenharmony_ci	int xattr_name_len;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	if ((!S_ISREG(inode->i_mode) &&
6878c2ecf20Sopenharmony_ci			!S_ISDIR(inode->i_mode)) ||
6888c2ecf20Sopenharmony_ci				HFSPLUS_IS_RSRC(inode))
6898c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	res = hfsplus_listxattr_finder_info(dentry, buffer, size);
6928c2ecf20Sopenharmony_ci	if (res < 0)
6938c2ecf20Sopenharmony_ci		return res;
6948c2ecf20Sopenharmony_ci	else if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
6958c2ecf20Sopenharmony_ci		return (res == 0) ? -EOPNOTSUPP : res;
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
6988c2ecf20Sopenharmony_ci	if (err) {
6998c2ecf20Sopenharmony_ci		pr_err("can't init xattr find struct\n");
7008c2ecf20Sopenharmony_ci		return err;
7018c2ecf20Sopenharmony_ci	}
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci	strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN +
7048c2ecf20Sopenharmony_ci			XATTR_MAC_OSX_PREFIX_LEN + 1, GFP_KERNEL);
7058c2ecf20Sopenharmony_ci	if (!strbuf) {
7068c2ecf20Sopenharmony_ci		res = -ENOMEM;
7078c2ecf20Sopenharmony_ci		goto out;
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	err = hfsplus_find_attr(inode->i_sb, inode->i_ino, NULL, &fd);
7118c2ecf20Sopenharmony_ci	if (err) {
7128c2ecf20Sopenharmony_ci		if (err == -ENOENT) {
7138c2ecf20Sopenharmony_ci			if (res == 0)
7148c2ecf20Sopenharmony_ci				res = -ENODATA;
7158c2ecf20Sopenharmony_ci			goto end_listxattr;
7168c2ecf20Sopenharmony_ci		} else {
7178c2ecf20Sopenharmony_ci			res = err;
7188c2ecf20Sopenharmony_ci			goto end_listxattr;
7198c2ecf20Sopenharmony_ci		}
7208c2ecf20Sopenharmony_ci	}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	for (;;) {
7238c2ecf20Sopenharmony_ci		key_len = hfs_bnode_read_u16(fd.bnode, fd.keyoffset);
7248c2ecf20Sopenharmony_ci		if (key_len == 0 || key_len > fd.tree->max_key_len) {
7258c2ecf20Sopenharmony_ci			pr_err("invalid xattr key length: %d\n", key_len);
7268c2ecf20Sopenharmony_ci			res = -EIO;
7278c2ecf20Sopenharmony_ci			goto end_listxattr;
7288c2ecf20Sopenharmony_ci		}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci		hfs_bnode_read(fd.bnode, &attr_key,
7318c2ecf20Sopenharmony_ci				fd.keyoffset, key_len + sizeof(key_len));
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci		if (be32_to_cpu(attr_key.cnid) != inode->i_ino)
7348c2ecf20Sopenharmony_ci			goto end_listxattr;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci		xattr_name_len = NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN;
7378c2ecf20Sopenharmony_ci		if (hfsplus_uni2asc(inode->i_sb,
7388c2ecf20Sopenharmony_ci			(const struct hfsplus_unistr *)&fd.key->attr.key_name,
7398c2ecf20Sopenharmony_ci					strbuf, &xattr_name_len)) {
7408c2ecf20Sopenharmony_ci			pr_err("unicode conversion failed\n");
7418c2ecf20Sopenharmony_ci			res = -EIO;
7428c2ecf20Sopenharmony_ci			goto end_listxattr;
7438c2ecf20Sopenharmony_ci		}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci		if (!buffer || !size) {
7468c2ecf20Sopenharmony_ci			if (can_list(strbuf))
7478c2ecf20Sopenharmony_ci				res += name_len(strbuf, xattr_name_len);
7488c2ecf20Sopenharmony_ci		} else if (can_list(strbuf)) {
7498c2ecf20Sopenharmony_ci			if (size < (res + name_len(strbuf, xattr_name_len))) {
7508c2ecf20Sopenharmony_ci				res = -ERANGE;
7518c2ecf20Sopenharmony_ci				goto end_listxattr;
7528c2ecf20Sopenharmony_ci			} else
7538c2ecf20Sopenharmony_ci				res += copy_name(buffer + res,
7548c2ecf20Sopenharmony_ci						strbuf, xattr_name_len);
7558c2ecf20Sopenharmony_ci		}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci		if (hfs_brec_goto(&fd, 1))
7588c2ecf20Sopenharmony_ci			goto end_listxattr;
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ciend_listxattr:
7628c2ecf20Sopenharmony_ci	kfree(strbuf);
7638c2ecf20Sopenharmony_ciout:
7648c2ecf20Sopenharmony_ci	hfs_find_exit(&fd);
7658c2ecf20Sopenharmony_ci	return res;
7668c2ecf20Sopenharmony_ci}
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_cistatic int hfsplus_removexattr(struct inode *inode, const char *name)
7698c2ecf20Sopenharmony_ci{
7708c2ecf20Sopenharmony_ci	int err = 0;
7718c2ecf20Sopenharmony_ci	struct hfs_find_data cat_fd;
7728c2ecf20Sopenharmony_ci	u16 flags;
7738c2ecf20Sopenharmony_ci	u16 cat_entry_type;
7748c2ecf20Sopenharmony_ci	int is_xattr_acl_deleted = 0;
7758c2ecf20Sopenharmony_ci	int is_all_xattrs_deleted = 0;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
7788c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	if (!strcmp_xattr_finder_info(name))
7818c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
7848c2ecf20Sopenharmony_ci	if (err) {
7858c2ecf20Sopenharmony_ci		pr_err("can't init xattr find struct\n");
7868c2ecf20Sopenharmony_ci		return err;
7878c2ecf20Sopenharmony_ci	}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
7908c2ecf20Sopenharmony_ci	if (err) {
7918c2ecf20Sopenharmony_ci		pr_err("catalog searching failed\n");
7928c2ecf20Sopenharmony_ci		goto end_removexattr;
7938c2ecf20Sopenharmony_ci	}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	err = hfsplus_delete_attr(inode, name);
7968c2ecf20Sopenharmony_ci	if (err)
7978c2ecf20Sopenharmony_ci		goto end_removexattr;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	is_xattr_acl_deleted = !strcmp_xattr_acl(name);
8008c2ecf20Sopenharmony_ci	is_all_xattrs_deleted = !hfsplus_attr_exists(inode, NULL);
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	if (!is_xattr_acl_deleted && !is_all_xattrs_deleted)
8038c2ecf20Sopenharmony_ci		goto end_removexattr;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	if (cat_entry_type == HFSPLUS_FOLDER) {
8088c2ecf20Sopenharmony_ci		flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset +
8098c2ecf20Sopenharmony_ci				offsetof(struct hfsplus_cat_folder, flags));
8108c2ecf20Sopenharmony_ci		if (is_xattr_acl_deleted)
8118c2ecf20Sopenharmony_ci			flags &= ~HFSPLUS_ACL_EXISTS;
8128c2ecf20Sopenharmony_ci		if (is_all_xattrs_deleted)
8138c2ecf20Sopenharmony_ci			flags &= ~HFSPLUS_XATTR_EXISTS;
8148c2ecf20Sopenharmony_ci		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
8158c2ecf20Sopenharmony_ci				offsetof(struct hfsplus_cat_folder, flags),
8168c2ecf20Sopenharmony_ci				flags);
8178c2ecf20Sopenharmony_ci		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
8188c2ecf20Sopenharmony_ci	} else if (cat_entry_type == HFSPLUS_FILE) {
8198c2ecf20Sopenharmony_ci		flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset +
8208c2ecf20Sopenharmony_ci				offsetof(struct hfsplus_cat_file, flags));
8218c2ecf20Sopenharmony_ci		if (is_xattr_acl_deleted)
8228c2ecf20Sopenharmony_ci			flags &= ~HFSPLUS_ACL_EXISTS;
8238c2ecf20Sopenharmony_ci		if (is_all_xattrs_deleted)
8248c2ecf20Sopenharmony_ci			flags &= ~HFSPLUS_XATTR_EXISTS;
8258c2ecf20Sopenharmony_ci		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
8268c2ecf20Sopenharmony_ci				offsetof(struct hfsplus_cat_file, flags),
8278c2ecf20Sopenharmony_ci				flags);
8288c2ecf20Sopenharmony_ci		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
8298c2ecf20Sopenharmony_ci	} else {
8308c2ecf20Sopenharmony_ci		pr_err("invalid catalog entry type\n");
8318c2ecf20Sopenharmony_ci		err = -EIO;
8328c2ecf20Sopenharmony_ci		goto end_removexattr;
8338c2ecf20Sopenharmony_ci	}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ciend_removexattr:
8368c2ecf20Sopenharmony_ci	hfs_find_exit(&cat_fd);
8378c2ecf20Sopenharmony_ci	return err;
8388c2ecf20Sopenharmony_ci}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_cistatic int hfsplus_osx_getxattr(const struct xattr_handler *handler,
8418c2ecf20Sopenharmony_ci				struct dentry *unused, struct inode *inode,
8428c2ecf20Sopenharmony_ci				const char *name, void *buffer, size_t size)
8438c2ecf20Sopenharmony_ci{
8448c2ecf20Sopenharmony_ci	/*
8458c2ecf20Sopenharmony_ci	 * Don't allow retrieving properly prefixed attributes
8468c2ecf20Sopenharmony_ci	 * by prepending them with "osx."
8478c2ecf20Sopenharmony_ci	 */
8488c2ecf20Sopenharmony_ci	if (is_known_namespace(name))
8498c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	/*
8528c2ecf20Sopenharmony_ci	 * osx is the namespace we use to indicate an unprefixed
8538c2ecf20Sopenharmony_ci	 * attribute on the filesystem (like the ones that OS X
8548c2ecf20Sopenharmony_ci	 * creates), so we pass the name through unmodified (after
8558c2ecf20Sopenharmony_ci	 * ensuring it doesn't conflict with another namespace).
8568c2ecf20Sopenharmony_ci	 */
8578c2ecf20Sopenharmony_ci	return __hfsplus_getxattr(inode, name, buffer, size);
8588c2ecf20Sopenharmony_ci}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_cistatic int hfsplus_osx_setxattr(const struct xattr_handler *handler,
8618c2ecf20Sopenharmony_ci				struct dentry *unused, struct inode *inode,
8628c2ecf20Sopenharmony_ci				const char *name, const void *buffer,
8638c2ecf20Sopenharmony_ci				size_t size, int flags)
8648c2ecf20Sopenharmony_ci{
8658c2ecf20Sopenharmony_ci	/*
8668c2ecf20Sopenharmony_ci	 * Don't allow setting properly prefixed attributes
8678c2ecf20Sopenharmony_ci	 * by prepending them with "osx."
8688c2ecf20Sopenharmony_ci	 */
8698c2ecf20Sopenharmony_ci	if (is_known_namespace(name))
8708c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	/*
8738c2ecf20Sopenharmony_ci	 * osx is the namespace we use to indicate an unprefixed
8748c2ecf20Sopenharmony_ci	 * attribute on the filesystem (like the ones that OS X
8758c2ecf20Sopenharmony_ci	 * creates), so we pass the name through unmodified (after
8768c2ecf20Sopenharmony_ci	 * ensuring it doesn't conflict with another namespace).
8778c2ecf20Sopenharmony_ci	 */
8788c2ecf20Sopenharmony_ci	return __hfsplus_setxattr(inode, name, buffer, size, flags);
8798c2ecf20Sopenharmony_ci}
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ciconst struct xattr_handler hfsplus_xattr_osx_handler = {
8828c2ecf20Sopenharmony_ci	.prefix	= XATTR_MAC_OSX_PREFIX,
8838c2ecf20Sopenharmony_ci	.get	= hfsplus_osx_getxattr,
8848c2ecf20Sopenharmony_ci	.set	= hfsplus_osx_setxattr,
8858c2ecf20Sopenharmony_ci};
886