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 printk(KERN_ERR
"hfs: can't init xattr find struct\n");
114 err
= hfsplus_find_cat(inode
->i_sb
, inode
->i_ino
, &cat_fd
);
116 printk(KERN_ERR
"hfs: catalog searching failed\n");
120 if (!strcmp_xattr_finder_info(name
)) {
121 if (flags
& XATTR_CREATE
) {
122 printk(KERN_ERR
"hfs: 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 printk(KERN_ERR
"hfs: 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 printk(KERN_ERR
"hfs: 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 printk(KERN_ERR
"hfs: 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 printk(KERN_ERR
"hfs: 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 printk(KERN_ERR
"hfs: can't allocate xattr entry\n");
347 res
= hfs_find_init(HFSPLUS_SB(inode
->i_sb
)->attr_tree
, &fd
);
349 printk(KERN_ERR
"hfs: 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 printk(KERN_ERR
"hfs: 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 printk(KERN_ERR
"hfs: invalid xattr record size\n");
375 } else if (record_type
== HFSPLUS_ATTR_FORK_DATA
||
376 record_type
== HFSPLUS_ATTR_EXTENTS
) {
377 printk(KERN_ERR
"hfs: only inline data xattr are supported\n");
381 printk(KERN_ERR
"hfs: 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 printk(KERN_ERR
"hfs: 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 printk(KERN_ERR
"hfs: 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 printk(KERN_ERR
"hfs: invalid xattr key length: %d\n",
534 hfs_bnode_read(fd
.bnode
, &attr_key
,
535 fd
.keyoffset
, key_len
+ sizeof(key_len
));
537 if (be32_to_cpu(attr_key
.cnid
) != inode
->i_ino
)
540 xattr_name_len
= HFSPLUS_ATTR_MAX_STRLEN
;
541 if (hfsplus_uni2asc(inode
->i_sb
,
542 (const struct hfsplus_unistr
*)&fd
.key
->attr
.key_name
,
543 strbuf
, &xattr_name_len
)) {
544 printk(KERN_ERR
"hfs: unicode conversion failed\n");
549 if (!buffer
|| !size
) {
550 if (can_list(strbuf
))
551 res
+= name_len(strbuf
, xattr_name_len
);
552 } else if (can_list(strbuf
)) {
553 if (size
< (res
+ name_len(strbuf
, xattr_name_len
))) {
557 res
+= copy_name(buffer
+ res
,
558 strbuf
, xattr_name_len
);
561 if (hfs_brec_goto(&fd
, 1))
570 int hfsplus_removexattr(struct dentry
*dentry
, const char *name
)
573 struct inode
*inode
= dentry
->d_inode
;
574 struct hfs_find_data cat_fd
;
577 int is_xattr_acl_deleted
= 0;
578 int is_all_xattrs_deleted
= 0;
580 if ((!S_ISREG(inode
->i_mode
) &&
581 !S_ISDIR(inode
->i_mode
)) ||
582 HFSPLUS_IS_RSRC(inode
))
585 if (!HFSPLUS_SB(inode
->i_sb
)->attr_tree
)
588 err
= can_set_xattr(inode
, name
, NULL
, 0);
592 if (strncmp(name
, XATTR_MAC_OSX_PREFIX
,
593 XATTR_MAC_OSX_PREFIX_LEN
) == 0)
594 name
+= XATTR_MAC_OSX_PREFIX_LEN
;
596 if (!strcmp_xattr_finder_info(name
))
599 err
= hfs_find_init(HFSPLUS_SB(inode
->i_sb
)->cat_tree
, &cat_fd
);
601 printk(KERN_ERR
"hfs: can't init xattr find struct\n");
605 err
= hfsplus_find_cat(inode
->i_sb
, inode
->i_ino
, &cat_fd
);
607 printk(KERN_ERR
"hfs: catalog searching failed\n");
608 goto end_removexattr
;
611 err
= hfsplus_delete_attr(inode
, name
);
613 goto end_removexattr
;
615 is_xattr_acl_deleted
= !strcmp_xattr_acl(name
);
616 is_all_xattrs_deleted
= !hfsplus_attr_exists(inode
, NULL
);
618 if (!is_xattr_acl_deleted
&& !is_all_xattrs_deleted
)
619 goto end_removexattr
;
621 cat_entry_type
= hfs_bnode_read_u16(cat_fd
.bnode
, cat_fd
.entryoffset
);
623 if (cat_entry_type
== HFSPLUS_FOLDER
) {
624 flags
= hfs_bnode_read_u16(cat_fd
.bnode
, cat_fd
.entryoffset
+
625 offsetof(struct hfsplus_cat_folder
, flags
));
626 if (is_xattr_acl_deleted
)
627 flags
&= ~HFSPLUS_ACL_EXISTS
;
628 if (is_all_xattrs_deleted
)
629 flags
&= ~HFSPLUS_XATTR_EXISTS
;
630 hfs_bnode_write_u16(cat_fd
.bnode
, cat_fd
.entryoffset
+
631 offsetof(struct hfsplus_cat_folder
, flags
),
633 hfsplus_mark_inode_dirty(inode
, HFSPLUS_I_CAT_DIRTY
);
634 } else if (cat_entry_type
== HFSPLUS_FILE
) {
635 flags
= hfs_bnode_read_u16(cat_fd
.bnode
, cat_fd
.entryoffset
+
636 offsetof(struct hfsplus_cat_file
, flags
));
637 if (is_xattr_acl_deleted
)
638 flags
&= ~HFSPLUS_ACL_EXISTS
;
639 if (is_all_xattrs_deleted
)
640 flags
&= ~HFSPLUS_XATTR_EXISTS
;
641 hfs_bnode_write_u16(cat_fd
.bnode
, cat_fd
.entryoffset
+
642 offsetof(struct hfsplus_cat_file
, flags
),
644 hfsplus_mark_inode_dirty(inode
, HFSPLUS_I_CAT_DIRTY
);
646 printk(KERN_ERR
"hfs: invalid catalog entry type\n");
648 goto end_removexattr
;
652 hfs_find_exit(&cat_fd
);
656 static int hfsplus_osx_getxattr(struct dentry
*dentry
, const char *name
,
657 void *buffer
, size_t size
, int type
)
659 char xattr_name
[HFSPLUS_ATTR_MAX_STRLEN
+
660 XATTR_MAC_OSX_PREFIX_LEN
+ 1] = {0};
661 size_t len
= strlen(name
);
663 if (!strcmp(name
, ""))
666 if (len
> HFSPLUS_ATTR_MAX_STRLEN
)
669 strcpy(xattr_name
, XATTR_MAC_OSX_PREFIX
);
670 strcpy(xattr_name
+ XATTR_MAC_OSX_PREFIX_LEN
, name
);
672 return hfsplus_getxattr(dentry
, xattr_name
, buffer
, size
);
675 static int hfsplus_osx_setxattr(struct dentry
*dentry
, const char *name
,
676 const void *buffer
, size_t size
, int flags
, int type
)
678 char xattr_name
[HFSPLUS_ATTR_MAX_STRLEN
+
679 XATTR_MAC_OSX_PREFIX_LEN
+ 1] = {0};
680 size_t len
= strlen(name
);
682 if (!strcmp(name
, ""))
685 if (len
> HFSPLUS_ATTR_MAX_STRLEN
)
688 strcpy(xattr_name
, XATTR_MAC_OSX_PREFIX
);
689 strcpy(xattr_name
+ XATTR_MAC_OSX_PREFIX_LEN
, name
);
691 return hfsplus_setxattr(dentry
, xattr_name
, buffer
, size
, flags
);
694 static size_t hfsplus_osx_listxattr(struct dentry
*dentry
, char *list
,
695 size_t list_size
, const char *name
, size_t name_len
, int type
)
698 * This method is not used.
699 * It is used hfsplus_listxattr() instead of generic_listxattr().
704 const struct xattr_handler hfsplus_xattr_osx_handler
= {
705 .prefix
= XATTR_MAC_OSX_PREFIX
,
706 .list
= hfsplus_osx_listxattr
,
707 .get
= hfsplus_osx_getxattr
,
708 .set
= hfsplus_osx_setxattr
,