2 * linux/fs/hfsplus/xattr.c
4 * Vyacheslav Dubeyko <slava@dubeyko.com>
6 * Logic of processing extended attributes
9 #include "hfsplus_fs.h"
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,
21 &hfsplus_xattr_security_handler,
25 static int strcmp_xattr_finder_info(const char *name)
28 return strncmp(name, HFSPLUS_XATTR_FINDER_INFO_NAME,
29 sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME));
34 static int strcmp_xattr_acl(const char *name)
37 return strncmp(name, HFSPLUS_XATTR_ACL_NAME,
38 sizeof(HFSPLUS_XATTR_ACL_NAME));
43 static inline int is_known_namespace(const char *name)
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))
54 static int can_set_system_xattr(struct inode *inode, const char *name,
55 const void *value, size_t size)
57 #ifdef CONFIG_HFSPLUS_FS_POSIX_ACL
58 struct posix_acl *acl;
61 if (!inode_owner_or_capable(inode))
65 * POSIX_ACL_XATTR_ACCESS is tied to i_mode
67 if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0) {
68 acl = posix_acl_from_xattr(&init_user_ns, value, size);
72 err = posix_acl_equiv_mode(acl, &inode->i_mode);
73 posix_acl_release(acl);
76 mark_inode_dirty(inode);
79 * We're changing the ACL. Get rid of the cached one
81 forget_cached_acl(inode, ACL_TYPE_ACCESS);
84 } else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0) {
85 acl = posix_acl_from_xattr(&init_user_ns, value, size);
88 posix_acl_release(acl);
91 * We're changing the default ACL. Get rid of the cached one
93 forget_cached_acl(inode, ACL_TYPE_DEFAULT);
97 #endif /* CONFIG_HFSPLUS_FS_POSIX_ACL */
101 static int can_set_xattr(struct inode *inode, const char *name,
102 const void *value, size_t value_len)
104 if (!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
105 return can_set_system_xattr(inode, name, value, value_len);
107 if (!strncmp(name, XATTR_MAC_OSX_PREFIX, XATTR_MAC_OSX_PREFIX_LEN)) {
109 * This makes sure that we aren't trying to set an
110 * attribute in a different namespace by prefixing it
113 if (is_known_namespace(name + XATTR_MAC_OSX_PREFIX_LEN))
120 * Don't allow setting an attribute in an unknown namespace.
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))
130 int __hfsplus_setxattr(struct inode *inode, const char *name,
131 const void *value, size_t size, int flags)
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);
142 if ((!S_ISREG(inode->i_mode) &&
143 !S_ISDIR(inode->i_mode)) ||
144 HFSPLUS_IS_RSRC(inode))
147 err = can_set_xattr(inode, name, value, size);
151 if (strncmp(name, XATTR_MAC_OSX_PREFIX,
152 XATTR_MAC_OSX_PREFIX_LEN) == 0)
153 name += XATTR_MAC_OSX_PREFIX_LEN;
160 err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
162 pr_err("can't init xattr find struct\n");
166 err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
168 pr_err("catalog searching failed\n");
172 if (!strcmp_xattr_finder_info(name)) {
173 if (flags & XATTR_CREATE) {
174 pr_err("xattr exists yet\n");
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,
186 sizeof(struct hfsplus_cat_folder));
187 hfsplus_mark_inode_dirty(inode,
188 HFSPLUS_I_CAT_DIRTY);
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,
199 sizeof(struct hfsplus_cat_file));
200 hfsplus_mark_inode_dirty(inode,
201 HFSPLUS_I_CAT_DIRTY);
213 if (!HFSPLUS_SB(inode->i_sb)->attr_tree) {
218 if (hfsplus_attr_exists(inode, name)) {
219 if (flags & XATTR_CREATE) {
220 pr_err("xattr exists yet\n");
224 err = hfsplus_delete_attr(inode, name);
227 err = hfsplus_create_attr(inode, name, value, size);
231 if (flags & XATTR_REPLACE) {
232 pr_err("cannot replace xattr\n");
236 err = hfsplus_create_attr(inode, name, value, size);
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,
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),
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,
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),
263 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
265 pr_err("invalid catalog entry type\n");
271 hfs_find_exit(&cat_fd);
275 static inline int is_osx_xattr(const char *xattr_name)
277 return !is_known_namespace(xattr_name);
280 static int name_len(const char *xattr_name, int xattr_name_len)
282 int len = xattr_name_len + 1;
284 if (is_osx_xattr(xattr_name))
285 len += XATTR_MAC_OSX_PREFIX_LEN;
290 static int copy_name(char *buffer, const char *xattr_name, int name_len)
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;
301 strncpy(buffer + offset, xattr_name, name_len);
302 memset(buffer + offset + name_len, 0, 1);
308 static ssize_t hfsplus_getxattr_finder_info(struct inode *inode,
309 void *value, size_t size)
312 struct hfs_find_data fd;
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)];
320 if (size >= record_len) {
321 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
323 pr_err("can't init xattr find struct\n");
326 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
328 goto end_getxattr_finder_info;
329 entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
331 if (entry_type == HFSPLUS_FOLDER) {
332 hfs_bnode_read(fd.bnode, folder_finder_info,
334 offsetof(struct hfsplus_cat_folder, user_info),
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,
341 offsetof(struct hfsplus_cat_file, user_info),
343 memcpy(value, file_finder_info, file_rec_len);
347 goto end_getxattr_finder_info;
350 res = size ? -ERANGE : record_len;
352 end_getxattr_finder_info:
353 if (size >= record_len)
358 ssize_t __hfsplus_getxattr(struct inode *inode, const char *name,
359 void *value, size_t size)
361 struct hfs_find_data fd;
362 hfsplus_attr_entry *entry;
363 __be32 xattr_record_type;
365 u16 record_length = 0;
368 if ((!S_ISREG(inode->i_mode) &&
369 !S_ISDIR(inode->i_mode)) ||
370 HFSPLUS_IS_RSRC(inode))
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;
378 * Don't allow retrieving properly prefixed attributes
379 * by prepending them with "osx."
381 if (is_known_namespace(name))
385 if (!strcmp_xattr_finder_info(name))
386 return hfsplus_getxattr_finder_info(inode, value, size);
388 if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
391 entry = hfsplus_alloc_attr_entry();
393 pr_err("can't allocate xattr entry\n");
397 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
399 pr_err("can't init xattr find struct\n");
400 goto failed_getxattr_init;
403 res = hfsplus_find_attr(inode->i_sb, inode->i_ino, name, &fd);
408 pr_err("xattr searching failed\n");
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,
418 offsetof(struct hfsplus_attr_inline_data,
420 if (record_length > HFSPLUS_MAX_INLINE_DATA_SIZE) {
421 pr_err("invalid xattr record size\n");
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");
431 pr_err("invalid xattr record\n");
437 hfs_bnode_read(fd.bnode, entry, fd.entryoffset,
438 offsetof(struct hfsplus_attr_inline_data,
439 raw_bytes) + record_length);
442 if (size >= record_length) {
443 memcpy(value, entry->inline_data.raw_bytes, record_length);
446 res = size ? -ERANGE : record_length;
451 failed_getxattr_init:
452 hfsplus_destroy_attr_entry(entry);
456 static inline int can_list(const char *xattr_name)
461 return strncmp(xattr_name, XATTR_TRUSTED_PREFIX,
462 XATTR_TRUSTED_PREFIX_LEN) ||
463 capable(CAP_SYS_ADMIN);
466 static ssize_t hfsplus_listxattr_finder_info(struct dentry *dentry,
467 char *buffer, size_t size)
470 struct inode *inode = dentry->d_inode;
471 struct hfs_find_data fd;
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;
478 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd);
480 pr_err("can't init xattr find struct\n");
484 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
486 goto end_listxattr_finder_info;
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,
493 offsetof(struct hfsplus_cat_folder, user_info),
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,
500 offsetof(struct hfsplus_cat_file, user_info),
502 found_bit = find_first_bit((void *)file_finder_info, len*8);
505 goto end_listxattr_finder_info;
508 if (found_bit >= (len*8))
511 symbols_count = sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME) - 1;
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)
521 res = copy_name(buffer,
522 HFSPLUS_XATTR_FINDER_INFO_NAME,
528 end_listxattr_finder_info:
534 ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
538 struct inode *inode = dentry->d_inode;
539 struct hfs_find_data fd;
541 struct hfsplus_attr_key attr_key;
542 char strbuf[HFSPLUS_ATTR_MAX_STRLEN +
543 XATTR_MAC_OSX_PREFIX_LEN + 1] = {0};
546 if ((!S_ISREG(inode->i_mode) &&
547 !S_ISDIR(inode->i_mode)) ||
548 HFSPLUS_IS_RSRC(inode))
551 res = hfsplus_listxattr_finder_info(dentry, buffer, size);
554 else if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
555 return (res == 0) ? -EOPNOTSUPP : res;
557 err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd);
559 pr_err("can't init xattr find struct\n");
563 err = hfsplus_find_attr(inode->i_sb, inode->i_ino, NULL, &fd);
565 if (err == -ENOENT) {
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);
583 hfs_bnode_read(fd.bnode, &attr_key,
584 fd.keyoffset, key_len + sizeof(key_len));
586 if (be32_to_cpu(attr_key.cnid) != inode->i_ino)
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");
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))) {
606 res += copy_name(buffer + res,
607 strbuf, xattr_name_len);
610 if (hfs_brec_goto(&fd, 1))
619 int hfsplus_removexattr(struct dentry *dentry, const char *name)
622 struct inode *inode = dentry->d_inode;
623 struct hfs_find_data cat_fd;
626 int is_xattr_acl_deleted = 0;
627 int is_all_xattrs_deleted = 0;
629 if ((!S_ISREG(inode->i_mode) &&
630 !S_ISDIR(inode->i_mode)) ||
631 HFSPLUS_IS_RSRC(inode))
634 if (!HFSPLUS_SB(inode->i_sb)->attr_tree)
637 err = can_set_xattr(inode, name, NULL, 0);
641 if (strncmp(name, XATTR_MAC_OSX_PREFIX,
642 XATTR_MAC_OSX_PREFIX_LEN) == 0)
643 name += XATTR_MAC_OSX_PREFIX_LEN;
645 if (!strcmp_xattr_finder_info(name))
648 err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd);
650 pr_err("can't init xattr find struct\n");
654 err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd);
656 pr_err("catalog searching failed\n");
657 goto end_removexattr;
660 err = hfsplus_delete_attr(inode, name);
662 goto end_removexattr;
664 is_xattr_acl_deleted = !strcmp_xattr_acl(name);
665 is_all_xattrs_deleted = !hfsplus_attr_exists(inode, NULL);
667 if (!is_xattr_acl_deleted && !is_all_xattrs_deleted)
668 goto end_removexattr;
670 cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset);
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),
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),
693 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY);
695 pr_err("invalid catalog entry type\n");
697 goto end_removexattr;
701 hfs_find_exit(&cat_fd);
705 static int hfsplus_osx_getxattr(struct dentry *dentry, const char *name,
706 void *buffer, size_t size, int type)
708 char xattr_name[HFSPLUS_ATTR_MAX_STRLEN +
709 XATTR_MAC_OSX_PREFIX_LEN + 1] = {0};
710 size_t len = strlen(name);
712 if (!strcmp(name, ""))
715 if (len > HFSPLUS_ATTR_MAX_STRLEN)
718 strcpy(xattr_name, XATTR_MAC_OSX_PREFIX);
719 strcpy(xattr_name + XATTR_MAC_OSX_PREFIX_LEN, name);
721 return hfsplus_getxattr(dentry, xattr_name, buffer, size);
724 static int hfsplus_osx_setxattr(struct dentry *dentry, const char *name,
725 const void *buffer, size_t size, int flags, int type)
727 char xattr_name[HFSPLUS_ATTR_MAX_STRLEN +
728 XATTR_MAC_OSX_PREFIX_LEN + 1] = {0};
729 size_t len = strlen(name);
731 if (!strcmp(name, ""))
734 if (len > HFSPLUS_ATTR_MAX_STRLEN)
737 strcpy(xattr_name, XATTR_MAC_OSX_PREFIX);
738 strcpy(xattr_name + XATTR_MAC_OSX_PREFIX_LEN, name);
740 return hfsplus_setxattr(dentry, xattr_name, buffer, size, flags);
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)
747 * This method is not used.
748 * It is used hfsplus_listxattr() instead of generic_listxattr().
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,