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