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 <linux/posix_acl_xattr.h> 11#include <linux/nls.h> 12#include "xattr.h" 13#include "acl.h" 14 15static int hfsplus_removexattr(struct inode *inode, const char *name); 16 17const struct xattr_handler *hfsplus_xattr_handlers[] = { 18 &hfsplus_xattr_osx_handler, 19 &hfsplus_xattr_user_handler, 20 &hfsplus_xattr_trusted_handler, 21#ifdef CONFIG_HFSPLUS_FS_POSIX_ACL 22 &posix_acl_access_xattr_handler, 23 &posix_acl_default_xattr_handler, 24#endif 25 &hfsplus_xattr_security_handler, 26 NULL 27}; 28 29static int strcmp_xattr_finder_info(const char *name) 30{ 31 if (name) { 32 return strncmp(name, HFSPLUS_XATTR_FINDER_INFO_NAME, 33 sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME)); 34 } 35 return -1; 36} 37 38static int strcmp_xattr_acl(const char *name) 39{ 40 if (name) { 41 return strncmp(name, HFSPLUS_XATTR_ACL_NAME, 42 sizeof(HFSPLUS_XATTR_ACL_NAME)); 43 } 44 return -1; 45} 46 47static bool is_known_namespace(const char *name) 48{ 49 if (strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) && 50 strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && 51 strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) && 52 strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN)) 53 return false; 54 55 return true; 56} 57 58static void hfsplus_init_header_node(struct inode *attr_file, 59 u32 clump_size, 60 char *buf, u16 node_size) 61{ 62 struct hfs_bnode_desc *desc; 63 struct hfs_btree_header_rec *head; 64 u16 offset; 65 __be16 *rec_offsets; 66 u32 hdr_node_map_rec_bits; 67 char *bmp; 68 u32 used_nodes; 69 u32 used_bmp_bytes; 70 u64 tmp; 71 72 hfs_dbg(ATTR_MOD, "init_hdr_attr_file: clump %u, node_size %u\n", 73 clump_size, node_size); 74 75 /* The end of the node contains list of record offsets */ 76 rec_offsets = (__be16 *)(buf + node_size); 77 78 desc = (struct hfs_bnode_desc *)buf; 79 desc->type = HFS_NODE_HEADER; 80 desc->num_recs = cpu_to_be16(HFSPLUS_BTREE_HDR_NODE_RECS_COUNT); 81 offset = sizeof(struct hfs_bnode_desc); 82 *--rec_offsets = cpu_to_be16(offset); 83 84 head = (struct hfs_btree_header_rec *)(buf + offset); 85 head->node_size = cpu_to_be16(node_size); 86 tmp = i_size_read(attr_file); 87 do_div(tmp, node_size); 88 head->node_count = cpu_to_be32(tmp); 89 head->free_nodes = cpu_to_be32(be32_to_cpu(head->node_count) - 1); 90 head->clump_size = cpu_to_be32(clump_size); 91 head->attributes |= cpu_to_be32(HFS_TREE_BIGKEYS | HFS_TREE_VARIDXKEYS); 92 head->max_key_len = cpu_to_be16(HFSPLUS_ATTR_KEYLEN - sizeof(u16)); 93 offset += sizeof(struct hfs_btree_header_rec); 94 *--rec_offsets = cpu_to_be16(offset); 95 offset += HFSPLUS_BTREE_HDR_USER_BYTES; 96 *--rec_offsets = cpu_to_be16(offset); 97 98 hdr_node_map_rec_bits = 8 * (node_size - offset - (4 * sizeof(u16))); 99 if (be32_to_cpu(head->node_count) > hdr_node_map_rec_bits) { 100 u32 map_node_bits; 101 u32 map_nodes; 102 103 desc->next = cpu_to_be32(be32_to_cpu(head->leaf_tail) + 1); 104 map_node_bits = 8 * (node_size - sizeof(struct hfs_bnode_desc) - 105 (2 * sizeof(u16)) - 2); 106 map_nodes = (be32_to_cpu(head->node_count) - 107 hdr_node_map_rec_bits + 108 (map_node_bits - 1)) / map_node_bits; 109 be32_add_cpu(&head->free_nodes, 0 - map_nodes); 110 } 111 112 bmp = buf + offset; 113 used_nodes = 114 be32_to_cpu(head->node_count) - be32_to_cpu(head->free_nodes); 115 used_bmp_bytes = used_nodes / 8; 116 if (used_bmp_bytes) { 117 memset(bmp, 0xFF, used_bmp_bytes); 118 bmp += used_bmp_bytes; 119 used_nodes %= 8; 120 } 121 *bmp = ~(0xFF >> used_nodes); 122 offset += hdr_node_map_rec_bits / 8; 123 *--rec_offsets = cpu_to_be16(offset); 124} 125 126static int hfsplus_create_attributes_file(struct super_block *sb) 127{ 128 int err = 0; 129 struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 130 struct inode *attr_file; 131 struct hfsplus_inode_info *hip; 132 u32 clump_size; 133 u16 node_size = HFSPLUS_ATTR_TREE_NODE_SIZE; 134 char *buf; 135 int index, written; 136 struct address_space *mapping; 137 struct page *page; 138 int old_state = HFSPLUS_EMPTY_ATTR_TREE; 139 140 hfs_dbg(ATTR_MOD, "create_attr_file: ino %d\n", HFSPLUS_ATTR_CNID); 141 142check_attr_tree_state_again: 143 switch (atomic_read(&sbi->attr_tree_state)) { 144 case HFSPLUS_EMPTY_ATTR_TREE: 145 if (old_state != atomic_cmpxchg(&sbi->attr_tree_state, 146 old_state, 147 HFSPLUS_CREATING_ATTR_TREE)) 148 goto check_attr_tree_state_again; 149 break; 150 case HFSPLUS_CREATING_ATTR_TREE: 151 /* 152 * This state means that another thread is in process 153 * of AttributesFile creation. Theoretically, it is 154 * possible to be here. But really __setxattr() method 155 * first of all calls hfs_find_init() for lookup in 156 * B-tree of CatalogFile. This method locks mutex of 157 * CatalogFile's B-tree. As a result, if some thread 158 * is inside AttributedFile creation operation then 159 * another threads will be waiting unlocking of 160 * CatalogFile's B-tree's mutex. However, if code will 161 * change then we will return error code (-EAGAIN) from 162 * here. Really, it means that first try to set of xattr 163 * fails with error but second attempt will have success. 164 */ 165 return -EAGAIN; 166 case HFSPLUS_VALID_ATTR_TREE: 167 return 0; 168 case HFSPLUS_FAILED_ATTR_TREE: 169 return -EOPNOTSUPP; 170 default: 171 BUG(); 172 } 173 174 attr_file = hfsplus_iget(sb, HFSPLUS_ATTR_CNID); 175 if (IS_ERR(attr_file)) { 176 pr_err("failed to load attributes file\n"); 177 return PTR_ERR(attr_file); 178 } 179 180 BUG_ON(i_size_read(attr_file) != 0); 181 182 hip = HFSPLUS_I(attr_file); 183 184 clump_size = hfsplus_calc_btree_clump_size(sb->s_blocksize, 185 node_size, 186 sbi->sect_count, 187 HFSPLUS_ATTR_CNID); 188 189 mutex_lock(&hip->extents_lock); 190 hip->clump_blocks = clump_size >> sbi->alloc_blksz_shift; 191 mutex_unlock(&hip->extents_lock); 192 193 if (sbi->free_blocks <= (hip->clump_blocks << 1)) { 194 err = -ENOSPC; 195 goto end_attr_file_creation; 196 } 197 198 while (hip->alloc_blocks < hip->clump_blocks) { 199 err = hfsplus_file_extend(attr_file, false); 200 if (unlikely(err)) { 201 pr_err("failed to extend attributes file\n"); 202 goto end_attr_file_creation; 203 } 204 hip->phys_size = attr_file->i_size = 205 (loff_t)hip->alloc_blocks << sbi->alloc_blksz_shift; 206 hip->fs_blocks = hip->alloc_blocks << sbi->fs_shift; 207 inode_set_bytes(attr_file, attr_file->i_size); 208 } 209 210 buf = kzalloc(node_size, GFP_NOFS); 211 if (!buf) { 212 pr_err("failed to allocate memory for header node\n"); 213 err = -ENOMEM; 214 goto end_attr_file_creation; 215 } 216 217 hfsplus_init_header_node(attr_file, clump_size, buf, node_size); 218 219 mapping = attr_file->i_mapping; 220 221 index = 0; 222 written = 0; 223 for (; written < node_size; index++, written += PAGE_CACHE_SIZE) { 224 void *kaddr; 225 226 page = read_mapping_page(mapping, index, NULL); 227 if (IS_ERR(page)) { 228 err = PTR_ERR(page); 229 goto failed_header_node_init; 230 } 231 232 kaddr = kmap_atomic(page); 233 memcpy(kaddr, buf + written, 234 min_t(size_t, PAGE_CACHE_SIZE, node_size - written)); 235 kunmap_atomic(kaddr); 236 237 set_page_dirty(page); 238 page_cache_release(page); 239 } 240 241 hfsplus_mark_inode_dirty(attr_file, HFSPLUS_I_ATTR_DIRTY); 242 243 sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID); 244 if (!sbi->attr_tree) 245 pr_err("failed to load attributes file\n"); 246 247failed_header_node_init: 248 kfree(buf); 249 250end_attr_file_creation: 251 iput(attr_file); 252 253 if (!err) 254 atomic_set(&sbi->attr_tree_state, HFSPLUS_VALID_ATTR_TREE); 255 else if (err == -ENOSPC) 256 atomic_set(&sbi->attr_tree_state, HFSPLUS_EMPTY_ATTR_TREE); 257 else 258 atomic_set(&sbi->attr_tree_state, HFSPLUS_FAILED_ATTR_TREE); 259 260 return err; 261} 262 263int __hfsplus_setxattr(struct inode *inode, const char *name, 264 const void *value, size_t size, int flags) 265{ 266 int err = 0; 267 struct hfs_find_data cat_fd; 268 hfsplus_cat_entry entry; 269 u16 cat_entry_flags, cat_entry_type; 270 u16 folder_finderinfo_len = sizeof(struct DInfo) + 271 sizeof(struct DXInfo); 272 u16 file_finderinfo_len = sizeof(struct FInfo) + 273 sizeof(struct FXInfo); 274 275 if ((!S_ISREG(inode->i_mode) && 276 !S_ISDIR(inode->i_mode)) || 277 HFSPLUS_IS_RSRC(inode)) 278 return -EOPNOTSUPP; 279 280 if (value == NULL) 281 return hfsplus_removexattr(inode, name); 282 283 err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd); 284 if (err) { 285 pr_err("can't init xattr find struct\n"); 286 return err; 287 } 288 289 err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd); 290 if (err) { 291 pr_err("catalog searching failed\n"); 292 goto end_setxattr; 293 } 294 295 if (!strcmp_xattr_finder_info(name)) { 296 if (flags & XATTR_CREATE) { 297 pr_err("xattr exists yet\n"); 298 err = -EOPNOTSUPP; 299 goto end_setxattr; 300 } 301 hfs_bnode_read(cat_fd.bnode, &entry, cat_fd.entryoffset, 302 sizeof(hfsplus_cat_entry)); 303 if (be16_to_cpu(entry.type) == HFSPLUS_FOLDER) { 304 if (size == folder_finderinfo_len) { 305 memcpy(&entry.folder.user_info, value, 306 folder_finderinfo_len); 307 hfs_bnode_write(cat_fd.bnode, &entry, 308 cat_fd.entryoffset, 309 sizeof(struct hfsplus_cat_folder)); 310 hfsplus_mark_inode_dirty(inode, 311 HFSPLUS_I_CAT_DIRTY); 312 } else { 313 err = -ERANGE; 314 goto end_setxattr; 315 } 316 } else if (be16_to_cpu(entry.type) == HFSPLUS_FILE) { 317 if (size == file_finderinfo_len) { 318 memcpy(&entry.file.user_info, value, 319 file_finderinfo_len); 320 hfs_bnode_write(cat_fd.bnode, &entry, 321 cat_fd.entryoffset, 322 sizeof(struct hfsplus_cat_file)); 323 hfsplus_mark_inode_dirty(inode, 324 HFSPLUS_I_CAT_DIRTY); 325 } else { 326 err = -ERANGE; 327 goto end_setxattr; 328 } 329 } else { 330 err = -EOPNOTSUPP; 331 goto end_setxattr; 332 } 333 goto end_setxattr; 334 } 335 336 if (!HFSPLUS_SB(inode->i_sb)->attr_tree) { 337 err = hfsplus_create_attributes_file(inode->i_sb); 338 if (unlikely(err)) 339 goto end_setxattr; 340 } 341 342 if (hfsplus_attr_exists(inode, name)) { 343 if (flags & XATTR_CREATE) { 344 pr_err("xattr exists yet\n"); 345 err = -EOPNOTSUPP; 346 goto end_setxattr; 347 } 348 err = hfsplus_delete_attr(inode, name); 349 if (err) 350 goto end_setxattr; 351 err = hfsplus_create_attr(inode, name, value, size); 352 if (err) 353 goto end_setxattr; 354 } else { 355 if (flags & XATTR_REPLACE) { 356 pr_err("cannot replace xattr\n"); 357 err = -EOPNOTSUPP; 358 goto end_setxattr; 359 } 360 err = hfsplus_create_attr(inode, name, value, size); 361 if (err) 362 goto end_setxattr; 363 } 364 365 cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset); 366 if (cat_entry_type == HFSPLUS_FOLDER) { 367 cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode, 368 cat_fd.entryoffset + 369 offsetof(struct hfsplus_cat_folder, flags)); 370 cat_entry_flags |= HFSPLUS_XATTR_EXISTS; 371 if (!strcmp_xattr_acl(name)) 372 cat_entry_flags |= HFSPLUS_ACL_EXISTS; 373 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset + 374 offsetof(struct hfsplus_cat_folder, flags), 375 cat_entry_flags); 376 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY); 377 } else if (cat_entry_type == HFSPLUS_FILE) { 378 cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode, 379 cat_fd.entryoffset + 380 offsetof(struct hfsplus_cat_file, flags)); 381 cat_entry_flags |= HFSPLUS_XATTR_EXISTS; 382 if (!strcmp_xattr_acl(name)) 383 cat_entry_flags |= HFSPLUS_ACL_EXISTS; 384 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset + 385 offsetof(struct hfsplus_cat_file, flags), 386 cat_entry_flags); 387 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY); 388 } else { 389 pr_err("invalid catalog entry type\n"); 390 err = -EIO; 391 goto end_setxattr; 392 } 393 394end_setxattr: 395 hfs_find_exit(&cat_fd); 396 return err; 397} 398 399static int name_len(const char *xattr_name, int xattr_name_len) 400{ 401 int len = xattr_name_len + 1; 402 403 if (!is_known_namespace(xattr_name)) 404 len += XATTR_MAC_OSX_PREFIX_LEN; 405 406 return len; 407} 408 409static int copy_name(char *buffer, const char *xattr_name, int name_len) 410{ 411 int len = name_len; 412 int offset = 0; 413 414 if (!is_known_namespace(xattr_name)) { 415 strncpy(buffer, XATTR_MAC_OSX_PREFIX, XATTR_MAC_OSX_PREFIX_LEN); 416 offset += XATTR_MAC_OSX_PREFIX_LEN; 417 len += XATTR_MAC_OSX_PREFIX_LEN; 418 } 419 420 strncpy(buffer + offset, xattr_name, name_len); 421 memset(buffer + offset + name_len, 0, 1); 422 len += 1; 423 424 return len; 425} 426 427int hfsplus_setxattr(struct dentry *dentry, const char *name, 428 const void *value, size_t size, int flags, 429 const char *prefix, size_t prefixlen) 430{ 431 char *xattr_name; 432 int res; 433 434 if (!strcmp(name, "")) 435 return -EINVAL; 436 437 xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1, 438 GFP_KERNEL); 439 if (!xattr_name) 440 return -ENOMEM; 441 strcpy(xattr_name, prefix); 442 strcpy(xattr_name + prefixlen, name); 443 res = __hfsplus_setxattr(d_inode(dentry), xattr_name, value, size, 444 flags); 445 kfree(xattr_name); 446 return res; 447} 448 449static ssize_t hfsplus_getxattr_finder_info(struct inode *inode, 450 void *value, size_t size) 451{ 452 ssize_t res = 0; 453 struct hfs_find_data fd; 454 u16 entry_type; 455 u16 folder_rec_len = sizeof(struct DInfo) + sizeof(struct DXInfo); 456 u16 file_rec_len = sizeof(struct FInfo) + sizeof(struct FXInfo); 457 u16 record_len = max(folder_rec_len, file_rec_len); 458 u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)]; 459 u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)]; 460 461 if (size >= record_len) { 462 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd); 463 if (res) { 464 pr_err("can't init xattr find struct\n"); 465 return res; 466 } 467 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd); 468 if (res) 469 goto end_getxattr_finder_info; 470 entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset); 471 472 if (entry_type == HFSPLUS_FOLDER) { 473 hfs_bnode_read(fd.bnode, folder_finder_info, 474 fd.entryoffset + 475 offsetof(struct hfsplus_cat_folder, user_info), 476 folder_rec_len); 477 memcpy(value, folder_finder_info, folder_rec_len); 478 res = folder_rec_len; 479 } else if (entry_type == HFSPLUS_FILE) { 480 hfs_bnode_read(fd.bnode, file_finder_info, 481 fd.entryoffset + 482 offsetof(struct hfsplus_cat_file, user_info), 483 file_rec_len); 484 memcpy(value, file_finder_info, file_rec_len); 485 res = file_rec_len; 486 } else { 487 res = -EOPNOTSUPP; 488 goto end_getxattr_finder_info; 489 } 490 } else 491 res = size ? -ERANGE : record_len; 492 493end_getxattr_finder_info: 494 if (size >= record_len) 495 hfs_find_exit(&fd); 496 return res; 497} 498 499ssize_t __hfsplus_getxattr(struct inode *inode, const char *name, 500 void *value, size_t size) 501{ 502 struct hfs_find_data fd; 503 hfsplus_attr_entry *entry; 504 __be32 xattr_record_type; 505 u32 record_type; 506 u16 record_length = 0; 507 ssize_t res = 0; 508 509 if ((!S_ISREG(inode->i_mode) && 510 !S_ISDIR(inode->i_mode)) || 511 HFSPLUS_IS_RSRC(inode)) 512 return -EOPNOTSUPP; 513 514 if (!strcmp_xattr_finder_info(name)) 515 return hfsplus_getxattr_finder_info(inode, value, size); 516 517 if (!HFSPLUS_SB(inode->i_sb)->attr_tree) 518 return -EOPNOTSUPP; 519 520 entry = hfsplus_alloc_attr_entry(); 521 if (!entry) { 522 pr_err("can't allocate xattr entry\n"); 523 return -ENOMEM; 524 } 525 526 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd); 527 if (res) { 528 pr_err("can't init xattr find struct\n"); 529 goto failed_getxattr_init; 530 } 531 532 res = hfsplus_find_attr(inode->i_sb, inode->i_ino, name, &fd); 533 if (res) { 534 if (res == -ENOENT) 535 res = -ENODATA; 536 else 537 pr_err("xattr searching failed\n"); 538 goto out; 539 } 540 541 hfs_bnode_read(fd.bnode, &xattr_record_type, 542 fd.entryoffset, sizeof(xattr_record_type)); 543 record_type = be32_to_cpu(xattr_record_type); 544 if (record_type == HFSPLUS_ATTR_INLINE_DATA) { 545 record_length = hfs_bnode_read_u16(fd.bnode, 546 fd.entryoffset + 547 offsetof(struct hfsplus_attr_inline_data, 548 length)); 549 if (record_length > HFSPLUS_MAX_INLINE_DATA_SIZE) { 550 pr_err("invalid xattr record size\n"); 551 res = -EIO; 552 goto out; 553 } 554 } else if (record_type == HFSPLUS_ATTR_FORK_DATA || 555 record_type == HFSPLUS_ATTR_EXTENTS) { 556 pr_err("only inline data xattr are supported\n"); 557 res = -EOPNOTSUPP; 558 goto out; 559 } else { 560 pr_err("invalid xattr record\n"); 561 res = -EIO; 562 goto out; 563 } 564 565 if (size) { 566 hfs_bnode_read(fd.bnode, entry, fd.entryoffset, 567 offsetof(struct hfsplus_attr_inline_data, 568 raw_bytes) + record_length); 569 } 570 571 if (size >= record_length) { 572 memcpy(value, entry->inline_data.raw_bytes, record_length); 573 res = record_length; 574 } else 575 res = size ? -ERANGE : record_length; 576 577out: 578 hfs_find_exit(&fd); 579 580failed_getxattr_init: 581 hfsplus_destroy_attr_entry(entry); 582 return res; 583} 584 585ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name, 586 void *value, size_t size, 587 const char *prefix, size_t prefixlen) 588{ 589 int res; 590 char *xattr_name; 591 592 if (!strcmp(name, "")) 593 return -EINVAL; 594 595 xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1, 596 GFP_KERNEL); 597 if (!xattr_name) 598 return -ENOMEM; 599 600 strcpy(xattr_name, prefix); 601 strcpy(xattr_name + prefixlen, name); 602 603 res = __hfsplus_getxattr(d_inode(dentry), xattr_name, value, size); 604 kfree(xattr_name); 605 return res; 606 607} 608 609static inline int can_list(const char *xattr_name) 610{ 611 if (!xattr_name) 612 return 0; 613 614 return strncmp(xattr_name, XATTR_TRUSTED_PREFIX, 615 XATTR_TRUSTED_PREFIX_LEN) || 616 capable(CAP_SYS_ADMIN); 617} 618 619static ssize_t hfsplus_listxattr_finder_info(struct dentry *dentry, 620 char *buffer, size_t size) 621{ 622 ssize_t res = 0; 623 struct inode *inode = d_inode(dentry); 624 struct hfs_find_data fd; 625 u16 entry_type; 626 u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)]; 627 u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)]; 628 unsigned long len, found_bit; 629 int xattr_name_len, symbols_count; 630 631 res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd); 632 if (res) { 633 pr_err("can't init xattr find struct\n"); 634 return res; 635 } 636 637 res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd); 638 if (res) 639 goto end_listxattr_finder_info; 640 641 entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset); 642 if (entry_type == HFSPLUS_FOLDER) { 643 len = sizeof(struct DInfo) + sizeof(struct DXInfo); 644 hfs_bnode_read(fd.bnode, folder_finder_info, 645 fd.entryoffset + 646 offsetof(struct hfsplus_cat_folder, user_info), 647 len); 648 found_bit = find_first_bit((void *)folder_finder_info, len*8); 649 } else if (entry_type == HFSPLUS_FILE) { 650 len = sizeof(struct FInfo) + sizeof(struct FXInfo); 651 hfs_bnode_read(fd.bnode, file_finder_info, 652 fd.entryoffset + 653 offsetof(struct hfsplus_cat_file, user_info), 654 len); 655 found_bit = find_first_bit((void *)file_finder_info, len*8); 656 } else { 657 res = -EOPNOTSUPP; 658 goto end_listxattr_finder_info; 659 } 660 661 if (found_bit >= (len*8)) 662 res = 0; 663 else { 664 symbols_count = sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME) - 1; 665 xattr_name_len = 666 name_len(HFSPLUS_XATTR_FINDER_INFO_NAME, symbols_count); 667 if (!buffer || !size) { 668 if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME)) 669 res = xattr_name_len; 670 } else if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME)) { 671 if (size < xattr_name_len) 672 res = -ERANGE; 673 else { 674 res = copy_name(buffer, 675 HFSPLUS_XATTR_FINDER_INFO_NAME, 676 symbols_count); 677 } 678 } 679 } 680 681end_listxattr_finder_info: 682 hfs_find_exit(&fd); 683 684 return res; 685} 686 687ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size) 688{ 689 ssize_t err; 690 ssize_t res = 0; 691 struct inode *inode = d_inode(dentry); 692 struct hfs_find_data fd; 693 u16 key_len = 0; 694 struct hfsplus_attr_key attr_key; 695 char *strbuf; 696 int xattr_name_len; 697 698 if ((!S_ISREG(inode->i_mode) && 699 !S_ISDIR(inode->i_mode)) || 700 HFSPLUS_IS_RSRC(inode)) 701 return -EOPNOTSUPP; 702 703 res = hfsplus_listxattr_finder_info(dentry, buffer, size); 704 if (res < 0) 705 return res; 706 else if (!HFSPLUS_SB(inode->i_sb)->attr_tree) 707 return (res == 0) ? -EOPNOTSUPP : res; 708 709 err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd); 710 if (err) { 711 pr_err("can't init xattr find struct\n"); 712 return err; 713 } 714 715 strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 716 XATTR_MAC_OSX_PREFIX_LEN + 1, GFP_KERNEL); 717 if (!strbuf) { 718 res = -ENOMEM; 719 goto out; 720 } 721 722 err = hfsplus_find_attr(inode->i_sb, inode->i_ino, NULL, &fd); 723 if (err) { 724 if (err == -ENOENT) { 725 if (res == 0) 726 res = -ENODATA; 727 goto end_listxattr; 728 } else { 729 res = err; 730 goto end_listxattr; 731 } 732 } 733 734 for (;;) { 735 key_len = hfs_bnode_read_u16(fd.bnode, fd.keyoffset); 736 if (key_len == 0 || key_len > fd.tree->max_key_len) { 737 pr_err("invalid xattr key length: %d\n", key_len); 738 res = -EIO; 739 goto end_listxattr; 740 } 741 742 hfs_bnode_read(fd.bnode, &attr_key, 743 fd.keyoffset, key_len + sizeof(key_len)); 744 745 if (be32_to_cpu(attr_key.cnid) != inode->i_ino) 746 goto end_listxattr; 747 748 xattr_name_len = NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN; 749 if (hfsplus_uni2asc(inode->i_sb, 750 (const struct hfsplus_unistr *)&fd.key->attr.key_name, 751 strbuf, &xattr_name_len)) { 752 pr_err("unicode conversion failed\n"); 753 res = -EIO; 754 goto end_listxattr; 755 } 756 757 if (!buffer || !size) { 758 if (can_list(strbuf)) 759 res += name_len(strbuf, xattr_name_len); 760 } else if (can_list(strbuf)) { 761 if (size < (res + name_len(strbuf, xattr_name_len))) { 762 res = -ERANGE; 763 goto end_listxattr; 764 } else 765 res += copy_name(buffer + res, 766 strbuf, xattr_name_len); 767 } 768 769 if (hfs_brec_goto(&fd, 1)) 770 goto end_listxattr; 771 } 772 773end_listxattr: 774 kfree(strbuf); 775out: 776 hfs_find_exit(&fd); 777 return res; 778} 779 780static int hfsplus_removexattr(struct inode *inode, const char *name) 781{ 782 int err = 0; 783 struct hfs_find_data cat_fd; 784 u16 flags; 785 u16 cat_entry_type; 786 int is_xattr_acl_deleted = 0; 787 int is_all_xattrs_deleted = 0; 788 789 if (!HFSPLUS_SB(inode->i_sb)->attr_tree) 790 return -EOPNOTSUPP; 791 792 if (!strcmp_xattr_finder_info(name)) 793 return -EOPNOTSUPP; 794 795 err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd); 796 if (err) { 797 pr_err("can't init xattr find struct\n"); 798 return err; 799 } 800 801 err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd); 802 if (err) { 803 pr_err("catalog searching failed\n"); 804 goto end_removexattr; 805 } 806 807 err = hfsplus_delete_attr(inode, name); 808 if (err) 809 goto end_removexattr; 810 811 is_xattr_acl_deleted = !strcmp_xattr_acl(name); 812 is_all_xattrs_deleted = !hfsplus_attr_exists(inode, NULL); 813 814 if (!is_xattr_acl_deleted && !is_all_xattrs_deleted) 815 goto end_removexattr; 816 817 cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset); 818 819 if (cat_entry_type == HFSPLUS_FOLDER) { 820 flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset + 821 offsetof(struct hfsplus_cat_folder, flags)); 822 if (is_xattr_acl_deleted) 823 flags &= ~HFSPLUS_ACL_EXISTS; 824 if (is_all_xattrs_deleted) 825 flags &= ~HFSPLUS_XATTR_EXISTS; 826 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset + 827 offsetof(struct hfsplus_cat_folder, flags), 828 flags); 829 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY); 830 } else if (cat_entry_type == HFSPLUS_FILE) { 831 flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset + 832 offsetof(struct hfsplus_cat_file, flags)); 833 if (is_xattr_acl_deleted) 834 flags &= ~HFSPLUS_ACL_EXISTS; 835 if (is_all_xattrs_deleted) 836 flags &= ~HFSPLUS_XATTR_EXISTS; 837 hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset + 838 offsetof(struct hfsplus_cat_file, flags), 839 flags); 840 hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY); 841 } else { 842 pr_err("invalid catalog entry type\n"); 843 err = -EIO; 844 goto end_removexattr; 845 } 846 847end_removexattr: 848 hfs_find_exit(&cat_fd); 849 return err; 850} 851 852static int hfsplus_osx_getxattr(struct dentry *dentry, const char *name, 853 void *buffer, size_t size, int type) 854{ 855 if (!strcmp(name, "")) 856 return -EINVAL; 857 858 /* 859 * Don't allow retrieving properly prefixed attributes 860 * by prepending them with "osx." 861 */ 862 if (is_known_namespace(name)) 863 return -EOPNOTSUPP; 864 865 /* 866 * osx is the namespace we use to indicate an unprefixed 867 * attribute on the filesystem (like the ones that OS X 868 * creates), so we pass the name through unmodified (after 869 * ensuring it doesn't conflict with another namespace). 870 */ 871 return __hfsplus_getxattr(d_inode(dentry), name, buffer, size); 872} 873 874static int hfsplus_osx_setxattr(struct dentry *dentry, const char *name, 875 const void *buffer, size_t size, int flags, int type) 876{ 877 if (!strcmp(name, "")) 878 return -EINVAL; 879 880 /* 881 * Don't allow setting properly prefixed attributes 882 * by prepending them with "osx." 883 */ 884 if (is_known_namespace(name)) 885 return -EOPNOTSUPP; 886 887 /* 888 * osx is the namespace we use to indicate an unprefixed 889 * attribute on the filesystem (like the ones that OS X 890 * creates), so we pass the name through unmodified (after 891 * ensuring it doesn't conflict with another namespace). 892 */ 893 return __hfsplus_setxattr(d_inode(dentry), name, buffer, size, flags); 894} 895 896static size_t hfsplus_osx_listxattr(struct dentry *dentry, char *list, 897 size_t list_size, const char *name, size_t name_len, int type) 898{ 899 /* 900 * This method is not used. 901 * It is used hfsplus_listxattr() instead of generic_listxattr(). 902 */ 903 return -EOPNOTSUPP; 904} 905 906const struct xattr_handler hfsplus_xattr_osx_handler = { 907 .prefix = XATTR_MAC_OSX_PREFIX, 908 .list = hfsplus_osx_listxattr, 909 .get = hfsplus_osx_getxattr, 910 .set = hfsplus_osx_setxattr, 911}; 912