2 * linux/fs/hfsplus/xattr.c
4 * Vyacheslav Dubeyko <slava@dubeyko.com>
6 * Logic of processing extended attributes
9 #include "hfsplus_fs.h"
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
,
20 static int strcmp_xattr_finder_info(const char *name
)
23 return strncmp(name
, HFSPLUS_XATTR_FINDER_INFO_NAME
,
24 sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME
));
29 static int strcmp_xattr_acl(const char *name
)
32 return strncmp(name
, HFSPLUS_XATTR_ACL_NAME
,
33 sizeof(HFSPLUS_XATTR_ACL_NAME
));
38 static inline int is_known_namespace(const char *name
)
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
))
49 static int can_set_xattr(struct inode
*inode
, const char *name
,
50 const void *value
, size_t value_len
)
52 if (!strncmp(name
, XATTR_SYSTEM_PREFIX
, XATTR_SYSTEM_PREFIX_LEN
))
53 return -EOPNOTSUPP
; /* TODO: implement ACL support */
55 if (!strncmp(name
, XATTR_MAC_OSX_PREFIX
, XATTR_MAC_OSX_PREFIX_LEN
)) {
57 * This makes sure that we aren't trying to set an
58 * attribute in a different namespace by prefixing it
61 if (is_known_namespace(name
+ XATTR_MAC_OSX_PREFIX_LEN
))
68 * Don't allow setting an attribute in an unknown namespace.
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
))
78 int __hfsplus_setxattr(struct inode
*inode
, const char *name
,
79 const void *value
, size_t size
, int flags
)
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
);
90 if ((!S_ISREG(inode
->i_mode
) &&
91 !S_ISDIR(inode
->i_mode
)) ||
92 HFSPLUS_IS_RSRC(inode
))
95 err
= can_set_xattr(inode
, name
, value
, size
);
99 if (strncmp(name
, XATTR_MAC_OSX_PREFIX
,
100 XATTR_MAC_OSX_PREFIX_LEN
) == 0)
101 name
+= XATTR_MAC_OSX_PREFIX_LEN
;
108 err
= hfs_find_init(HFSPLUS_SB(inode
->i_sb
)->cat_tree
, &cat_fd
);
110 pr_err("can't init xattr find struct\n");
114 err
= hfsplus_find_cat(inode
->i_sb
, inode
->i_ino
, &cat_fd
);
116 pr_err("catalog searching failed\n");
120 if (!strcmp_xattr_finder_info(name
)) {
121 if (flags
& XATTR_CREATE
) {
122 pr_err("xattr exists yet\n");
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
,
134 sizeof(struct hfsplus_cat_folder
));
135 hfsplus_mark_inode_dirty(inode
,
136 HFSPLUS_I_CAT_DIRTY
);
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
,
147 sizeof(struct hfsplus_cat_file
));
148 hfsplus_mark_inode_dirty(inode
,
149 HFSPLUS_I_CAT_DIRTY
);
161 if (!HFSPLUS_SB(inode
->i_sb
)->attr_tree
) {
166 if (hfsplus_attr_exists(inode
, name
)) {
167 if (flags
& XATTR_CREATE
) {
168 pr_err("xattr exists yet\n");
172 err
= hfsplus_delete_attr(inode
, name
);
175 err
= hfsplus_create_attr(inode
, name
, value
, size
);
179 if (flags
& XATTR_REPLACE
) {
180 pr_err("cannot replace xattr\n");
184 err
= hfsplus_create_attr(inode
, name
, value
, size
);
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
,
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
),
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
,
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
),
211 hfsplus_mark_inode_dirty(inode
, HFSPLUS_I_CAT_DIRTY
);
213 pr_err("invalid catalog entry type\n");
219 hfs_find_exit(&cat_fd
);
223 static inline int is_osx_xattr(const char *xattr_name
)
225 return !is_known_namespace(xattr_name
);
228 static int name_len(const char *xattr_name
, int xattr_name_len
)
230 int len
= xattr_name_len
+ 1;
232 if (is_osx_xattr(xattr_name
))
233 len
+= XATTR_MAC_OSX_PREFIX_LEN
;
238 static int copy_name(char *buffer
, const char *xattr_name
, int name_len
)
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
;
249 strncpy(buffer
+ offset
, xattr_name
, name_len
);
250 memset(buffer
+ offset
+ name_len
, 0, 1);
256 static ssize_t
hfsplus_getxattr_finder_info(struct dentry
*dentry
,
257 void *value
, size_t size
)
260 struct inode
*inode
= dentry
->d_inode
;
261 struct hfs_find_data fd
;
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
)];
269 if (size
>= record_len
) {
270 res
= hfs_find_init(HFSPLUS_SB(inode
->i_sb
)->cat_tree
, &fd
);
272 pr_err("can't init xattr find struct\n");
275 res
= hfsplus_find_cat(inode
->i_sb
, inode
->i_ino
, &fd
);
277 goto end_getxattr_finder_info
;
278 entry_type
= hfs_bnode_read_u16(fd
.bnode
, fd
.entryoffset
);
280 if (entry_type
== HFSPLUS_FOLDER
) {
281 hfs_bnode_read(fd
.bnode
, folder_finder_info
,
283 offsetof(struct hfsplus_cat_folder
, user_info
),
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
,
290 offsetof(struct hfsplus_cat_file
, user_info
),
292 memcpy(value
, file_finder_info
, file_rec_len
);
296 goto end_getxattr_finder_info
;
299 res
= size
? -ERANGE
: record_len
;
301 end_getxattr_finder_info
:
302 if (size
>= record_len
)
307 ssize_t
hfsplus_getxattr(struct dentry
*dentry
, const char *name
,
308 void *value
, size_t size
)
310 struct inode
*inode
= dentry
->d_inode
;
311 struct hfs_find_data fd
;
312 hfsplus_attr_entry
*entry
;
313 __be32 xattr_record_type
;
315 u16 record_length
= 0;
318 if ((!S_ISREG(inode
->i_mode
) &&
319 !S_ISDIR(inode
->i_mode
)) ||
320 HFSPLUS_IS_RSRC(inode
))
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
;
328 * Don't allow retrieving properly prefixed attributes
329 * by prepending them with "osx."
331 if (is_known_namespace(name
))
335 if (!strcmp_xattr_finder_info(name
))
336 return hfsplus_getxattr_finder_info(dentry
, value
, size
);
338 if (!HFSPLUS_SB(inode
->i_sb
)->attr_tree
)
341 entry
= hfsplus_alloc_attr_entry();
343 pr_err("can't allocate xattr entry\n");
347 res
= hfs_find_init(HFSPLUS_SB(inode
->i_sb
)->attr_tree
, &fd
);
349 pr_err("can't init xattr find struct\n");
350 goto failed_getxattr_init
;
353 res
= hfsplus_find_attr(inode
->i_sb
, inode
->i_ino
, name
, &fd
);
358 pr_err("xattr searching failed\n");
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
,
368 offsetof(struct hfsplus_attr_inline_data
,
370 if (record_length
> HFSPLUS_MAX_INLINE_DATA_SIZE
) {
371 pr_err("invalid xattr record size\n");
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");
381 pr_err("invalid xattr record\n");
387 hfs_bnode_read(fd
.bnode
, entry
, fd
.entryoffset
,
388 offsetof(struct hfsplus_attr_inline_data
,
389 raw_bytes
) + record_length
);
392 if (size
>= record_length
) {
393 memcpy(value
, entry
->inline_data
.raw_bytes
, record_length
);
396 res
= size
? -ERANGE
: record_length
;
401 failed_getxattr_init
:
402 hfsplus_destroy_attr_entry(entry
);
406 static inline int can_list(const char *xattr_name
)
411 return strncmp(xattr_name
, XATTR_TRUSTED_PREFIX
,
412 XATTR_TRUSTED_PREFIX_LEN
) ||
413 capable(CAP_SYS_ADMIN
);
416 static ssize_t
hfsplus_listxattr_finder_info(struct dentry
*dentry
,
417 char *buffer
, size_t size
)
420 struct inode
*inode
= dentry
->d_inode
;
421 struct hfs_find_data fd
;
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
;
428 res
= hfs_find_init(HFSPLUS_SB(inode
->i_sb
)->cat_tree
, &fd
);
430 pr_err("can't init xattr find struct\n");
434 res
= hfsplus_find_cat(inode
->i_sb
, inode
->i_ino
, &fd
);
436 goto end_listxattr_finder_info
;
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
,
443 offsetof(struct hfsplus_cat_folder
, user_info
),
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
,
450 offsetof(struct hfsplus_cat_file
, user_info
),
452 found_bit
= find_first_bit((void *)file_finder_info
, len
*8);
455 goto end_listxattr_finder_info
;
458 if (found_bit
>= (len
*8))
461 symbols_count
= sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME
) - 1;
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
)
471 res
= copy_name(buffer
,
472 HFSPLUS_XATTR_FINDER_INFO_NAME
,
478 end_listxattr_finder_info
:
484 ssize_t
hfsplus_listxattr(struct dentry
*dentry
, char *buffer
, size_t size
)
488 struct inode
*inode
= dentry
->d_inode
;
489 struct hfs_find_data fd
;
491 struct hfsplus_attr_key attr_key
;
492 char strbuf
[HFSPLUS_ATTR_MAX_STRLEN
+
493 XATTR_MAC_OSX_PREFIX_LEN
+ 1] = {0};
496 if ((!S_ISREG(inode
->i_mode
) &&
497 !S_ISDIR(inode
->i_mode
)) ||
498 HFSPLUS_IS_RSRC(inode
))
501 res
= hfsplus_listxattr_finder_info(dentry
, buffer
, size
);
504 else if (!HFSPLUS_SB(inode
->i_sb
)->attr_tree
)
505 return (res
== 0) ? -EOPNOTSUPP
: res
;
507 err
= hfs_find_init(HFSPLUS_SB(inode
->i_sb
)->attr_tree
, &fd
);
509 pr_err("can't init xattr find struct\n");
513 err
= hfsplus_find_attr(inode
->i_sb
, inode
->i_ino
, NULL
, &fd
);
515 if (err
== -ENOENT
) {
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
);
533 hfs_bnode_read(fd
.bnode
, &attr_key
,
534 fd
.keyoffset
, key_len
+ sizeof(key_len
));
536 if (be32_to_cpu(attr_key
.cnid
) != inode
->i_ino
)
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");
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
))) {
556 res
+= copy_name(buffer
+ res
,
557 strbuf
, xattr_name_len
);
560 if (hfs_brec_goto(&fd
, 1))
569 int hfsplus_removexattr(struct dentry
*dentry
, const char *name
)
572 struct inode
*inode
= dentry
->d_inode
;
573 struct hfs_find_data cat_fd
;
576 int is_xattr_acl_deleted
= 0;
577 int is_all_xattrs_deleted
= 0;
579 if ((!S_ISREG(inode
->i_mode
) &&
580 !S_ISDIR(inode
->i_mode
)) ||
581 HFSPLUS_IS_RSRC(inode
))
584 if (!HFSPLUS_SB(inode
->i_sb
)->attr_tree
)
587 err
= can_set_xattr(inode
, name
, NULL
, 0);
591 if (strncmp(name
, XATTR_MAC_OSX_PREFIX
,
592 XATTR_MAC_OSX_PREFIX_LEN
) == 0)
593 name
+= XATTR_MAC_OSX_PREFIX_LEN
;
595 if (!strcmp_xattr_finder_info(name
))
598 err
= hfs_find_init(HFSPLUS_SB(inode
->i_sb
)->cat_tree
, &cat_fd
);
600 pr_err("can't init xattr find struct\n");
604 err
= hfsplus_find_cat(inode
->i_sb
, inode
->i_ino
, &cat_fd
);
606 pr_err("catalog searching failed\n");
607 goto end_removexattr
;
610 err
= hfsplus_delete_attr(inode
, name
);
612 goto end_removexattr
;
614 is_xattr_acl_deleted
= !strcmp_xattr_acl(name
);
615 is_all_xattrs_deleted
= !hfsplus_attr_exists(inode
, NULL
);
617 if (!is_xattr_acl_deleted
&& !is_all_xattrs_deleted
)
618 goto end_removexattr
;
620 cat_entry_type
= hfs_bnode_read_u16(cat_fd
.bnode
, cat_fd
.entryoffset
);
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
),
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
),
643 hfsplus_mark_inode_dirty(inode
, HFSPLUS_I_CAT_DIRTY
);
645 pr_err("invalid catalog entry type\n");
647 goto end_removexattr
;
651 hfs_find_exit(&cat_fd
);
655 static int hfsplus_osx_getxattr(struct dentry
*dentry
, const char *name
,
656 void *buffer
, size_t size
, int type
)
658 char xattr_name
[HFSPLUS_ATTR_MAX_STRLEN
+
659 XATTR_MAC_OSX_PREFIX_LEN
+ 1] = {0};
660 size_t len
= strlen(name
);
662 if (!strcmp(name
, ""))
665 if (len
> HFSPLUS_ATTR_MAX_STRLEN
)
668 strcpy(xattr_name
, XATTR_MAC_OSX_PREFIX
);
669 strcpy(xattr_name
+ XATTR_MAC_OSX_PREFIX_LEN
, name
);
671 return hfsplus_getxattr(dentry
, xattr_name
, buffer
, size
);
674 static int hfsplus_osx_setxattr(struct dentry
*dentry
, const char *name
,
675 const void *buffer
, size_t size
, int flags
, int type
)
677 char xattr_name
[HFSPLUS_ATTR_MAX_STRLEN
+
678 XATTR_MAC_OSX_PREFIX_LEN
+ 1] = {0};
679 size_t len
= strlen(name
);
681 if (!strcmp(name
, ""))
684 if (len
> HFSPLUS_ATTR_MAX_STRLEN
)
687 strcpy(xattr_name
, XATTR_MAC_OSX_PREFIX
);
688 strcpy(xattr_name
+ XATTR_MAC_OSX_PREFIX_LEN
, name
);
690 return hfsplus_setxattr(dentry
, xattr_name
, buffer
, size
, flags
);
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
)
697 * This method is not used.
698 * It is used hfsplus_listxattr() instead of generic_listxattr().
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
,