xref: /OK3568_Linux_fs/kernel/fs/hfsplus/xattr.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * linux/fs/hfsplus/xattr.c
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Vyacheslav Dubeyko <slava@dubeyko.com>
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Logic of processing extended attributes
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include "hfsplus_fs.h"
11*4882a593Smuzhiyun #include <linux/nls.h>
12*4882a593Smuzhiyun #include "xattr.h"
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun static int hfsplus_removexattr(struct inode *inode, const char *name);
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun const struct xattr_handler *hfsplus_xattr_handlers[] = {
17*4882a593Smuzhiyun 	&hfsplus_xattr_osx_handler,
18*4882a593Smuzhiyun 	&hfsplus_xattr_user_handler,
19*4882a593Smuzhiyun 	&hfsplus_xattr_trusted_handler,
20*4882a593Smuzhiyun 	&hfsplus_xattr_security_handler,
21*4882a593Smuzhiyun 	NULL
22*4882a593Smuzhiyun };
23*4882a593Smuzhiyun 
strcmp_xattr_finder_info(const char * name)24*4882a593Smuzhiyun static int strcmp_xattr_finder_info(const char *name)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun 	if (name) {
27*4882a593Smuzhiyun 		return strncmp(name, HFSPLUS_XATTR_FINDER_INFO_NAME,
28*4882a593Smuzhiyun 				sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME));
29*4882a593Smuzhiyun 	}
30*4882a593Smuzhiyun 	return -1;
31*4882a593Smuzhiyun }
32*4882a593Smuzhiyun 
strcmp_xattr_acl(const char * name)33*4882a593Smuzhiyun static int strcmp_xattr_acl(const char *name)
34*4882a593Smuzhiyun {
35*4882a593Smuzhiyun 	if (name) {
36*4882a593Smuzhiyun 		return strncmp(name, HFSPLUS_XATTR_ACL_NAME,
37*4882a593Smuzhiyun 				sizeof(HFSPLUS_XATTR_ACL_NAME));
38*4882a593Smuzhiyun 	}
39*4882a593Smuzhiyun 	return -1;
40*4882a593Smuzhiyun }
41*4882a593Smuzhiyun 
is_known_namespace(const char * name)42*4882a593Smuzhiyun static bool is_known_namespace(const char *name)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun 	if (strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) &&
45*4882a593Smuzhiyun 	    strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) &&
46*4882a593Smuzhiyun 	    strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) &&
47*4882a593Smuzhiyun 	    strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN))
48*4882a593Smuzhiyun 		return false;
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	return true;
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun 
hfsplus_init_header_node(struct inode * attr_file,u32 clump_size,char * buf,u16 node_size)53*4882a593Smuzhiyun static void hfsplus_init_header_node(struct inode *attr_file,
54*4882a593Smuzhiyun 					u32 clump_size,
55*4882a593Smuzhiyun 					char *buf, u16 node_size)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun 	struct hfs_bnode_desc *desc;
58*4882a593Smuzhiyun 	struct hfs_btree_header_rec *head;
59*4882a593Smuzhiyun 	u16 offset;
60*4882a593Smuzhiyun 	__be16 *rec_offsets;
61*4882a593Smuzhiyun 	u32 hdr_node_map_rec_bits;
62*4882a593Smuzhiyun 	char *bmp;
63*4882a593Smuzhiyun 	u32 used_nodes;
64*4882a593Smuzhiyun 	u32 used_bmp_bytes;
65*4882a593Smuzhiyun 	u64 tmp;
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 	hfs_dbg(ATTR_MOD, "init_hdr_attr_file: clump %u, node_size %u\n",
68*4882a593Smuzhiyun 		clump_size, node_size);
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 	/* The end of the node contains list of record offsets */
71*4882a593Smuzhiyun 	rec_offsets = (__be16 *)(buf + node_size);
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	desc = (struct hfs_bnode_desc *)buf;
74*4882a593Smuzhiyun 	desc->type = HFS_NODE_HEADER;
75*4882a593Smuzhiyun 	desc->num_recs = cpu_to_be16(HFSPLUS_BTREE_HDR_NODE_RECS_COUNT);
76*4882a593Smuzhiyun 	offset = sizeof(struct hfs_bnode_desc);
77*4882a593Smuzhiyun 	*--rec_offsets = cpu_to_be16(offset);
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 	head = (struct hfs_btree_header_rec *)(buf + offset);
80*4882a593Smuzhiyun 	head->node_size = cpu_to_be16(node_size);
81*4882a593Smuzhiyun 	tmp = i_size_read(attr_file);
82*4882a593Smuzhiyun 	do_div(tmp, node_size);
83*4882a593Smuzhiyun 	head->node_count = cpu_to_be32(tmp);
84*4882a593Smuzhiyun 	head->free_nodes = cpu_to_be32(be32_to_cpu(head->node_count) - 1);
85*4882a593Smuzhiyun 	head->clump_size = cpu_to_be32(clump_size);
86*4882a593Smuzhiyun 	head->attributes |= cpu_to_be32(HFS_TREE_BIGKEYS | HFS_TREE_VARIDXKEYS);
87*4882a593Smuzhiyun 	head->max_key_len = cpu_to_be16(HFSPLUS_ATTR_KEYLEN - sizeof(u16));
88*4882a593Smuzhiyun 	offset += sizeof(struct hfs_btree_header_rec);
89*4882a593Smuzhiyun 	*--rec_offsets = cpu_to_be16(offset);
90*4882a593Smuzhiyun 	offset += HFSPLUS_BTREE_HDR_USER_BYTES;
91*4882a593Smuzhiyun 	*--rec_offsets = cpu_to_be16(offset);
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun 	hdr_node_map_rec_bits = 8 * (node_size - offset - (4 * sizeof(u16)));
94*4882a593Smuzhiyun 	if (be32_to_cpu(head->node_count) > hdr_node_map_rec_bits) {
95*4882a593Smuzhiyun 		u32 map_node_bits;
96*4882a593Smuzhiyun 		u32 map_nodes;
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 		desc->next = cpu_to_be32(be32_to_cpu(head->leaf_tail) + 1);
99*4882a593Smuzhiyun 		map_node_bits = 8 * (node_size - sizeof(struct hfs_bnode_desc) -
100*4882a593Smuzhiyun 					(2 * sizeof(u16)) - 2);
101*4882a593Smuzhiyun 		map_nodes = (be32_to_cpu(head->node_count) -
102*4882a593Smuzhiyun 				hdr_node_map_rec_bits +
103*4882a593Smuzhiyun 				(map_node_bits - 1)) / map_node_bits;
104*4882a593Smuzhiyun 		be32_add_cpu(&head->free_nodes, 0 - map_nodes);
105*4882a593Smuzhiyun 	}
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	bmp = buf + offset;
108*4882a593Smuzhiyun 	used_nodes =
109*4882a593Smuzhiyun 		be32_to_cpu(head->node_count) - be32_to_cpu(head->free_nodes);
110*4882a593Smuzhiyun 	used_bmp_bytes = used_nodes / 8;
111*4882a593Smuzhiyun 	if (used_bmp_bytes) {
112*4882a593Smuzhiyun 		memset(bmp, 0xFF, used_bmp_bytes);
113*4882a593Smuzhiyun 		bmp += used_bmp_bytes;
114*4882a593Smuzhiyun 		used_nodes %= 8;
115*4882a593Smuzhiyun 	}
116*4882a593Smuzhiyun 	*bmp = ~(0xFF >> used_nodes);
117*4882a593Smuzhiyun 	offset += hdr_node_map_rec_bits / 8;
118*4882a593Smuzhiyun 	*--rec_offsets = cpu_to_be16(offset);
119*4882a593Smuzhiyun }
120*4882a593Smuzhiyun 
hfsplus_create_attributes_file(struct super_block * sb)121*4882a593Smuzhiyun static int hfsplus_create_attributes_file(struct super_block *sb)
122*4882a593Smuzhiyun {
123*4882a593Smuzhiyun 	int err = 0;
124*4882a593Smuzhiyun 	struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb);
125*4882a593Smuzhiyun 	struct inode *attr_file;
126*4882a593Smuzhiyun 	struct hfsplus_inode_info *hip;
127*4882a593Smuzhiyun 	u32 clump_size;
128*4882a593Smuzhiyun 	u16 node_size = HFSPLUS_ATTR_TREE_NODE_SIZE;
129*4882a593Smuzhiyun 	char *buf;
130*4882a593Smuzhiyun 	int index, written;
131*4882a593Smuzhiyun 	struct address_space *mapping;
132*4882a593Smuzhiyun 	struct page *page;
133*4882a593Smuzhiyun 	int old_state = HFSPLUS_EMPTY_ATTR_TREE;
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	hfs_dbg(ATTR_MOD, "create_attr_file: ino %d\n", HFSPLUS_ATTR_CNID);
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun check_attr_tree_state_again:
138*4882a593Smuzhiyun 	switch (atomic_read(&sbi->attr_tree_state)) {
139*4882a593Smuzhiyun 	case HFSPLUS_EMPTY_ATTR_TREE:
140*4882a593Smuzhiyun 		if (old_state != atomic_cmpxchg(&sbi->attr_tree_state,
141*4882a593Smuzhiyun 						old_state,
142*4882a593Smuzhiyun 						HFSPLUS_CREATING_ATTR_TREE))
143*4882a593Smuzhiyun 			goto check_attr_tree_state_again;
144*4882a593Smuzhiyun 		break;
145*4882a593Smuzhiyun 	case HFSPLUS_CREATING_ATTR_TREE:
146*4882a593Smuzhiyun 		/*
147*4882a593Smuzhiyun 		 * This state means that another thread is in process
148*4882a593Smuzhiyun 		 * of AttributesFile creation. Theoretically, it is
149*4882a593Smuzhiyun 		 * possible to be here. But really __setxattr() method
150*4882a593Smuzhiyun 		 * first of all calls hfs_find_init() for lookup in
151*4882a593Smuzhiyun 		 * B-tree of CatalogFile. This method locks mutex of
152*4882a593Smuzhiyun 		 * CatalogFile's B-tree. As a result, if some thread
153*4882a593Smuzhiyun 		 * is inside AttributedFile creation operation then
154*4882a593Smuzhiyun 		 * another threads will be waiting unlocking of
155*4882a593Smuzhiyun 		 * CatalogFile's B-tree's mutex. However, if code will
156*4882a593Smuzhiyun 		 * change then we will return error code (-EAGAIN) from
157*4882a593Smuzhiyun 		 * here. Really, it means that first try to set of xattr
158*4882a593Smuzhiyun 		 * fails with error but second attempt will have success.
159*4882a593Smuzhiyun 		 */
160*4882a593Smuzhiyun 		return -EAGAIN;
161*4882a593Smuzhiyun 	case HFSPLUS_VALID_ATTR_TREE:
162*4882a593Smuzhiyun 		return 0;
163*4882a593Smuzhiyun 	case HFSPLUS_FAILED_ATTR_TREE:
164*4882a593Smuzhiyun 		return -EOPNOTSUPP;
165*4882a593Smuzhiyun 	default:
166*4882a593Smuzhiyun 		BUG();
167*4882a593Smuzhiyun 	}
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	attr_file = hfsplus_iget(sb, HFSPLUS_ATTR_CNID);
170*4882a593Smuzhiyun 	if (IS_ERR(attr_file)) {
171*4882a593Smuzhiyun 		pr_err("failed to load attributes file\n");
172*4882a593Smuzhiyun 		return PTR_ERR(attr_file);
173*4882a593Smuzhiyun 	}
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	BUG_ON(i_size_read(attr_file) != 0);
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	hip = HFSPLUS_I(attr_file);
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	clump_size = hfsplus_calc_btree_clump_size(sb->s_blocksize,
180*4882a593Smuzhiyun 						    node_size,
181*4882a593Smuzhiyun 						    sbi->sect_count,
182*4882a593Smuzhiyun 						    HFSPLUS_ATTR_CNID);
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	mutex_lock(&hip->extents_lock);
185*4882a593Smuzhiyun 	hip->clump_blocks = clump_size >> sbi->alloc_blksz_shift;
186*4882a593Smuzhiyun 	mutex_unlock(&hip->extents_lock);
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	if (sbi->free_blocks <= (hip->clump_blocks << 1)) {
189*4882a593Smuzhiyun 		err = -ENOSPC;
190*4882a593Smuzhiyun 		goto end_attr_file_creation;
191*4882a593Smuzhiyun 	}
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	while (hip->alloc_blocks < hip->clump_blocks) {
194*4882a593Smuzhiyun 		err = hfsplus_file_extend(attr_file, false);
195*4882a593Smuzhiyun 		if (unlikely(err)) {
196*4882a593Smuzhiyun 			pr_err("failed to extend attributes file\n");
197*4882a593Smuzhiyun 			goto end_attr_file_creation;
198*4882a593Smuzhiyun 		}
199*4882a593Smuzhiyun 		hip->phys_size = attr_file->i_size =
200*4882a593Smuzhiyun 			(loff_t)hip->alloc_blocks << sbi->alloc_blksz_shift;
201*4882a593Smuzhiyun 		hip->fs_blocks = hip->alloc_blocks << sbi->fs_shift;
202*4882a593Smuzhiyun 		inode_set_bytes(attr_file, attr_file->i_size);
203*4882a593Smuzhiyun 	}
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	buf = kzalloc(node_size, GFP_NOFS);
206*4882a593Smuzhiyun 	if (!buf) {
207*4882a593Smuzhiyun 		pr_err("failed to allocate memory for header node\n");
208*4882a593Smuzhiyun 		err = -ENOMEM;
209*4882a593Smuzhiyun 		goto end_attr_file_creation;
210*4882a593Smuzhiyun 	}
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	hfsplus_init_header_node(attr_file, clump_size, buf, node_size);
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	mapping = attr_file->i_mapping;
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	index = 0;
217*4882a593Smuzhiyun 	written = 0;
218*4882a593Smuzhiyun 	for (; written < node_size; index++, written += PAGE_SIZE) {
219*4882a593Smuzhiyun 		void *kaddr;
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun 		page = read_mapping_page(mapping, index, NULL);
222*4882a593Smuzhiyun 		if (IS_ERR(page)) {
223*4882a593Smuzhiyun 			err = PTR_ERR(page);
224*4882a593Smuzhiyun 			goto failed_header_node_init;
225*4882a593Smuzhiyun 		}
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 		kaddr = kmap_atomic(page);
228*4882a593Smuzhiyun 		memcpy(kaddr, buf + written,
229*4882a593Smuzhiyun 			min_t(size_t, PAGE_SIZE, node_size - written));
230*4882a593Smuzhiyun 		kunmap_atomic(kaddr);
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 		set_page_dirty(page);
233*4882a593Smuzhiyun 		put_page(page);
234*4882a593Smuzhiyun 	}
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun 	hfsplus_mark_inode_dirty(attr_file, HFSPLUS_I_ATTR_DIRTY);
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun 	sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID);
239*4882a593Smuzhiyun 	if (!sbi->attr_tree)
240*4882a593Smuzhiyun 		pr_err("failed to load attributes file\n");
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun failed_header_node_init:
243*4882a593Smuzhiyun 	kfree(buf);
244*4882a593Smuzhiyun 
245*4882a593Smuzhiyun end_attr_file_creation:
246*4882a593Smuzhiyun 	iput(attr_file);
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 	if (!err)
249*4882a593Smuzhiyun 		atomic_set(&sbi->attr_tree_state, HFSPLUS_VALID_ATTR_TREE);
250*4882a593Smuzhiyun 	else if (err == -ENOSPC)
251*4882a593Smuzhiyun 		atomic_set(&sbi->attr_tree_state, HFSPLUS_EMPTY_ATTR_TREE);
252*4882a593Smuzhiyun 	else
253*4882a593Smuzhiyun 		atomic_set(&sbi->attr_tree_state, HFSPLUS_FAILED_ATTR_TREE);
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	return err;
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun 
__hfsplus_setxattr(struct inode * inode,const char * name,const void * value,size_t size,int flags)258*4882a593Smuzhiyun int __hfsplus_setxattr(struct inode *inode, const char *name,
259*4882a593Smuzhiyun 			const void *value, size_t size, int flags)
260*4882a593Smuzhiyun {
261*4882a593Smuzhiyun 	int err = 0;
262*4882a593Smuzhiyun 	struct hfs_find_data cat_fd;
263*4882a593Smuzhiyun 	hfsplus_cat_entry entry;
264*4882a593Smuzhiyun 	u16 cat_entry_flags, cat_entry_type;
265*4882a593Smuzhiyun 	u16 folder_finderinfo_len = sizeof(struct DInfo) +
266*4882a593Smuzhiyun 					sizeof(struct DXInfo);
267*4882a593Smuzhiyun 	u16 file_finderinfo_len = sizeof(struct FInfo) +
268*4882a593Smuzhiyun 					sizeof(struct FXInfo);
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 	if ((!S_ISREG(inode->i_mode) &&
271*4882a593Smuzhiyun 			!S_ISDIR(inode->i_mode)) ||
272*4882a593Smuzhiyun 				HFSPLUS_IS_RSRC(inode))
273*4882a593Smuzhiyun 		return -EOPNOTSUPP;
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	if (value == NULL)
276*4882a593Smuzhiyun 		return hfsplus_removexattr(inode, name);
277*4882a593Smuzhiyun 
278*4882a593Smuzhiyun 	err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
279*4882a593Smuzhiyun 	if (err) {
280*4882a593Smuzhiyun 		pr_err("can't init xattr find struct\n");
281*4882a593Smuzhiyun 		return err;
282*4882a593Smuzhiyun 	}
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
285*4882a593Smuzhiyun 	if (err) {
286*4882a593Smuzhiyun 		pr_err("catalog searching failed\n");
287*4882a593Smuzhiyun 		goto end_setxattr;
288*4882a593Smuzhiyun 	}
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	if (!strcmp_xattr_finder_info(name)) {
291*4882a593Smuzhiyun 		if (flags & XATTR_CREATE) {
292*4882a593Smuzhiyun 			pr_err("xattr exists yet\n");
293*4882a593Smuzhiyun 			err = -EOPNOTSUPP;
294*4882a593Smuzhiyun 			goto end_setxattr;
295*4882a593Smuzhiyun 		}
296*4882a593Smuzhiyun 		hfs_bnode_read(cat_fd.bnode, &entry, cat_fd.entryoffset,
297*4882a593Smuzhiyun 					sizeof(hfsplus_cat_entry));
298*4882a593Smuzhiyun 		if (be16_to_cpu(entry.type) == HFSPLUS_FOLDER) {
299*4882a593Smuzhiyun 			if (size == folder_finderinfo_len) {
300*4882a593Smuzhiyun 				memcpy(&entry.folder.user_info, value,
301*4882a593Smuzhiyun 						folder_finderinfo_len);
302*4882a593Smuzhiyun 				hfs_bnode_write(cat_fd.bnode, &entry,
303*4882a593Smuzhiyun 					cat_fd.entryoffset,
304*4882a593Smuzhiyun 					sizeof(struct hfsplus_cat_folder));
305*4882a593Smuzhiyun 				hfsplus_mark_inode_dirty(inode,
306*4882a593Smuzhiyun 						HFSPLUS_I_CAT_DIRTY);
307*4882a593Smuzhiyun 			} else {
308*4882a593Smuzhiyun 				err = -ERANGE;
309*4882a593Smuzhiyun 				goto end_setxattr;
310*4882a593Smuzhiyun 			}
311*4882a593Smuzhiyun 		} else if (be16_to_cpu(entry.type) == HFSPLUS_FILE) {
312*4882a593Smuzhiyun 			if (size == file_finderinfo_len) {
313*4882a593Smuzhiyun 				memcpy(&entry.file.user_info, value,
314*4882a593Smuzhiyun 						file_finderinfo_len);
315*4882a593Smuzhiyun 				hfs_bnode_write(cat_fd.bnode, &entry,
316*4882a593Smuzhiyun 					cat_fd.entryoffset,
317*4882a593Smuzhiyun 					sizeof(struct hfsplus_cat_file));
318*4882a593Smuzhiyun 				hfsplus_mark_inode_dirty(inode,
319*4882a593Smuzhiyun 						HFSPLUS_I_CAT_DIRTY);
320*4882a593Smuzhiyun 			} else {
321*4882a593Smuzhiyun 				err = -ERANGE;
322*4882a593Smuzhiyun 				goto end_setxattr;
323*4882a593Smuzhiyun 			}
324*4882a593Smuzhiyun 		} else {
325*4882a593Smuzhiyun 			err = -EOPNOTSUPP;
326*4882a593Smuzhiyun 			goto end_setxattr;
327*4882a593Smuzhiyun 		}
328*4882a593Smuzhiyun 		goto end_setxattr;
329*4882a593Smuzhiyun 	}
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	if (!HFSPLUS_SB(inode->i_sb)->attr_tree) {
332*4882a593Smuzhiyun 		err = hfsplus_create_attributes_file(inode->i_sb);
333*4882a593Smuzhiyun 		if (unlikely(err))
334*4882a593Smuzhiyun 			goto end_setxattr;
335*4882a593Smuzhiyun 	}
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	if (hfsplus_attr_exists(inode, name)) {
338*4882a593Smuzhiyun 		if (flags & XATTR_CREATE) {
339*4882a593Smuzhiyun 			pr_err("xattr exists yet\n");
340*4882a593Smuzhiyun 			err = -EOPNOTSUPP;
341*4882a593Smuzhiyun 			goto end_setxattr;
342*4882a593Smuzhiyun 		}
343*4882a593Smuzhiyun 		err = hfsplus_delete_attr(inode, name);
344*4882a593Smuzhiyun 		if (err)
345*4882a593Smuzhiyun 			goto end_setxattr;
346*4882a593Smuzhiyun 		err = hfsplus_create_attr(inode, name, value, size);
347*4882a593Smuzhiyun 		if (err)
348*4882a593Smuzhiyun 			goto end_setxattr;
349*4882a593Smuzhiyun 	} else {
350*4882a593Smuzhiyun 		if (flags & XATTR_REPLACE) {
351*4882a593Smuzhiyun 			pr_err("cannot replace xattr\n");
352*4882a593Smuzhiyun 			err = -EOPNOTSUPP;
353*4882a593Smuzhiyun 			goto end_setxattr;
354*4882a593Smuzhiyun 		}
355*4882a593Smuzhiyun 		err = hfsplus_create_attr(inode, name, value, size);
356*4882a593Smuzhiyun 		if (err)
357*4882a593Smuzhiyun 			goto end_setxattr;
358*4882a593Smuzhiyun 	}
359*4882a593Smuzhiyun 
360*4882a593Smuzhiyun 	cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
361*4882a593Smuzhiyun 	if (cat_entry_type == HFSPLUS_FOLDER) {
362*4882a593Smuzhiyun 		cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
363*4882a593Smuzhiyun 				    cat_fd.entryoffset +
364*4882a593Smuzhiyun 				    offsetof(struct hfsplus_cat_folder, flags));
365*4882a593Smuzhiyun 		cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
366*4882a593Smuzhiyun 		if (!strcmp_xattr_acl(name))
367*4882a593Smuzhiyun 			cat_entry_flags |= HFSPLUS_ACL_EXISTS;
368*4882a593Smuzhiyun 		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
369*4882a593Smuzhiyun 				offsetof(struct hfsplus_cat_folder, flags),
370*4882a593Smuzhiyun 				cat_entry_flags);
371*4882a593Smuzhiyun 		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
372*4882a593Smuzhiyun 	} else if (cat_entry_type == HFSPLUS_FILE) {
373*4882a593Smuzhiyun 		cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
374*4882a593Smuzhiyun 				    cat_fd.entryoffset +
375*4882a593Smuzhiyun 				    offsetof(struct hfsplus_cat_file, flags));
376*4882a593Smuzhiyun 		cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
377*4882a593Smuzhiyun 		if (!strcmp_xattr_acl(name))
378*4882a593Smuzhiyun 			cat_entry_flags |= HFSPLUS_ACL_EXISTS;
379*4882a593Smuzhiyun 		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
380*4882a593Smuzhiyun 				    offsetof(struct hfsplus_cat_file, flags),
381*4882a593Smuzhiyun 				    cat_entry_flags);
382*4882a593Smuzhiyun 		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
383*4882a593Smuzhiyun 	} else {
384*4882a593Smuzhiyun 		pr_err("invalid catalog entry type\n");
385*4882a593Smuzhiyun 		err = -EIO;
386*4882a593Smuzhiyun 		goto end_setxattr;
387*4882a593Smuzhiyun 	}
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun end_setxattr:
390*4882a593Smuzhiyun 	hfs_find_exit(&cat_fd);
391*4882a593Smuzhiyun 	return err;
392*4882a593Smuzhiyun }
393*4882a593Smuzhiyun 
name_len(const char * xattr_name,int xattr_name_len)394*4882a593Smuzhiyun static int name_len(const char *xattr_name, int xattr_name_len)
395*4882a593Smuzhiyun {
396*4882a593Smuzhiyun 	int len = xattr_name_len + 1;
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 	if (!is_known_namespace(xattr_name))
399*4882a593Smuzhiyun 		len += XATTR_MAC_OSX_PREFIX_LEN;
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	return len;
402*4882a593Smuzhiyun }
403*4882a593Smuzhiyun 
copy_name(char * buffer,const char * xattr_name,int name_len)404*4882a593Smuzhiyun static int copy_name(char *buffer, const char *xattr_name, int name_len)
405*4882a593Smuzhiyun {
406*4882a593Smuzhiyun 	int len = name_len;
407*4882a593Smuzhiyun 	int offset = 0;
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun 	if (!is_known_namespace(xattr_name)) {
410*4882a593Smuzhiyun 		memcpy(buffer, XATTR_MAC_OSX_PREFIX, XATTR_MAC_OSX_PREFIX_LEN);
411*4882a593Smuzhiyun 		offset += XATTR_MAC_OSX_PREFIX_LEN;
412*4882a593Smuzhiyun 		len += XATTR_MAC_OSX_PREFIX_LEN;
413*4882a593Smuzhiyun 	}
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	strncpy(buffer + offset, xattr_name, name_len);
416*4882a593Smuzhiyun 	memset(buffer + offset + name_len, 0, 1);
417*4882a593Smuzhiyun 	len += 1;
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun 	return len;
420*4882a593Smuzhiyun }
421*4882a593Smuzhiyun 
hfsplus_setxattr(struct inode * inode,const char * name,const void * value,size_t size,int flags,const char * prefix,size_t prefixlen)422*4882a593Smuzhiyun int hfsplus_setxattr(struct inode *inode, const char *name,
423*4882a593Smuzhiyun 		     const void *value, size_t size, int flags,
424*4882a593Smuzhiyun 		     const char *prefix, size_t prefixlen)
425*4882a593Smuzhiyun {
426*4882a593Smuzhiyun 	char *xattr_name;
427*4882a593Smuzhiyun 	int res;
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun 	xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1,
430*4882a593Smuzhiyun 		GFP_KERNEL);
431*4882a593Smuzhiyun 	if (!xattr_name)
432*4882a593Smuzhiyun 		return -ENOMEM;
433*4882a593Smuzhiyun 	strcpy(xattr_name, prefix);
434*4882a593Smuzhiyun 	strcpy(xattr_name + prefixlen, name);
435*4882a593Smuzhiyun 	res = __hfsplus_setxattr(inode, xattr_name, value, size, flags);
436*4882a593Smuzhiyun 	kfree(xattr_name);
437*4882a593Smuzhiyun 	return res;
438*4882a593Smuzhiyun }
439*4882a593Smuzhiyun 
hfsplus_getxattr_finder_info(struct inode * inode,void * value,size_t size)440*4882a593Smuzhiyun static ssize_t hfsplus_getxattr_finder_info(struct inode *inode,
441*4882a593Smuzhiyun 						void *value, size_t size)
442*4882a593Smuzhiyun {
443*4882a593Smuzhiyun 	ssize_t res = 0;
444*4882a593Smuzhiyun 	struct hfs_find_data fd;
445*4882a593Smuzhiyun 	u16 entry_type;
446*4882a593Smuzhiyun 	u16 folder_rec_len = sizeof(struct DInfo) + sizeof(struct DXInfo);
447*4882a593Smuzhiyun 	u16 file_rec_len = sizeof(struct FInfo) + sizeof(struct FXInfo);
448*4882a593Smuzhiyun 	u16 record_len = max(folder_rec_len, file_rec_len);
449*4882a593Smuzhiyun 	u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
450*4882a593Smuzhiyun 	u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	if (size >= record_len) {
453*4882a593Smuzhiyun 		res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
454*4882a593Smuzhiyun 		if (res) {
455*4882a593Smuzhiyun 			pr_err("can't init xattr find struct\n");
456*4882a593Smuzhiyun 			return res;
457*4882a593Smuzhiyun 		}
458*4882a593Smuzhiyun 		res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
459*4882a593Smuzhiyun 		if (res)
460*4882a593Smuzhiyun 			goto end_getxattr_finder_info;
461*4882a593Smuzhiyun 		entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
462*4882a593Smuzhiyun 
463*4882a593Smuzhiyun 		if (entry_type == HFSPLUS_FOLDER) {
464*4882a593Smuzhiyun 			hfs_bnode_read(fd.bnode, folder_finder_info,
465*4882a593Smuzhiyun 				fd.entryoffset +
466*4882a593Smuzhiyun 				offsetof(struct hfsplus_cat_folder, user_info),
467*4882a593Smuzhiyun 				folder_rec_len);
468*4882a593Smuzhiyun 			memcpy(value, folder_finder_info, folder_rec_len);
469*4882a593Smuzhiyun 			res = folder_rec_len;
470*4882a593Smuzhiyun 		} else if (entry_type == HFSPLUS_FILE) {
471*4882a593Smuzhiyun 			hfs_bnode_read(fd.bnode, file_finder_info,
472*4882a593Smuzhiyun 				fd.entryoffset +
473*4882a593Smuzhiyun 				offsetof(struct hfsplus_cat_file, user_info),
474*4882a593Smuzhiyun 				file_rec_len);
475*4882a593Smuzhiyun 			memcpy(value, file_finder_info, file_rec_len);
476*4882a593Smuzhiyun 			res = file_rec_len;
477*4882a593Smuzhiyun 		} else {
478*4882a593Smuzhiyun 			res = -EOPNOTSUPP;
479*4882a593Smuzhiyun 			goto end_getxattr_finder_info;
480*4882a593Smuzhiyun 		}
481*4882a593Smuzhiyun 	} else
482*4882a593Smuzhiyun 		res = size ? -ERANGE : record_len;
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun end_getxattr_finder_info:
485*4882a593Smuzhiyun 	if (size >= record_len)
486*4882a593Smuzhiyun 		hfs_find_exit(&fd);
487*4882a593Smuzhiyun 	return res;
488*4882a593Smuzhiyun }
489*4882a593Smuzhiyun 
__hfsplus_getxattr(struct inode * inode,const char * name,void * value,size_t size)490*4882a593Smuzhiyun ssize_t __hfsplus_getxattr(struct inode *inode, const char *name,
491*4882a593Smuzhiyun 			 void *value, size_t size)
492*4882a593Smuzhiyun {
493*4882a593Smuzhiyun 	struct hfs_find_data fd;
494*4882a593Smuzhiyun 	hfsplus_attr_entry *entry;
495*4882a593Smuzhiyun 	__be32 xattr_record_type;
496*4882a593Smuzhiyun 	u32 record_type;
497*4882a593Smuzhiyun 	u16 record_length = 0;
498*4882a593Smuzhiyun 	ssize_t res = 0;
499*4882a593Smuzhiyun 
500*4882a593Smuzhiyun 	if ((!S_ISREG(inode->i_mode) &&
501*4882a593Smuzhiyun 			!S_ISDIR(inode->i_mode)) ||
502*4882a593Smuzhiyun 				HFSPLUS_IS_RSRC(inode))
503*4882a593Smuzhiyun 		return -EOPNOTSUPP;
504*4882a593Smuzhiyun 
505*4882a593Smuzhiyun 	if (!strcmp_xattr_finder_info(name))
506*4882a593Smuzhiyun 		return hfsplus_getxattr_finder_info(inode, value, size);
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun 	if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
509*4882a593Smuzhiyun 		return -EOPNOTSUPP;
510*4882a593Smuzhiyun 
511*4882a593Smuzhiyun 	entry = hfsplus_alloc_attr_entry();
512*4882a593Smuzhiyun 	if (!entry) {
513*4882a593Smuzhiyun 		pr_err("can't allocate xattr entry\n");
514*4882a593Smuzhiyun 		return -ENOMEM;
515*4882a593Smuzhiyun 	}
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun 	res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
518*4882a593Smuzhiyun 	if (res) {
519*4882a593Smuzhiyun 		pr_err("can't init xattr find struct\n");
520*4882a593Smuzhiyun 		goto failed_getxattr_init;
521*4882a593Smuzhiyun 	}
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun 	res = hfsplus_find_attr(inode->i_sb, inode->i_ino, name, &fd);
524*4882a593Smuzhiyun 	if (res) {
525*4882a593Smuzhiyun 		if (res == -ENOENT)
526*4882a593Smuzhiyun 			res = -ENODATA;
527*4882a593Smuzhiyun 		else
528*4882a593Smuzhiyun 			pr_err("xattr searching failed\n");
529*4882a593Smuzhiyun 		goto out;
530*4882a593Smuzhiyun 	}
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun 	hfs_bnode_read(fd.bnode, &xattr_record_type,
533*4882a593Smuzhiyun 			fd.entryoffset, sizeof(xattr_record_type));
534*4882a593Smuzhiyun 	record_type = be32_to_cpu(xattr_record_type);
535*4882a593Smuzhiyun 	if (record_type == HFSPLUS_ATTR_INLINE_DATA) {
536*4882a593Smuzhiyun 		record_length = hfs_bnode_read_u16(fd.bnode,
537*4882a593Smuzhiyun 				fd.entryoffset +
538*4882a593Smuzhiyun 				offsetof(struct hfsplus_attr_inline_data,
539*4882a593Smuzhiyun 				length));
540*4882a593Smuzhiyun 		if (record_length > HFSPLUS_MAX_INLINE_DATA_SIZE) {
541*4882a593Smuzhiyun 			pr_err("invalid xattr record size\n");
542*4882a593Smuzhiyun 			res = -EIO;
543*4882a593Smuzhiyun 			goto out;
544*4882a593Smuzhiyun 		}
545*4882a593Smuzhiyun 	} else if (record_type == HFSPLUS_ATTR_FORK_DATA ||
546*4882a593Smuzhiyun 			record_type == HFSPLUS_ATTR_EXTENTS) {
547*4882a593Smuzhiyun 		pr_err("only inline data xattr are supported\n");
548*4882a593Smuzhiyun 		res = -EOPNOTSUPP;
549*4882a593Smuzhiyun 		goto out;
550*4882a593Smuzhiyun 	} else {
551*4882a593Smuzhiyun 		pr_err("invalid xattr record\n");
552*4882a593Smuzhiyun 		res = -EIO;
553*4882a593Smuzhiyun 		goto out;
554*4882a593Smuzhiyun 	}
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun 	if (size) {
557*4882a593Smuzhiyun 		hfs_bnode_read(fd.bnode, entry, fd.entryoffset,
558*4882a593Smuzhiyun 				offsetof(struct hfsplus_attr_inline_data,
559*4882a593Smuzhiyun 					raw_bytes) + record_length);
560*4882a593Smuzhiyun 	}
561*4882a593Smuzhiyun 
562*4882a593Smuzhiyun 	if (size >= record_length) {
563*4882a593Smuzhiyun 		memcpy(value, entry->inline_data.raw_bytes, record_length);
564*4882a593Smuzhiyun 		res = record_length;
565*4882a593Smuzhiyun 	} else
566*4882a593Smuzhiyun 		res = size ? -ERANGE : record_length;
567*4882a593Smuzhiyun 
568*4882a593Smuzhiyun out:
569*4882a593Smuzhiyun 	hfs_find_exit(&fd);
570*4882a593Smuzhiyun 
571*4882a593Smuzhiyun failed_getxattr_init:
572*4882a593Smuzhiyun 	hfsplus_destroy_attr_entry(entry);
573*4882a593Smuzhiyun 	return res;
574*4882a593Smuzhiyun }
575*4882a593Smuzhiyun 
hfsplus_getxattr(struct inode * inode,const char * name,void * value,size_t size,const char * prefix,size_t prefixlen)576*4882a593Smuzhiyun ssize_t hfsplus_getxattr(struct inode *inode, const char *name,
577*4882a593Smuzhiyun 			 void *value, size_t size,
578*4882a593Smuzhiyun 			 const char *prefix, size_t prefixlen)
579*4882a593Smuzhiyun {
580*4882a593Smuzhiyun 	int res;
581*4882a593Smuzhiyun 	char *xattr_name;
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun 	xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1,
584*4882a593Smuzhiyun 			     GFP_KERNEL);
585*4882a593Smuzhiyun 	if (!xattr_name)
586*4882a593Smuzhiyun 		return -ENOMEM;
587*4882a593Smuzhiyun 
588*4882a593Smuzhiyun 	strcpy(xattr_name, prefix);
589*4882a593Smuzhiyun 	strcpy(xattr_name + prefixlen, name);
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun 	res = __hfsplus_getxattr(inode, xattr_name, value, size);
592*4882a593Smuzhiyun 	kfree(xattr_name);
593*4882a593Smuzhiyun 	return res;
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun }
596*4882a593Smuzhiyun 
can_list(const char * xattr_name)597*4882a593Smuzhiyun static inline int can_list(const char *xattr_name)
598*4882a593Smuzhiyun {
599*4882a593Smuzhiyun 	if (!xattr_name)
600*4882a593Smuzhiyun 		return 0;
601*4882a593Smuzhiyun 
602*4882a593Smuzhiyun 	return strncmp(xattr_name, XATTR_TRUSTED_PREFIX,
603*4882a593Smuzhiyun 			XATTR_TRUSTED_PREFIX_LEN) ||
604*4882a593Smuzhiyun 				capable(CAP_SYS_ADMIN);
605*4882a593Smuzhiyun }
606*4882a593Smuzhiyun 
hfsplus_listxattr_finder_info(struct dentry * dentry,char * buffer,size_t size)607*4882a593Smuzhiyun static ssize_t hfsplus_listxattr_finder_info(struct dentry *dentry,
608*4882a593Smuzhiyun 						char *buffer, size_t size)
609*4882a593Smuzhiyun {
610*4882a593Smuzhiyun 	ssize_t res = 0;
611*4882a593Smuzhiyun 	struct inode *inode = d_inode(dentry);
612*4882a593Smuzhiyun 	struct hfs_find_data fd;
613*4882a593Smuzhiyun 	u16 entry_type;
614*4882a593Smuzhiyun 	u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
615*4882a593Smuzhiyun 	u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
616*4882a593Smuzhiyun 	unsigned long len, found_bit;
617*4882a593Smuzhiyun 	int xattr_name_len, symbols_count;
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 	res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
620*4882a593Smuzhiyun 	if (res) {
621*4882a593Smuzhiyun 		pr_err("can't init xattr find struct\n");
622*4882a593Smuzhiyun 		return res;
623*4882a593Smuzhiyun 	}
624*4882a593Smuzhiyun 
625*4882a593Smuzhiyun 	res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
626*4882a593Smuzhiyun 	if (res)
627*4882a593Smuzhiyun 		goto end_listxattr_finder_info;
628*4882a593Smuzhiyun 
629*4882a593Smuzhiyun 	entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
630*4882a593Smuzhiyun 	if (entry_type == HFSPLUS_FOLDER) {
631*4882a593Smuzhiyun 		len = sizeof(struct DInfo) + sizeof(struct DXInfo);
632*4882a593Smuzhiyun 		hfs_bnode_read(fd.bnode, folder_finder_info,
633*4882a593Smuzhiyun 				fd.entryoffset +
634*4882a593Smuzhiyun 				offsetof(struct hfsplus_cat_folder, user_info),
635*4882a593Smuzhiyun 				len);
636*4882a593Smuzhiyun 		found_bit = find_first_bit((void *)folder_finder_info, len*8);
637*4882a593Smuzhiyun 	} else if (entry_type == HFSPLUS_FILE) {
638*4882a593Smuzhiyun 		len = sizeof(struct FInfo) + sizeof(struct FXInfo);
639*4882a593Smuzhiyun 		hfs_bnode_read(fd.bnode, file_finder_info,
640*4882a593Smuzhiyun 				fd.entryoffset +
641*4882a593Smuzhiyun 				offsetof(struct hfsplus_cat_file, user_info),
642*4882a593Smuzhiyun 				len);
643*4882a593Smuzhiyun 		found_bit = find_first_bit((void *)file_finder_info, len*8);
644*4882a593Smuzhiyun 	} else {
645*4882a593Smuzhiyun 		res = -EOPNOTSUPP;
646*4882a593Smuzhiyun 		goto end_listxattr_finder_info;
647*4882a593Smuzhiyun 	}
648*4882a593Smuzhiyun 
649*4882a593Smuzhiyun 	if (found_bit >= (len*8))
650*4882a593Smuzhiyun 		res = 0;
651*4882a593Smuzhiyun 	else {
652*4882a593Smuzhiyun 		symbols_count = sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME) - 1;
653*4882a593Smuzhiyun 		xattr_name_len =
654*4882a593Smuzhiyun 			name_len(HFSPLUS_XATTR_FINDER_INFO_NAME, symbols_count);
655*4882a593Smuzhiyun 		if (!buffer || !size) {
656*4882a593Smuzhiyun 			if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME))
657*4882a593Smuzhiyun 				res = xattr_name_len;
658*4882a593Smuzhiyun 		} else if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME)) {
659*4882a593Smuzhiyun 			if (size < xattr_name_len)
660*4882a593Smuzhiyun 				res = -ERANGE;
661*4882a593Smuzhiyun 			else {
662*4882a593Smuzhiyun 				res = copy_name(buffer,
663*4882a593Smuzhiyun 						HFSPLUS_XATTR_FINDER_INFO_NAME,
664*4882a593Smuzhiyun 						symbols_count);
665*4882a593Smuzhiyun 			}
666*4882a593Smuzhiyun 		}
667*4882a593Smuzhiyun 	}
668*4882a593Smuzhiyun 
669*4882a593Smuzhiyun end_listxattr_finder_info:
670*4882a593Smuzhiyun 	hfs_find_exit(&fd);
671*4882a593Smuzhiyun 
672*4882a593Smuzhiyun 	return res;
673*4882a593Smuzhiyun }
674*4882a593Smuzhiyun 
hfsplus_listxattr(struct dentry * dentry,char * buffer,size_t size)675*4882a593Smuzhiyun ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
676*4882a593Smuzhiyun {
677*4882a593Smuzhiyun 	ssize_t err;
678*4882a593Smuzhiyun 	ssize_t res = 0;
679*4882a593Smuzhiyun 	struct inode *inode = d_inode(dentry);
680*4882a593Smuzhiyun 	struct hfs_find_data fd;
681*4882a593Smuzhiyun 	u16 key_len = 0;
682*4882a593Smuzhiyun 	struct hfsplus_attr_key attr_key;
683*4882a593Smuzhiyun 	char *strbuf;
684*4882a593Smuzhiyun 	int xattr_name_len;
685*4882a593Smuzhiyun 
686*4882a593Smuzhiyun 	if ((!S_ISREG(inode->i_mode) &&
687*4882a593Smuzhiyun 			!S_ISDIR(inode->i_mode)) ||
688*4882a593Smuzhiyun 				HFSPLUS_IS_RSRC(inode))
689*4882a593Smuzhiyun 		return -EOPNOTSUPP;
690*4882a593Smuzhiyun 
691*4882a593Smuzhiyun 	res = hfsplus_listxattr_finder_info(dentry, buffer, size);
692*4882a593Smuzhiyun 	if (res < 0)
693*4882a593Smuzhiyun 		return res;
694*4882a593Smuzhiyun 	else if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
695*4882a593Smuzhiyun 		return (res == 0) ? -EOPNOTSUPP : res;
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun 	err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
698*4882a593Smuzhiyun 	if (err) {
699*4882a593Smuzhiyun 		pr_err("can't init xattr find struct\n");
700*4882a593Smuzhiyun 		return err;
701*4882a593Smuzhiyun 	}
702*4882a593Smuzhiyun 
703*4882a593Smuzhiyun 	strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN +
704*4882a593Smuzhiyun 			XATTR_MAC_OSX_PREFIX_LEN + 1, GFP_KERNEL);
705*4882a593Smuzhiyun 	if (!strbuf) {
706*4882a593Smuzhiyun 		res = -ENOMEM;
707*4882a593Smuzhiyun 		goto out;
708*4882a593Smuzhiyun 	}
709*4882a593Smuzhiyun 
710*4882a593Smuzhiyun 	err = hfsplus_find_attr(inode->i_sb, inode->i_ino, NULL, &fd);
711*4882a593Smuzhiyun 	if (err) {
712*4882a593Smuzhiyun 		if (err == -ENOENT) {
713*4882a593Smuzhiyun 			if (res == 0)
714*4882a593Smuzhiyun 				res = -ENODATA;
715*4882a593Smuzhiyun 			goto end_listxattr;
716*4882a593Smuzhiyun 		} else {
717*4882a593Smuzhiyun 			res = err;
718*4882a593Smuzhiyun 			goto end_listxattr;
719*4882a593Smuzhiyun 		}
720*4882a593Smuzhiyun 	}
721*4882a593Smuzhiyun 
722*4882a593Smuzhiyun 	for (;;) {
723*4882a593Smuzhiyun 		key_len = hfs_bnode_read_u16(fd.bnode, fd.keyoffset);
724*4882a593Smuzhiyun 		if (key_len == 0 || key_len > fd.tree->max_key_len) {
725*4882a593Smuzhiyun 			pr_err("invalid xattr key length: %d\n", key_len);
726*4882a593Smuzhiyun 			res = -EIO;
727*4882a593Smuzhiyun 			goto end_listxattr;
728*4882a593Smuzhiyun 		}
729*4882a593Smuzhiyun 
730*4882a593Smuzhiyun 		hfs_bnode_read(fd.bnode, &attr_key,
731*4882a593Smuzhiyun 				fd.keyoffset, key_len + sizeof(key_len));
732*4882a593Smuzhiyun 
733*4882a593Smuzhiyun 		if (be32_to_cpu(attr_key.cnid) != inode->i_ino)
734*4882a593Smuzhiyun 			goto end_listxattr;
735*4882a593Smuzhiyun 
736*4882a593Smuzhiyun 		xattr_name_len = NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN;
737*4882a593Smuzhiyun 		if (hfsplus_uni2asc(inode->i_sb,
738*4882a593Smuzhiyun 			(const struct hfsplus_unistr *)&fd.key->attr.key_name,
739*4882a593Smuzhiyun 					strbuf, &xattr_name_len)) {
740*4882a593Smuzhiyun 			pr_err("unicode conversion failed\n");
741*4882a593Smuzhiyun 			res = -EIO;
742*4882a593Smuzhiyun 			goto end_listxattr;
743*4882a593Smuzhiyun 		}
744*4882a593Smuzhiyun 
745*4882a593Smuzhiyun 		if (!buffer || !size) {
746*4882a593Smuzhiyun 			if (can_list(strbuf))
747*4882a593Smuzhiyun 				res += name_len(strbuf, xattr_name_len);
748*4882a593Smuzhiyun 		} else if (can_list(strbuf)) {
749*4882a593Smuzhiyun 			if (size < (res + name_len(strbuf, xattr_name_len))) {
750*4882a593Smuzhiyun 				res = -ERANGE;
751*4882a593Smuzhiyun 				goto end_listxattr;
752*4882a593Smuzhiyun 			} else
753*4882a593Smuzhiyun 				res += copy_name(buffer + res,
754*4882a593Smuzhiyun 						strbuf, xattr_name_len);
755*4882a593Smuzhiyun 		}
756*4882a593Smuzhiyun 
757*4882a593Smuzhiyun 		if (hfs_brec_goto(&fd, 1))
758*4882a593Smuzhiyun 			goto end_listxattr;
759*4882a593Smuzhiyun 	}
760*4882a593Smuzhiyun 
761*4882a593Smuzhiyun end_listxattr:
762*4882a593Smuzhiyun 	kfree(strbuf);
763*4882a593Smuzhiyun out:
764*4882a593Smuzhiyun 	hfs_find_exit(&fd);
765*4882a593Smuzhiyun 	return res;
766*4882a593Smuzhiyun }
767*4882a593Smuzhiyun 
hfsplus_removexattr(struct inode * inode,const char * name)768*4882a593Smuzhiyun static int hfsplus_removexattr(struct inode *inode, const char *name)
769*4882a593Smuzhiyun {
770*4882a593Smuzhiyun 	int err = 0;
771*4882a593Smuzhiyun 	struct hfs_find_data cat_fd;
772*4882a593Smuzhiyun 	u16 flags;
773*4882a593Smuzhiyun 	u16 cat_entry_type;
774*4882a593Smuzhiyun 	int is_xattr_acl_deleted = 0;
775*4882a593Smuzhiyun 	int is_all_xattrs_deleted = 0;
776*4882a593Smuzhiyun 
777*4882a593Smuzhiyun 	if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
778*4882a593Smuzhiyun 		return -EOPNOTSUPP;
779*4882a593Smuzhiyun 
780*4882a593Smuzhiyun 	if (!strcmp_xattr_finder_info(name))
781*4882a593Smuzhiyun 		return -EOPNOTSUPP;
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun 	err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
784*4882a593Smuzhiyun 	if (err) {
785*4882a593Smuzhiyun 		pr_err("can't init xattr find struct\n");
786*4882a593Smuzhiyun 		return err;
787*4882a593Smuzhiyun 	}
788*4882a593Smuzhiyun 
789*4882a593Smuzhiyun 	err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
790*4882a593Smuzhiyun 	if (err) {
791*4882a593Smuzhiyun 		pr_err("catalog searching failed\n");
792*4882a593Smuzhiyun 		goto end_removexattr;
793*4882a593Smuzhiyun 	}
794*4882a593Smuzhiyun 
795*4882a593Smuzhiyun 	err = hfsplus_delete_attr(inode, name);
796*4882a593Smuzhiyun 	if (err)
797*4882a593Smuzhiyun 		goto end_removexattr;
798*4882a593Smuzhiyun 
799*4882a593Smuzhiyun 	is_xattr_acl_deleted = !strcmp_xattr_acl(name);
800*4882a593Smuzhiyun 	is_all_xattrs_deleted = !hfsplus_attr_exists(inode, NULL);
801*4882a593Smuzhiyun 
802*4882a593Smuzhiyun 	if (!is_xattr_acl_deleted && !is_all_xattrs_deleted)
803*4882a593Smuzhiyun 		goto end_removexattr;
804*4882a593Smuzhiyun 
805*4882a593Smuzhiyun 	cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
806*4882a593Smuzhiyun 
807*4882a593Smuzhiyun 	if (cat_entry_type == HFSPLUS_FOLDER) {
808*4882a593Smuzhiyun 		flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset +
809*4882a593Smuzhiyun 				offsetof(struct hfsplus_cat_folder, flags));
810*4882a593Smuzhiyun 		if (is_xattr_acl_deleted)
811*4882a593Smuzhiyun 			flags &= ~HFSPLUS_ACL_EXISTS;
812*4882a593Smuzhiyun 		if (is_all_xattrs_deleted)
813*4882a593Smuzhiyun 			flags &= ~HFSPLUS_XATTR_EXISTS;
814*4882a593Smuzhiyun 		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
815*4882a593Smuzhiyun 				offsetof(struct hfsplus_cat_folder, flags),
816*4882a593Smuzhiyun 				flags);
817*4882a593Smuzhiyun 		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
818*4882a593Smuzhiyun 	} else if (cat_entry_type == HFSPLUS_FILE) {
819*4882a593Smuzhiyun 		flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset +
820*4882a593Smuzhiyun 				offsetof(struct hfsplus_cat_file, flags));
821*4882a593Smuzhiyun 		if (is_xattr_acl_deleted)
822*4882a593Smuzhiyun 			flags &= ~HFSPLUS_ACL_EXISTS;
823*4882a593Smuzhiyun 		if (is_all_xattrs_deleted)
824*4882a593Smuzhiyun 			flags &= ~HFSPLUS_XATTR_EXISTS;
825*4882a593Smuzhiyun 		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
826*4882a593Smuzhiyun 				offsetof(struct hfsplus_cat_file, flags),
827*4882a593Smuzhiyun 				flags);
828*4882a593Smuzhiyun 		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
829*4882a593Smuzhiyun 	} else {
830*4882a593Smuzhiyun 		pr_err("invalid catalog entry type\n");
831*4882a593Smuzhiyun 		err = -EIO;
832*4882a593Smuzhiyun 		goto end_removexattr;
833*4882a593Smuzhiyun 	}
834*4882a593Smuzhiyun 
835*4882a593Smuzhiyun end_removexattr:
836*4882a593Smuzhiyun 	hfs_find_exit(&cat_fd);
837*4882a593Smuzhiyun 	return err;
838*4882a593Smuzhiyun }
839*4882a593Smuzhiyun 
hfsplus_osx_getxattr(const struct xattr_handler * handler,struct dentry * unused,struct inode * inode,const char * name,void * buffer,size_t size,int flags)840*4882a593Smuzhiyun static int hfsplus_osx_getxattr(const struct xattr_handler *handler,
841*4882a593Smuzhiyun 				struct dentry *unused, struct inode *inode,
842*4882a593Smuzhiyun 				const char *name, void *buffer, size_t size,
843*4882a593Smuzhiyun 				int flags)
844*4882a593Smuzhiyun {
845*4882a593Smuzhiyun 	/*
846*4882a593Smuzhiyun 	 * Don't allow retrieving properly prefixed attributes
847*4882a593Smuzhiyun 	 * by prepending them with "osx."
848*4882a593Smuzhiyun 	 */
849*4882a593Smuzhiyun 	if (is_known_namespace(name))
850*4882a593Smuzhiyun 		return -EOPNOTSUPP;
851*4882a593Smuzhiyun 
852*4882a593Smuzhiyun 	/*
853*4882a593Smuzhiyun 	 * osx is the namespace we use to indicate an unprefixed
854*4882a593Smuzhiyun 	 * attribute on the filesystem (like the ones that OS X
855*4882a593Smuzhiyun 	 * creates), so we pass the name through unmodified (after
856*4882a593Smuzhiyun 	 * ensuring it doesn't conflict with another namespace).
857*4882a593Smuzhiyun 	 */
858*4882a593Smuzhiyun 	return __hfsplus_getxattr(inode, name, buffer, size);
859*4882a593Smuzhiyun }
860*4882a593Smuzhiyun 
hfsplus_osx_setxattr(const struct xattr_handler * handler,struct dentry * unused,struct inode * inode,const char * name,const void * buffer,size_t size,int flags)861*4882a593Smuzhiyun static int hfsplus_osx_setxattr(const struct xattr_handler *handler,
862*4882a593Smuzhiyun 				struct dentry *unused, struct inode *inode,
863*4882a593Smuzhiyun 				const char *name, const void *buffer,
864*4882a593Smuzhiyun 				size_t size, int flags)
865*4882a593Smuzhiyun {
866*4882a593Smuzhiyun 	/*
867*4882a593Smuzhiyun 	 * Don't allow setting properly prefixed attributes
868*4882a593Smuzhiyun 	 * by prepending them with "osx."
869*4882a593Smuzhiyun 	 */
870*4882a593Smuzhiyun 	if (is_known_namespace(name))
871*4882a593Smuzhiyun 		return -EOPNOTSUPP;
872*4882a593Smuzhiyun 
873*4882a593Smuzhiyun 	/*
874*4882a593Smuzhiyun 	 * osx is the namespace we use to indicate an unprefixed
875*4882a593Smuzhiyun 	 * attribute on the filesystem (like the ones that OS X
876*4882a593Smuzhiyun 	 * creates), so we pass the name through unmodified (after
877*4882a593Smuzhiyun 	 * ensuring it doesn't conflict with another namespace).
878*4882a593Smuzhiyun 	 */
879*4882a593Smuzhiyun 	return __hfsplus_setxattr(inode, name, buffer, size, flags);
880*4882a593Smuzhiyun }
881*4882a593Smuzhiyun 
882*4882a593Smuzhiyun const struct xattr_handler hfsplus_xattr_osx_handler = {
883*4882a593Smuzhiyun 	.prefix	= XATTR_MAC_OSX_PREFIX,
884*4882a593Smuzhiyun 	.get	= hfsplus_osx_getxattr,
885*4882a593Smuzhiyun 	.set	= hfsplus_osx_setxattr,
886*4882a593Smuzhiyun };
887