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