xref: /openbmc/linux/fs/hfsplus/xattr.c (revision ca79522c)
1 /*
2  * linux/fs/hfsplus/xattr.c
3  *
4  * Vyacheslav Dubeyko <slava@dubeyko.com>
5  *
6  * Logic of processing extended attributes
7  */
8 
9 #include "hfsplus_fs.h"
10 #include "xattr.h"
11 
12 const struct xattr_handler *hfsplus_xattr_handlers[] = {
13 	&hfsplus_xattr_osx_handler,
14 	&hfsplus_xattr_user_handler,
15 	&hfsplus_xattr_trusted_handler,
16 	&hfsplus_xattr_security_handler,
17 	NULL
18 };
19 
20 static int strcmp_xattr_finder_info(const char *name)
21 {
22 	if (name) {
23 		return strncmp(name, HFSPLUS_XATTR_FINDER_INFO_NAME,
24 				sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME));
25 	}
26 	return -1;
27 }
28 
29 static int strcmp_xattr_acl(const char *name)
30 {
31 	if (name) {
32 		return strncmp(name, HFSPLUS_XATTR_ACL_NAME,
33 				sizeof(HFSPLUS_XATTR_ACL_NAME));
34 	}
35 	return -1;
36 }
37 
38 static inline int is_known_namespace(const char *name)
39 {
40 	if (strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) &&
41 	    strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) &&
42 	    strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) &&
43 	    strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN))
44 		return false;
45 
46 	return true;
47 }
48 
49 static int can_set_xattr(struct inode *inode, const char *name,
50 				const void *value, size_t value_len)
51 {
52 	if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
53 		return -EOPNOTSUPP; /* TODO: implement ACL support */
54 
55 	if (!strncmp(name, XATTR_MAC_OSX_PREFIX, XATTR_MAC_OSX_PREFIX_LEN)) {
56 		/*
57 		 * This makes sure that we aren't trying to set an
58 		 * attribute in a different namespace by prefixing it
59 		 * with "osx."
60 		 */
61 		if (is_known_namespace(name + XATTR_MAC_OSX_PREFIX_LEN))
62 			return -EOPNOTSUPP;
63 
64 		return 0;
65 	}
66 
67 	/*
68 	 * Don't allow setting an attribute in an unknown namespace.
69 	 */
70 	if (strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) &&
71 	    strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) &&
72 	    strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
73 		return -EOPNOTSUPP;
74 
75 	return 0;
76 }
77 
78 int __hfsplus_setxattr(struct inode *inode, const char *name,
79 			const void *value, size_t size, int flags)
80 {
81 	int err = 0;
82 	struct hfs_find_data cat_fd;
83 	hfsplus_cat_entry entry;
84 	u16 cat_entry_flags, cat_entry_type;
85 	u16 folder_finderinfo_len = sizeof(struct DInfo) +
86 					sizeof(struct DXInfo);
87 	u16 file_finderinfo_len = sizeof(struct FInfo) +
88 					sizeof(struct FXInfo);
89 
90 	if ((!S_ISREG(inode->i_mode) &&
91 			!S_ISDIR(inode->i_mode)) ||
92 				HFSPLUS_IS_RSRC(inode))
93 		return -EOPNOTSUPP;
94 
95 	err = can_set_xattr(inode, name, value, size);
96 	if (err)
97 		return err;
98 
99 	if (strncmp(name, XATTR_MAC_OSX_PREFIX,
100 				XATTR_MAC_OSX_PREFIX_LEN) == 0)
101 		name += XATTR_MAC_OSX_PREFIX_LEN;
102 
103 	if (value == NULL) {
104 		value = "";
105 		size = 0;
106 	}
107 
108 	err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
109 	if (err) {
110 		pr_err("can't init xattr find struct\n");
111 		return err;
112 	}
113 
114 	err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
115 	if (err) {
116 		pr_err("catalog searching failed\n");
117 		goto end_setxattr;
118 	}
119 
120 	if (!strcmp_xattr_finder_info(name)) {
121 		if (flags & XATTR_CREATE) {
122 			pr_err("xattr exists yet\n");
123 			err = -EOPNOTSUPP;
124 			goto end_setxattr;
125 		}
126 		hfs_bnode_read(cat_fd.bnode, &entry, cat_fd.entryoffset,
127 					sizeof(hfsplus_cat_entry));
128 		if (be16_to_cpu(entry.type) == HFSPLUS_FOLDER) {
129 			if (size == folder_finderinfo_len) {
130 				memcpy(&entry.folder.user_info, value,
131 						folder_finderinfo_len);
132 				hfs_bnode_write(cat_fd.bnode, &entry,
133 					cat_fd.entryoffset,
134 					sizeof(struct hfsplus_cat_folder));
135 				hfsplus_mark_inode_dirty(inode,
136 						HFSPLUS_I_CAT_DIRTY);
137 			} else {
138 				err = -ERANGE;
139 				goto end_setxattr;
140 			}
141 		} else if (be16_to_cpu(entry.type) == HFSPLUS_FILE) {
142 			if (size == file_finderinfo_len) {
143 				memcpy(&entry.file.user_info, value,
144 						file_finderinfo_len);
145 				hfs_bnode_write(cat_fd.bnode, &entry,
146 					cat_fd.entryoffset,
147 					sizeof(struct hfsplus_cat_file));
148 				hfsplus_mark_inode_dirty(inode,
149 						HFSPLUS_I_CAT_DIRTY);
150 			} else {
151 				err = -ERANGE;
152 				goto end_setxattr;
153 			}
154 		} else {
155 			err = -EOPNOTSUPP;
156 			goto end_setxattr;
157 		}
158 		goto end_setxattr;
159 	}
160 
161 	if (!HFSPLUS_SB(inode->i_sb)->attr_tree) {
162 		err = -EOPNOTSUPP;
163 		goto end_setxattr;
164 	}
165 
166 	if (hfsplus_attr_exists(inode, name)) {
167 		if (flags & XATTR_CREATE) {
168 			pr_err("xattr exists yet\n");
169 			err = -EOPNOTSUPP;
170 			goto end_setxattr;
171 		}
172 		err = hfsplus_delete_attr(inode, name);
173 		if (err)
174 			goto end_setxattr;
175 		err = hfsplus_create_attr(inode, name, value, size);
176 		if (err)
177 			goto end_setxattr;
178 	} else {
179 		if (flags & XATTR_REPLACE) {
180 			pr_err("cannot replace xattr\n");
181 			err = -EOPNOTSUPP;
182 			goto end_setxattr;
183 		}
184 		err = hfsplus_create_attr(inode, name, value, size);
185 		if (err)
186 			goto end_setxattr;
187 	}
188 
189 	cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
190 	if (cat_entry_type == HFSPLUS_FOLDER) {
191 		cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
192 				    cat_fd.entryoffset +
193 				    offsetof(struct hfsplus_cat_folder, flags));
194 		cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
195 		if (!strcmp_xattr_acl(name))
196 			cat_entry_flags |= HFSPLUS_ACL_EXISTS;
197 		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
198 				offsetof(struct hfsplus_cat_folder, flags),
199 				cat_entry_flags);
200 		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
201 	} else if (cat_entry_type == HFSPLUS_FILE) {
202 		cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode,
203 				    cat_fd.entryoffset +
204 				    offsetof(struct hfsplus_cat_file, flags));
205 		cat_entry_flags |= HFSPLUS_XATTR_EXISTS;
206 		if (!strcmp_xattr_acl(name))
207 			cat_entry_flags |= HFSPLUS_ACL_EXISTS;
208 		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
209 				    offsetof(struct hfsplus_cat_file, flags),
210 				    cat_entry_flags);
211 		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
212 	} else {
213 		pr_err("invalid catalog entry type\n");
214 		err = -EIO;
215 		goto end_setxattr;
216 	}
217 
218 end_setxattr:
219 	hfs_find_exit(&cat_fd);
220 	return err;
221 }
222 
223 static inline int is_osx_xattr(const char *xattr_name)
224 {
225 	return !is_known_namespace(xattr_name);
226 }
227 
228 static int name_len(const char *xattr_name, int xattr_name_len)
229 {
230 	int len = xattr_name_len + 1;
231 
232 	if (is_osx_xattr(xattr_name))
233 		len += XATTR_MAC_OSX_PREFIX_LEN;
234 
235 	return len;
236 }
237 
238 static int copy_name(char *buffer, const char *xattr_name, int name_len)
239 {
240 	int len = name_len;
241 	int offset = 0;
242 
243 	if (is_osx_xattr(xattr_name)) {
244 		strncpy(buffer, XATTR_MAC_OSX_PREFIX, XATTR_MAC_OSX_PREFIX_LEN);
245 		offset += XATTR_MAC_OSX_PREFIX_LEN;
246 		len += XATTR_MAC_OSX_PREFIX_LEN;
247 	}
248 
249 	strncpy(buffer + offset, xattr_name, name_len);
250 	memset(buffer + offset + name_len, 0, 1);
251 	len += 1;
252 
253 	return len;
254 }
255 
256 static ssize_t hfsplus_getxattr_finder_info(struct dentry *dentry,
257 						void *value, size_t size)
258 {
259 	ssize_t res = 0;
260 	struct inode *inode = dentry->d_inode;
261 	struct hfs_find_data fd;
262 	u16 entry_type;
263 	u16 folder_rec_len = sizeof(struct DInfo) + sizeof(struct DXInfo);
264 	u16 file_rec_len = sizeof(struct FInfo) + sizeof(struct FXInfo);
265 	u16 record_len = max(folder_rec_len, file_rec_len);
266 	u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
267 	u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
268 
269 	if (size >= record_len) {
270 		res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
271 		if (res) {
272 			pr_err("can't init xattr find struct\n");
273 			return res;
274 		}
275 		res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
276 		if (res)
277 			goto end_getxattr_finder_info;
278 		entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
279 
280 		if (entry_type == HFSPLUS_FOLDER) {
281 			hfs_bnode_read(fd.bnode, folder_finder_info,
282 				fd.entryoffset +
283 				offsetof(struct hfsplus_cat_folder, user_info),
284 				folder_rec_len);
285 			memcpy(value, folder_finder_info, folder_rec_len);
286 			res = folder_rec_len;
287 		} else if (entry_type == HFSPLUS_FILE) {
288 			hfs_bnode_read(fd.bnode, file_finder_info,
289 				fd.entryoffset +
290 				offsetof(struct hfsplus_cat_file, user_info),
291 				file_rec_len);
292 			memcpy(value, file_finder_info, file_rec_len);
293 			res = file_rec_len;
294 		} else {
295 			res = -EOPNOTSUPP;
296 			goto end_getxattr_finder_info;
297 		}
298 	} else
299 		res = size ? -ERANGE : record_len;
300 
301 end_getxattr_finder_info:
302 	if (size >= record_len)
303 		hfs_find_exit(&fd);
304 	return res;
305 }
306 
307 ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name,
308 			 void *value, size_t size)
309 {
310 	struct inode *inode = dentry->d_inode;
311 	struct hfs_find_data fd;
312 	hfsplus_attr_entry *entry;
313 	__be32 xattr_record_type;
314 	u32 record_type;
315 	u16 record_length = 0;
316 	ssize_t res = 0;
317 
318 	if ((!S_ISREG(inode->i_mode) &&
319 			!S_ISDIR(inode->i_mode)) ||
320 				HFSPLUS_IS_RSRC(inode))
321 		return -EOPNOTSUPP;
322 
323 	if (strncmp(name, XATTR_MAC_OSX_PREFIX,
324 				XATTR_MAC_OSX_PREFIX_LEN) == 0) {
325 		/* skip "osx." prefix */
326 		name += XATTR_MAC_OSX_PREFIX_LEN;
327 		/*
328 		 * Don't allow retrieving properly prefixed attributes
329 		 * by prepending them with "osx."
330 		 */
331 		if (is_known_namespace(name))
332 			return -EOPNOTSUPP;
333 	}
334 
335 	if (!strcmp_xattr_finder_info(name))
336 		return hfsplus_getxattr_finder_info(dentry, value, size);
337 
338 	if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
339 		return -EOPNOTSUPP;
340 
341 	entry = hfsplus_alloc_attr_entry();
342 	if (!entry) {
343 		pr_err("can't allocate xattr entry\n");
344 		return -ENOMEM;
345 	}
346 
347 	res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
348 	if (res) {
349 		pr_err("can't init xattr find struct\n");
350 		goto failed_getxattr_init;
351 	}
352 
353 	res = hfsplus_find_attr(inode->i_sb, inode->i_ino, name, &fd);
354 	if (res) {
355 		if (res == -ENOENT)
356 			res = -ENODATA;
357 		else
358 			pr_err("xattr searching failed\n");
359 		goto out;
360 	}
361 
362 	hfs_bnode_read(fd.bnode, &xattr_record_type,
363 			fd.entryoffset, sizeof(xattr_record_type));
364 	record_type = be32_to_cpu(xattr_record_type);
365 	if (record_type == HFSPLUS_ATTR_INLINE_DATA) {
366 		record_length = hfs_bnode_read_u16(fd.bnode,
367 				fd.entryoffset +
368 				offsetof(struct hfsplus_attr_inline_data,
369 				length));
370 		if (record_length > HFSPLUS_MAX_INLINE_DATA_SIZE) {
371 			pr_err("invalid xattr record size\n");
372 			res = -EIO;
373 			goto out;
374 		}
375 	} else if (record_type == HFSPLUS_ATTR_FORK_DATA ||
376 			record_type == HFSPLUS_ATTR_EXTENTS) {
377 		pr_err("only inline data xattr are supported\n");
378 		res = -EOPNOTSUPP;
379 		goto out;
380 	} else {
381 		pr_err("invalid xattr record\n");
382 		res = -EIO;
383 		goto out;
384 	}
385 
386 	if (size) {
387 		hfs_bnode_read(fd.bnode, entry, fd.entryoffset,
388 				offsetof(struct hfsplus_attr_inline_data,
389 					raw_bytes) + record_length);
390 	}
391 
392 	if (size >= record_length) {
393 		memcpy(value, entry->inline_data.raw_bytes, record_length);
394 		res = record_length;
395 	} else
396 		res = size ? -ERANGE : record_length;
397 
398 out:
399 	hfs_find_exit(&fd);
400 
401 failed_getxattr_init:
402 	hfsplus_destroy_attr_entry(entry);
403 	return res;
404 }
405 
406 static inline int can_list(const char *xattr_name)
407 {
408 	if (!xattr_name)
409 		return 0;
410 
411 	return strncmp(xattr_name, XATTR_TRUSTED_PREFIX,
412 			XATTR_TRUSTED_PREFIX_LEN) ||
413 				capable(CAP_SYS_ADMIN);
414 }
415 
416 static ssize_t hfsplus_listxattr_finder_info(struct dentry *dentry,
417 						char *buffer, size_t size)
418 {
419 	ssize_t res = 0;
420 	struct inode *inode = dentry->d_inode;
421 	struct hfs_find_data fd;
422 	u16 entry_type;
423 	u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)];
424 	u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)];
425 	unsigned long len, found_bit;
426 	int xattr_name_len, symbols_count;
427 
428 	res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
429 	if (res) {
430 		pr_err("can't init xattr find struct\n");
431 		return res;
432 	}
433 
434 	res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
435 	if (res)
436 		goto end_listxattr_finder_info;
437 
438 	entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
439 	if (entry_type == HFSPLUS_FOLDER) {
440 		len = sizeof(struct DInfo) + sizeof(struct DXInfo);
441 		hfs_bnode_read(fd.bnode, folder_finder_info,
442 				fd.entryoffset +
443 				offsetof(struct hfsplus_cat_folder, user_info),
444 				len);
445 		found_bit = find_first_bit((void *)folder_finder_info, len*8);
446 	} else if (entry_type == HFSPLUS_FILE) {
447 		len = sizeof(struct FInfo) + sizeof(struct FXInfo);
448 		hfs_bnode_read(fd.bnode, file_finder_info,
449 				fd.entryoffset +
450 				offsetof(struct hfsplus_cat_file, user_info),
451 				len);
452 		found_bit = find_first_bit((void *)file_finder_info, len*8);
453 	} else {
454 		res = -EOPNOTSUPP;
455 		goto end_listxattr_finder_info;
456 	}
457 
458 	if (found_bit >= (len*8))
459 		res = 0;
460 	else {
461 		symbols_count = sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME) - 1;
462 		xattr_name_len =
463 			name_len(HFSPLUS_XATTR_FINDER_INFO_NAME, symbols_count);
464 		if (!buffer || !size) {
465 			if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME))
466 				res = xattr_name_len;
467 		} else if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME)) {
468 			if (size < xattr_name_len)
469 				res = -ERANGE;
470 			else {
471 				res = copy_name(buffer,
472 						HFSPLUS_XATTR_FINDER_INFO_NAME,
473 						symbols_count);
474 			}
475 		}
476 	}
477 
478 end_listxattr_finder_info:
479 	hfs_find_exit(&fd);
480 
481 	return res;
482 }
483 
484 ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
485 {
486 	ssize_t err;
487 	ssize_t res = 0;
488 	struct inode *inode = dentry->d_inode;
489 	struct hfs_find_data fd;
490 	u16 key_len = 0;
491 	struct hfsplus_attr_key attr_key;
492 	char strbuf[HFSPLUS_ATTR_MAX_STRLEN +
493 			XATTR_MAC_OSX_PREFIX_LEN + 1] = {0};
494 	int xattr_name_len;
495 
496 	if ((!S_ISREG(inode->i_mode) &&
497 			!S_ISDIR(inode->i_mode)) ||
498 				HFSPLUS_IS_RSRC(inode))
499 		return -EOPNOTSUPP;
500 
501 	res = hfsplus_listxattr_finder_info(dentry, buffer, size);
502 	if (res < 0)
503 		return res;
504 	else if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
505 		return (res == 0) ? -EOPNOTSUPP : res;
506 
507 	err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
508 	if (err) {
509 		pr_err("can't init xattr find struct\n");
510 		return err;
511 	}
512 
513 	err = hfsplus_find_attr(inode->i_sb, inode->i_ino, NULL, &fd);
514 	if (err) {
515 		if (err == -ENOENT) {
516 			if (res == 0)
517 				res = -ENODATA;
518 			goto end_listxattr;
519 		} else {
520 			res = err;
521 			goto end_listxattr;
522 		}
523 	}
524 
525 	for (;;) {
526 		key_len = hfs_bnode_read_u16(fd.bnode, fd.keyoffset);
527 		if (key_len == 0 || key_len > fd.tree->max_key_len) {
528 			pr_err("invalid xattr key length: %d\n", key_len);
529 			res = -EIO;
530 			goto end_listxattr;
531 		}
532 
533 		hfs_bnode_read(fd.bnode, &attr_key,
534 				fd.keyoffset, key_len + sizeof(key_len));
535 
536 		if (be32_to_cpu(attr_key.cnid) != inode->i_ino)
537 			goto end_listxattr;
538 
539 		xattr_name_len = HFSPLUS_ATTR_MAX_STRLEN;
540 		if (hfsplus_uni2asc(inode->i_sb,
541 			(const struct hfsplus_unistr *)&fd.key->attr.key_name,
542 					strbuf, &xattr_name_len)) {
543 			pr_err("unicode conversion failed\n");
544 			res = -EIO;
545 			goto end_listxattr;
546 		}
547 
548 		if (!buffer || !size) {
549 			if (can_list(strbuf))
550 				res += name_len(strbuf, xattr_name_len);
551 		} else if (can_list(strbuf)) {
552 			if (size < (res + name_len(strbuf, xattr_name_len))) {
553 				res = -ERANGE;
554 				goto end_listxattr;
555 			} else
556 				res += copy_name(buffer + res,
557 						strbuf, xattr_name_len);
558 		}
559 
560 		if (hfs_brec_goto(&fd, 1))
561 			goto end_listxattr;
562 	}
563 
564 end_listxattr:
565 	hfs_find_exit(&fd);
566 	return res;
567 }
568 
569 int hfsplus_removexattr(struct dentry *dentry, const char *name)
570 {
571 	int err = 0;
572 	struct inode *inode = dentry->d_inode;
573 	struct hfs_find_data cat_fd;
574 	u16 flags;
575 	u16 cat_entry_type;
576 	int is_xattr_acl_deleted = 0;
577 	int is_all_xattrs_deleted = 0;
578 
579 	if ((!S_ISREG(inode->i_mode) &&
580 			!S_ISDIR(inode->i_mode)) ||
581 				HFSPLUS_IS_RSRC(inode))
582 		return -EOPNOTSUPP;
583 
584 	if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
585 		return -EOPNOTSUPP;
586 
587 	err = can_set_xattr(inode, name, NULL, 0);
588 	if (err)
589 		return err;
590 
591 	if (strncmp(name, XATTR_MAC_OSX_PREFIX,
592 				XATTR_MAC_OSX_PREFIX_LEN) == 0)
593 		name += XATTR_MAC_OSX_PREFIX_LEN;
594 
595 	if (!strcmp_xattr_finder_info(name))
596 		return -EOPNOTSUPP;
597 
598 	err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
599 	if (err) {
600 		pr_err("can't init xattr find struct\n");
601 		return err;
602 	}
603 
604 	err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
605 	if (err) {
606 		pr_err("catalog searching failed\n");
607 		goto end_removexattr;
608 	}
609 
610 	err = hfsplus_delete_attr(inode, name);
611 	if (err)
612 		goto end_removexattr;
613 
614 	is_xattr_acl_deleted = !strcmp_xattr_acl(name);
615 	is_all_xattrs_deleted = !hfsplus_attr_exists(inode, NULL);
616 
617 	if (!is_xattr_acl_deleted && !is_all_xattrs_deleted)
618 		goto end_removexattr;
619 
620 	cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
621 
622 	if (cat_entry_type == HFSPLUS_FOLDER) {
623 		flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset +
624 				offsetof(struct hfsplus_cat_folder, flags));
625 		if (is_xattr_acl_deleted)
626 			flags &= ~HFSPLUS_ACL_EXISTS;
627 		if (is_all_xattrs_deleted)
628 			flags &= ~HFSPLUS_XATTR_EXISTS;
629 		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
630 				offsetof(struct hfsplus_cat_folder, flags),
631 				flags);
632 		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
633 	} else if (cat_entry_type == HFSPLUS_FILE) {
634 		flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset +
635 				offsetof(struct hfsplus_cat_file, flags));
636 		if (is_xattr_acl_deleted)
637 			flags &= ~HFSPLUS_ACL_EXISTS;
638 		if (is_all_xattrs_deleted)
639 			flags &= ~HFSPLUS_XATTR_EXISTS;
640 		hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset +
641 				offsetof(struct hfsplus_cat_file, flags),
642 				flags);
643 		hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
644 	} else {
645 		pr_err("invalid catalog entry type\n");
646 		err = -EIO;
647 		goto end_removexattr;
648 	}
649 
650 end_removexattr:
651 	hfs_find_exit(&cat_fd);
652 	return err;
653 }
654 
655 static int hfsplus_osx_getxattr(struct dentry *dentry, const char *name,
656 					void *buffer, size_t size, int type)
657 {
658 	char xattr_name[HFSPLUS_ATTR_MAX_STRLEN +
659 				XATTR_MAC_OSX_PREFIX_LEN + 1] = {0};
660 	size_t len = strlen(name);
661 
662 	if (!strcmp(name, ""))
663 		return -EINVAL;
664 
665 	if (len > HFSPLUS_ATTR_MAX_STRLEN)
666 		return -EOPNOTSUPP;
667 
668 	strcpy(xattr_name, XATTR_MAC_OSX_PREFIX);
669 	strcpy(xattr_name + XATTR_MAC_OSX_PREFIX_LEN, name);
670 
671 	return hfsplus_getxattr(dentry, xattr_name, buffer, size);
672 }
673 
674 static int hfsplus_osx_setxattr(struct dentry *dentry, const char *name,
675 		const void *buffer, size_t size, int flags, int type)
676 {
677 	char xattr_name[HFSPLUS_ATTR_MAX_STRLEN +
678 				XATTR_MAC_OSX_PREFIX_LEN + 1] = {0};
679 	size_t len = strlen(name);
680 
681 	if (!strcmp(name, ""))
682 		return -EINVAL;
683 
684 	if (len > HFSPLUS_ATTR_MAX_STRLEN)
685 		return -EOPNOTSUPP;
686 
687 	strcpy(xattr_name, XATTR_MAC_OSX_PREFIX);
688 	strcpy(xattr_name + XATTR_MAC_OSX_PREFIX_LEN, name);
689 
690 	return hfsplus_setxattr(dentry, xattr_name, buffer, size, flags);
691 }
692 
693 static size_t hfsplus_osx_listxattr(struct dentry *dentry, char *list,
694 		size_t list_size, const char *name, size_t name_len, int type)
695 {
696 	/*
697 	 * This method is not used.
698 	 * It is used hfsplus_listxattr() instead of generic_listxattr().
699 	 */
700 	return -EOPNOTSUPP;
701 }
702 
703 const struct xattr_handler hfsplus_xattr_osx_handler = {
704 	.prefix	= XATTR_MAC_OSX_PREFIX,
705 	.list	= hfsplus_osx_listxattr,
706 	.get	= hfsplus_osx_getxattr,
707 	.set	= hfsplus_osx_setxattr,
708 };
709