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