File Index
Files and directories
Sign in
FILE

xattr.c

File preview with syntax highlighting.

linux-6.19.14/fs/hfsplus/xattr.c C
  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("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("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	if (i_size_read(attr_file) != 0) {
176		err = -EIO;
177		pr_err("detected inconsistent attributes file, running fsck.hfsplus is recommended.\n");
178		goto end_attr_file_creation;
179	}
180
181	hip = HFSPLUS_I(attr_file);
182
183	clump_size = hfsplus_calc_btree_clump_size(sb->s_blocksize,
184						    node_size,
185						    sbi->sect_count,
186						    HFSPLUS_ATTR_CNID);
187
188	mutex_lock(&hip->extents_lock);
189	hip->clump_blocks = clump_size >> sbi->alloc_blksz_shift;
190	mutex_unlock(&hip->extents_lock);
191
192	if (sbi->free_blocks <= (hip->clump_blocks << 1)) {
193		err = -ENOSPC;
194		goto end_attr_file_creation;
195	}
196
197	while (hip->alloc_blocks < hip->clump_blocks) {
198		err = hfsplus_file_extend(attr_file, false);
199		if (unlikely(err)) {
200			pr_err("failed to extend attributes file\n");
201			goto end_attr_file_creation;
202		}
203		hip->phys_size = attr_file->i_size =
204			(loff_t)hip->alloc_blocks << sbi->alloc_blksz_shift;
205		hip->fs_blocks = hip->alloc_blocks << sbi->fs_shift;
206		inode_set_bytes(attr_file, attr_file->i_size);
207	}
208
209	buf = kzalloc(node_size, GFP_NOFS);
210	if (!buf) {
211		err = -ENOMEM;
212		goto end_attr_file_creation;
213	}
214
215	hfsplus_init_header_node(attr_file, clump_size, buf, node_size);
216
217	mapping = attr_file->i_mapping;
218
219	index = 0;
220	written = 0;
221	for (; written < node_size; index++, written += PAGE_SIZE) {
222		void *kaddr;
223
224		page = read_mapping_page(mapping, index, NULL);
225		if (IS_ERR(page)) {
226			err = PTR_ERR(page);
227			goto failed_header_node_init;
228		}
229
230		kaddr = kmap_atomic(page);
231		memcpy(kaddr, buf + written,
232			min_t(size_t, PAGE_SIZE, node_size - written));
233		kunmap_atomic(kaddr);
234
235		set_page_dirty(page);
236		put_page(page);
237	}
238
239	hfsplus_mark_inode_dirty(attr_file, HFSPLUS_I_ATTR_DIRTY);
240
241	sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID);
242	if (!sbi->attr_tree)
243		pr_err("failed to load attributes file\n");
244
245failed_header_node_init:
246	kfree(buf);
247
248end_attr_file_creation:
249	iput(attr_file);
250
251	if (!err)
252		atomic_set(&sbi->attr_tree_state, HFSPLUS_VALID_ATTR_TREE);
253	else if (err == -ENOSPC)
254		atomic_set(&sbi->attr_tree_state, HFSPLUS_EMPTY_ATTR_TREE);
255	else
256		atomic_set(&sbi->attr_tree_state, HFSPLUS_FAILED_ATTR_TREE);
257
258	return err;
259}
260
261int __hfsplus_setxattr(struct inode *inode, const char *name,
262			const void *value, size_t size, int flags)
263{
264	int err;
265	struct hfs_find_data cat_fd;
266	hfsplus_cat_entry entry;
267	u16 cat_entry_flags, cat_entry_type;
268	u16 folder_finderinfo_len = sizeof(DInfo) + sizeof(DXInfo);
269	u16 file_finderinfo_len = sizeof(FInfo) + sizeof(FXInfo);
270
271	if ((!S_ISREG(inode->i_mode) &&
272			!S_ISDIR(inode->i_mode)) ||
273				HFSPLUS_IS_RSRC(inode))
274		return -EOPNOTSUPP;
275
276	if (value == NULL)
277		return hfsplus_removexattr(inode, name);
278
279	err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
280	if (err) {
281		pr_err("can't init xattr find struct\n");
282		return err;
283	}
284
285	err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
286	if (err) {
287		pr_err("catalog searching failed\n");
288		goto end_setxattr;
289	}
290
291	if (!strcmp_xattr_finder_info(name)) {
292		if (flags & XATTR_CREATE) {
293			pr_err("xattr exists yet\n");
294			err = -EOPNOTSUPP;
295			goto end_setxattr;
296		}
297		hfs_bnode_read(cat_fd.bnode, &entry, cat_fd.entryoffset,
298					sizeof(hfsplus_cat_entry));
299		if (be16_to_cpu(entry.type) == HFSPLUS_FOLDER) {
300			if (size == folder_finderinfo_len) {
301				memcpy(&entry.folder.info, value,
302						folder_finderinfo_len);
303				hfs_bnode_write(cat_fd.bnode, &entry,
304					cat_fd.entryoffset,
305					sizeof(struct hfsplus_cat_folder));
306				hfsplus_mark_inode_dirty(inode,
307						HFSPLUS_I_CAT_DIRTY);
308			} else {
309				err = -ERANGE;
310				goto end_setxattr;
311			}
312		} else if (be16_to_cpu(entry.type) == HFSPLUS_FILE) {
313			if (size == file_finderinfo_len) {
314				memcpy(&entry.file.info, value,
315						file_finderinfo_len);
316				hfs_bnode_write(cat_fd.bnode, &entry,
317					cat_fd.entryoffset,
318					sizeof(struct hfsplus_cat_file));
319				hfsplus_mark_inode_dirty(inode,
320						HFSPLUS_I_CAT_DIRTY);
321			} else {
322				err = -ERANGE;
323				goto end_setxattr;
324			}
325		} else {
326			err = -EOPNOTSUPP;
327			goto end_setxattr;
328		}
329		goto end_setxattr;
330	}
331
332	if (!HFSPLUS_SB(inode->i_sb)->attr_tree) {
333		err = hfsplus_create_attributes_file(inode->i_sb);
334		if (unlikely(err))
335			goto end_setxattr;
336	}
337
338	if (hfsplus_attr_exists(inode, name)) {
339		if (flags & XATTR_CREATE) {
340			pr_err("xattr exists yet\n");
341			err = -EOPNOTSUPP;
342			goto end_setxattr;
343		}
344		err = hfsplus_delete_attr(inode, name);
345		if (err)
346			goto end_setxattr;
347		err = hfsplus_create_attr(inode, name, value, size);
348		if (err)
349			goto end_setxattr;
350	} else {
351		if (flags & XATTR_REPLACE) {
352			pr_err("cannot replace xattr\n");
353			err = -EOPNOTSUPP;
354			goto end_setxattr;
355		}
356		err = hfsplus_create_attr(inode, name, value, size);
357		if (err)
358			goto end_setxattr;
359	}
360
361	cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
362	if (cat_entry_type == HFSPLUS_FOLDER) {
363		cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
364				    cat_fd.entryoffset +
365				    offsetof(struct hfsplus_cat_folder, flags));
366		cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
367		if (!strcmp_xattr_acl(name))
368			cat_entry_flags |= HFSPLUS_ACL_EXISTS;
369		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
370				offsetof(struct hfsplus_cat_folder, flags),
371				cat_entry_flags);
372		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
373	} else if (cat_entry_type == HFSPLUS_FILE) {
374		cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
375				    cat_fd.entryoffset +
376				    offsetof(struct hfsplus_cat_file, flags));
377		cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
378		if (!strcmp_xattr_acl(name))
379			cat_entry_flags |= HFSPLUS_ACL_EXISTS;
380		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
381				    offsetof(struct hfsplus_cat_file, flags),
382				    cat_entry_flags);
383		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
384	} else {
385		pr_err("invalid catalog entry type\n");
386		err = -EIO;
387		goto end_setxattr;
388	}
389
390end_setxattr:
391	hfs_find_exit(&cat_fd);
392	return err;
393}
394
395static int name_len(const char *xattr_name, int xattr_name_len)
396{
397	int len = xattr_name_len + 1;
398
399	if (!is_known_namespace(xattr_name))
400		len += XATTR_MAC_OSX_PREFIX_LEN;
401
402	return len;
403}
404
405static ssize_t copy_name(char *buffer, const char *xattr_name, int name_len)
406{
407	ssize_t len;
408
409	if (!is_known_namespace(xattr_name))
410		len = scnprintf(buffer, name_len + XATTR_MAC_OSX_PREFIX_LEN,
411				 "%s%s", XATTR_MAC_OSX_PREFIX, xattr_name);
412	else
413		len = strscpy(buffer, xattr_name, name_len + 1);
414
415	/* include NUL-byte in length for non-empty name */
416	if (len >= 0)
417		len++;
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(DInfo) + sizeof(DXInfo);
446	u16 file_rec_len = sizeof(FInfo) + sizeof(FXInfo);
447	u16 record_len = max(folder_rec_len, file_rec_len);
448	u8 folder_finder_info[sizeof(DInfo) + sizeof(DXInfo)];
449	u8 file_finder_info[sizeof(FInfo) + sizeof(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(DInfo) + sizeof(DXInfo)];
614	u8 file_finder_info[sizeof(FInfo) + sizeof(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(DInfo) + sizeof(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(FInfo) + sizeof(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 = kzalloc(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_xattr_str(inode->i_sb,
737					      &fd.key->attr.key_name, strbuf,
738					      &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};
~/ Read-only file preview.