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
,