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