162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/hfsplus/xattr.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Vyacheslav Dubeyko <slava@dubeyko.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Logic of processing extended attributes 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "hfsplus_fs.h" 1162306a36Sopenharmony_ci#include <linux/nls.h> 1262306a36Sopenharmony_ci#include "xattr.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic int hfsplus_removexattr(struct inode *inode, const char *name); 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ciconst struct xattr_handler *hfsplus_xattr_handlers[] = { 1762306a36Sopenharmony_ci &hfsplus_xattr_osx_handler, 1862306a36Sopenharmony_ci &hfsplus_xattr_user_handler, 1962306a36Sopenharmony_ci &hfsplus_xattr_trusted_handler, 2062306a36Sopenharmony_ci &hfsplus_xattr_security_handler, 2162306a36Sopenharmony_ci NULL 2262306a36Sopenharmony_ci}; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistatic int strcmp_xattr_finder_info(const char *name) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci if (name) { 2762306a36Sopenharmony_ci return strncmp(name, HFSPLUS_XATTR_FINDER_INFO_NAME, 2862306a36Sopenharmony_ci sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME)); 2962306a36Sopenharmony_ci } 3062306a36Sopenharmony_ci return -1; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int strcmp_xattr_acl(const char *name) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci if (name) { 3662306a36Sopenharmony_ci return strncmp(name, HFSPLUS_XATTR_ACL_NAME, 3762306a36Sopenharmony_ci sizeof(HFSPLUS_XATTR_ACL_NAME)); 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci return -1; 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic bool is_known_namespace(const char *name) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci if (strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN) && 4562306a36Sopenharmony_ci strncmp(name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) && 4662306a36Sopenharmony_ci strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) && 4762306a36Sopenharmony_ci strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN)) 4862306a36Sopenharmony_ci return false; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci return true; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void hfsplus_init_header_node(struct inode *attr_file, 5462306a36Sopenharmony_ci u32 clump_size, 5562306a36Sopenharmony_ci char *buf, u16 node_size) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci struct hfs_bnode_desc *desc; 5862306a36Sopenharmony_ci struct hfs_btree_header_rec *head; 5962306a36Sopenharmony_ci u16 offset; 6062306a36Sopenharmony_ci __be16 *rec_offsets; 6162306a36Sopenharmony_ci u32 hdr_node_map_rec_bits; 6262306a36Sopenharmony_ci char *bmp; 6362306a36Sopenharmony_ci u32 used_nodes; 6462306a36Sopenharmony_ci u32 used_bmp_bytes; 6562306a36Sopenharmony_ci u64 tmp; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci hfs_dbg(ATTR_MOD, "init_hdr_attr_file: clump %u, node_size %u\n", 6862306a36Sopenharmony_ci clump_size, node_size); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* The end of the node contains list of record offsets */ 7162306a36Sopenharmony_ci rec_offsets = (__be16 *)(buf + node_size); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci desc = (struct hfs_bnode_desc *)buf; 7462306a36Sopenharmony_ci desc->type = HFS_NODE_HEADER; 7562306a36Sopenharmony_ci desc->num_recs = cpu_to_be16(HFSPLUS_BTREE_HDR_NODE_RECS_COUNT); 7662306a36Sopenharmony_ci offset = sizeof(struct hfs_bnode_desc); 7762306a36Sopenharmony_ci *--rec_offsets = cpu_to_be16(offset); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci head = (struct hfs_btree_header_rec *)(buf + offset); 8062306a36Sopenharmony_ci head->node_size = cpu_to_be16(node_size); 8162306a36Sopenharmony_ci tmp = i_size_read(attr_file); 8262306a36Sopenharmony_ci do_div(tmp, node_size); 8362306a36Sopenharmony_ci head->node_count = cpu_to_be32(tmp); 8462306a36Sopenharmony_ci head->free_nodes = cpu_to_be32(be32_to_cpu(head->node_count) - 1); 8562306a36Sopenharmony_ci head->clump_size = cpu_to_be32(clump_size); 8662306a36Sopenharmony_ci head->attributes |= cpu_to_be32(HFS_TREE_BIGKEYS | HFS_TREE_VARIDXKEYS); 8762306a36Sopenharmony_ci head->max_key_len = cpu_to_be16(HFSPLUS_ATTR_KEYLEN - sizeof(u16)); 8862306a36Sopenharmony_ci offset += sizeof(struct hfs_btree_header_rec); 8962306a36Sopenharmony_ci *--rec_offsets = cpu_to_be16(offset); 9062306a36Sopenharmony_ci offset += HFSPLUS_BTREE_HDR_USER_BYTES; 9162306a36Sopenharmony_ci *--rec_offsets = cpu_to_be16(offset); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci hdr_node_map_rec_bits = 8 * (node_size - offset - (4 * sizeof(u16))); 9462306a36Sopenharmony_ci if (be32_to_cpu(head->node_count) > hdr_node_map_rec_bits) { 9562306a36Sopenharmony_ci u32 map_node_bits; 9662306a36Sopenharmony_ci u32 map_nodes; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci desc->next = cpu_to_be32(be32_to_cpu(head->leaf_tail) + 1); 9962306a36Sopenharmony_ci map_node_bits = 8 * (node_size - sizeof(struct hfs_bnode_desc) - 10062306a36Sopenharmony_ci (2 * sizeof(u16)) - 2); 10162306a36Sopenharmony_ci map_nodes = (be32_to_cpu(head->node_count) - 10262306a36Sopenharmony_ci hdr_node_map_rec_bits + 10362306a36Sopenharmony_ci (map_node_bits - 1)) / map_node_bits; 10462306a36Sopenharmony_ci be32_add_cpu(&head->free_nodes, 0 - map_nodes); 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci bmp = buf + offset; 10862306a36Sopenharmony_ci used_nodes = 10962306a36Sopenharmony_ci be32_to_cpu(head->node_count) - be32_to_cpu(head->free_nodes); 11062306a36Sopenharmony_ci used_bmp_bytes = used_nodes / 8; 11162306a36Sopenharmony_ci if (used_bmp_bytes) { 11262306a36Sopenharmony_ci memset(bmp, 0xFF, used_bmp_bytes); 11362306a36Sopenharmony_ci bmp += used_bmp_bytes; 11462306a36Sopenharmony_ci used_nodes %= 8; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci *bmp = ~(0xFF >> used_nodes); 11762306a36Sopenharmony_ci offset += hdr_node_map_rec_bits / 8; 11862306a36Sopenharmony_ci *--rec_offsets = cpu_to_be16(offset); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic int hfsplus_create_attributes_file(struct super_block *sb) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci int err = 0; 12462306a36Sopenharmony_ci struct hfsplus_sb_info *sbi = HFSPLUS_SB(sb); 12562306a36Sopenharmony_ci struct inode *attr_file; 12662306a36Sopenharmony_ci struct hfsplus_inode_info *hip; 12762306a36Sopenharmony_ci u32 clump_size; 12862306a36Sopenharmony_ci u16 node_size = HFSPLUS_ATTR_TREE_NODE_SIZE; 12962306a36Sopenharmony_ci char *buf; 13062306a36Sopenharmony_ci int index, written; 13162306a36Sopenharmony_ci struct address_space *mapping; 13262306a36Sopenharmony_ci struct page *page; 13362306a36Sopenharmony_ci int old_state = HFSPLUS_EMPTY_ATTR_TREE; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci hfs_dbg(ATTR_MOD, "create_attr_file: ino %d\n", HFSPLUS_ATTR_CNID); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cicheck_attr_tree_state_again: 13862306a36Sopenharmony_ci switch (atomic_read(&sbi->attr_tree_state)) { 13962306a36Sopenharmony_ci case HFSPLUS_EMPTY_ATTR_TREE: 14062306a36Sopenharmony_ci if (old_state != atomic_cmpxchg(&sbi->attr_tree_state, 14162306a36Sopenharmony_ci old_state, 14262306a36Sopenharmony_ci HFSPLUS_CREATING_ATTR_TREE)) 14362306a36Sopenharmony_ci goto check_attr_tree_state_again; 14462306a36Sopenharmony_ci break; 14562306a36Sopenharmony_ci case HFSPLUS_CREATING_ATTR_TREE: 14662306a36Sopenharmony_ci /* 14762306a36Sopenharmony_ci * This state means that another thread is in process 14862306a36Sopenharmony_ci * of AttributesFile creation. Theoretically, it is 14962306a36Sopenharmony_ci * possible to be here. But really __setxattr() method 15062306a36Sopenharmony_ci * first of all calls hfs_find_init() for lookup in 15162306a36Sopenharmony_ci * B-tree of CatalogFile. This method locks mutex of 15262306a36Sopenharmony_ci * CatalogFile's B-tree. As a result, if some thread 15362306a36Sopenharmony_ci * is inside AttributedFile creation operation then 15462306a36Sopenharmony_ci * another threads will be waiting unlocking of 15562306a36Sopenharmony_ci * CatalogFile's B-tree's mutex. However, if code will 15662306a36Sopenharmony_ci * change then we will return error code (-EAGAIN) from 15762306a36Sopenharmony_ci * here. Really, it means that first try to set of xattr 15862306a36Sopenharmony_ci * fails with error but second attempt will have success. 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_ci return -EAGAIN; 16162306a36Sopenharmony_ci case HFSPLUS_VALID_ATTR_TREE: 16262306a36Sopenharmony_ci return 0; 16362306a36Sopenharmony_ci case HFSPLUS_FAILED_ATTR_TREE: 16462306a36Sopenharmony_ci return -EOPNOTSUPP; 16562306a36Sopenharmony_ci default: 16662306a36Sopenharmony_ci BUG(); 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci attr_file = hfsplus_iget(sb, HFSPLUS_ATTR_CNID); 17062306a36Sopenharmony_ci if (IS_ERR(attr_file)) { 17162306a36Sopenharmony_ci pr_err("failed to load attributes file\n"); 17262306a36Sopenharmony_ci return PTR_ERR(attr_file); 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci BUG_ON(i_size_read(attr_file) != 0); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci hip = HFSPLUS_I(attr_file); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci clump_size = hfsplus_calc_btree_clump_size(sb->s_blocksize, 18062306a36Sopenharmony_ci node_size, 18162306a36Sopenharmony_ci sbi->sect_count, 18262306a36Sopenharmony_ci HFSPLUS_ATTR_CNID); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci mutex_lock(&hip->extents_lock); 18562306a36Sopenharmony_ci hip->clump_blocks = clump_size >> sbi->alloc_blksz_shift; 18662306a36Sopenharmony_ci mutex_unlock(&hip->extents_lock); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (sbi->free_blocks <= (hip->clump_blocks << 1)) { 18962306a36Sopenharmony_ci err = -ENOSPC; 19062306a36Sopenharmony_ci goto end_attr_file_creation; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci while (hip->alloc_blocks < hip->clump_blocks) { 19462306a36Sopenharmony_ci err = hfsplus_file_extend(attr_file, false); 19562306a36Sopenharmony_ci if (unlikely(err)) { 19662306a36Sopenharmony_ci pr_err("failed to extend attributes file\n"); 19762306a36Sopenharmony_ci goto end_attr_file_creation; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci hip->phys_size = attr_file->i_size = 20062306a36Sopenharmony_ci (loff_t)hip->alloc_blocks << sbi->alloc_blksz_shift; 20162306a36Sopenharmony_ci hip->fs_blocks = hip->alloc_blocks << sbi->fs_shift; 20262306a36Sopenharmony_ci inode_set_bytes(attr_file, attr_file->i_size); 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci buf = kzalloc(node_size, GFP_NOFS); 20662306a36Sopenharmony_ci if (!buf) { 20762306a36Sopenharmony_ci err = -ENOMEM; 20862306a36Sopenharmony_ci goto end_attr_file_creation; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci hfsplus_init_header_node(attr_file, clump_size, buf, node_size); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci mapping = attr_file->i_mapping; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci index = 0; 21662306a36Sopenharmony_ci written = 0; 21762306a36Sopenharmony_ci for (; written < node_size; index++, written += PAGE_SIZE) { 21862306a36Sopenharmony_ci void *kaddr; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci page = read_mapping_page(mapping, index, NULL); 22162306a36Sopenharmony_ci if (IS_ERR(page)) { 22262306a36Sopenharmony_ci err = PTR_ERR(page); 22362306a36Sopenharmony_ci goto failed_header_node_init; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci kaddr = kmap_atomic(page); 22762306a36Sopenharmony_ci memcpy(kaddr, buf + written, 22862306a36Sopenharmony_ci min_t(size_t, PAGE_SIZE, node_size - written)); 22962306a36Sopenharmony_ci kunmap_atomic(kaddr); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci set_page_dirty(page); 23262306a36Sopenharmony_ci put_page(page); 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci hfsplus_mark_inode_dirty(attr_file, HFSPLUS_I_ATTR_DIRTY); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci sbi->attr_tree = hfs_btree_open(sb, HFSPLUS_ATTR_CNID); 23862306a36Sopenharmony_ci if (!sbi->attr_tree) 23962306a36Sopenharmony_ci pr_err("failed to load attributes file\n"); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cifailed_header_node_init: 24262306a36Sopenharmony_ci kfree(buf); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ciend_attr_file_creation: 24562306a36Sopenharmony_ci iput(attr_file); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (!err) 24862306a36Sopenharmony_ci atomic_set(&sbi->attr_tree_state, HFSPLUS_VALID_ATTR_TREE); 24962306a36Sopenharmony_ci else if (err == -ENOSPC) 25062306a36Sopenharmony_ci atomic_set(&sbi->attr_tree_state, HFSPLUS_EMPTY_ATTR_TREE); 25162306a36Sopenharmony_ci else 25262306a36Sopenharmony_ci atomic_set(&sbi->attr_tree_state, HFSPLUS_FAILED_ATTR_TREE); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci return err; 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ciint __hfsplus_setxattr(struct inode *inode, const char *name, 25862306a36Sopenharmony_ci const void *value, size_t size, int flags) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci int err; 26162306a36Sopenharmony_ci struct hfs_find_data cat_fd; 26262306a36Sopenharmony_ci hfsplus_cat_entry entry; 26362306a36Sopenharmony_ci u16 cat_entry_flags, cat_entry_type; 26462306a36Sopenharmony_ci u16 folder_finderinfo_len = sizeof(struct DInfo) + 26562306a36Sopenharmony_ci sizeof(struct DXInfo); 26662306a36Sopenharmony_ci u16 file_finderinfo_len = sizeof(struct FInfo) + 26762306a36Sopenharmony_ci sizeof(struct FXInfo); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if ((!S_ISREG(inode->i_mode) && 27062306a36Sopenharmony_ci !S_ISDIR(inode->i_mode)) || 27162306a36Sopenharmony_ci HFSPLUS_IS_RSRC(inode)) 27262306a36Sopenharmony_ci return -EOPNOTSUPP; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if (value == NULL) 27562306a36Sopenharmony_ci return hfsplus_removexattr(inode, name); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd); 27862306a36Sopenharmony_ci if (err) { 27962306a36Sopenharmony_ci pr_err("can't init xattr find struct\n"); 28062306a36Sopenharmony_ci return err; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd); 28462306a36Sopenharmony_ci if (err) { 28562306a36Sopenharmony_ci pr_err("catalog searching failed\n"); 28662306a36Sopenharmony_ci goto end_setxattr; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (!strcmp_xattr_finder_info(name)) { 29062306a36Sopenharmony_ci if (flags & XATTR_CREATE) { 29162306a36Sopenharmony_ci pr_err("xattr exists yet\n"); 29262306a36Sopenharmony_ci err = -EOPNOTSUPP; 29362306a36Sopenharmony_ci goto end_setxattr; 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci hfs_bnode_read(cat_fd.bnode, &entry, cat_fd.entryoffset, 29662306a36Sopenharmony_ci sizeof(hfsplus_cat_entry)); 29762306a36Sopenharmony_ci if (be16_to_cpu(entry.type) == HFSPLUS_FOLDER) { 29862306a36Sopenharmony_ci if (size == folder_finderinfo_len) { 29962306a36Sopenharmony_ci memcpy(&entry.folder.info, value, 30062306a36Sopenharmony_ci folder_finderinfo_len); 30162306a36Sopenharmony_ci hfs_bnode_write(cat_fd.bnode, &entry, 30262306a36Sopenharmony_ci cat_fd.entryoffset, 30362306a36Sopenharmony_ci sizeof(struct hfsplus_cat_folder)); 30462306a36Sopenharmony_ci hfsplus_mark_inode_dirty(inode, 30562306a36Sopenharmony_ci HFSPLUS_I_CAT_DIRTY); 30662306a36Sopenharmony_ci } else { 30762306a36Sopenharmony_ci err = -ERANGE; 30862306a36Sopenharmony_ci goto end_setxattr; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci } else if (be16_to_cpu(entry.type) == HFSPLUS_FILE) { 31162306a36Sopenharmony_ci if (size == file_finderinfo_len) { 31262306a36Sopenharmony_ci memcpy(&entry.file.info, value, 31362306a36Sopenharmony_ci file_finderinfo_len); 31462306a36Sopenharmony_ci hfs_bnode_write(cat_fd.bnode, &entry, 31562306a36Sopenharmony_ci cat_fd.entryoffset, 31662306a36Sopenharmony_ci sizeof(struct hfsplus_cat_file)); 31762306a36Sopenharmony_ci hfsplus_mark_inode_dirty(inode, 31862306a36Sopenharmony_ci HFSPLUS_I_CAT_DIRTY); 31962306a36Sopenharmony_ci } else { 32062306a36Sopenharmony_ci err = -ERANGE; 32162306a36Sopenharmony_ci goto end_setxattr; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci } else { 32462306a36Sopenharmony_ci err = -EOPNOTSUPP; 32562306a36Sopenharmony_ci goto end_setxattr; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci goto end_setxattr; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if (!HFSPLUS_SB(inode->i_sb)->attr_tree) { 33162306a36Sopenharmony_ci err = hfsplus_create_attributes_file(inode->i_sb); 33262306a36Sopenharmony_ci if (unlikely(err)) 33362306a36Sopenharmony_ci goto end_setxattr; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (hfsplus_attr_exists(inode, name)) { 33762306a36Sopenharmony_ci if (flags & XATTR_CREATE) { 33862306a36Sopenharmony_ci pr_err("xattr exists yet\n"); 33962306a36Sopenharmony_ci err = -EOPNOTSUPP; 34062306a36Sopenharmony_ci goto end_setxattr; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci err = hfsplus_delete_attr(inode, name); 34362306a36Sopenharmony_ci if (err) 34462306a36Sopenharmony_ci goto end_setxattr; 34562306a36Sopenharmony_ci err = hfsplus_create_attr(inode, name, value, size); 34662306a36Sopenharmony_ci if (err) 34762306a36Sopenharmony_ci goto end_setxattr; 34862306a36Sopenharmony_ci } else { 34962306a36Sopenharmony_ci if (flags & XATTR_REPLACE) { 35062306a36Sopenharmony_ci pr_err("cannot replace xattr\n"); 35162306a36Sopenharmony_ci err = -EOPNOTSUPP; 35262306a36Sopenharmony_ci goto end_setxattr; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci err = hfsplus_create_attr(inode, name, value, size); 35562306a36Sopenharmony_ci if (err) 35662306a36Sopenharmony_ci goto end_setxattr; 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset); 36062306a36Sopenharmony_ci if (cat_entry_type == HFSPLUS_FOLDER) { 36162306a36Sopenharmony_ci cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode, 36262306a36Sopenharmony_ci cat_fd.entryoffset + 36362306a36Sopenharmony_ci offsetof(struct hfsplus_cat_folder, flags)); 36462306a36Sopenharmony_ci cat_entry_flags |= HFSPLUS_XATTR_EXISTS; 36562306a36Sopenharmony_ci if (!strcmp_xattr_acl(name)) 36662306a36Sopenharmony_ci cat_entry_flags |= HFSPLUS_ACL_EXISTS; 36762306a36Sopenharmony_ci hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset + 36862306a36Sopenharmony_ci offsetof(struct hfsplus_cat_folder, flags), 36962306a36Sopenharmony_ci cat_entry_flags); 37062306a36Sopenharmony_ci hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY); 37162306a36Sopenharmony_ci } else if (cat_entry_type == HFSPLUS_FILE) { 37262306a36Sopenharmony_ci cat_entry_flags = hfs_bnode_read_u16(cat_fd.bnode, 37362306a36Sopenharmony_ci cat_fd.entryoffset + 37462306a36Sopenharmony_ci offsetof(struct hfsplus_cat_file, flags)); 37562306a36Sopenharmony_ci cat_entry_flags |= HFSPLUS_XATTR_EXISTS; 37662306a36Sopenharmony_ci if (!strcmp_xattr_acl(name)) 37762306a36Sopenharmony_ci cat_entry_flags |= HFSPLUS_ACL_EXISTS; 37862306a36Sopenharmony_ci hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset + 37962306a36Sopenharmony_ci offsetof(struct hfsplus_cat_file, flags), 38062306a36Sopenharmony_ci cat_entry_flags); 38162306a36Sopenharmony_ci hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY); 38262306a36Sopenharmony_ci } else { 38362306a36Sopenharmony_ci pr_err("invalid catalog entry type\n"); 38462306a36Sopenharmony_ci err = -EIO; 38562306a36Sopenharmony_ci goto end_setxattr; 38662306a36Sopenharmony_ci } 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ciend_setxattr: 38962306a36Sopenharmony_ci hfs_find_exit(&cat_fd); 39062306a36Sopenharmony_ci return err; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic int name_len(const char *xattr_name, int xattr_name_len) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci int len = xattr_name_len + 1; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (!is_known_namespace(xattr_name)) 39862306a36Sopenharmony_ci len += XATTR_MAC_OSX_PREFIX_LEN; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return len; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic int copy_name(char *buffer, const char *xattr_name, int name_len) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci int len = name_len; 40662306a36Sopenharmony_ci int offset = 0; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (!is_known_namespace(xattr_name)) { 40962306a36Sopenharmony_ci memcpy(buffer, XATTR_MAC_OSX_PREFIX, XATTR_MAC_OSX_PREFIX_LEN); 41062306a36Sopenharmony_ci offset += XATTR_MAC_OSX_PREFIX_LEN; 41162306a36Sopenharmony_ci len += XATTR_MAC_OSX_PREFIX_LEN; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci strncpy(buffer + offset, xattr_name, name_len); 41562306a36Sopenharmony_ci memset(buffer + offset + name_len, 0, 1); 41662306a36Sopenharmony_ci len += 1; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci return len; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ciint hfsplus_setxattr(struct inode *inode, const char *name, 42262306a36Sopenharmony_ci const void *value, size_t size, int flags, 42362306a36Sopenharmony_ci const char *prefix, size_t prefixlen) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci char *xattr_name; 42662306a36Sopenharmony_ci int res; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1, 42962306a36Sopenharmony_ci GFP_KERNEL); 43062306a36Sopenharmony_ci if (!xattr_name) 43162306a36Sopenharmony_ci return -ENOMEM; 43262306a36Sopenharmony_ci strcpy(xattr_name, prefix); 43362306a36Sopenharmony_ci strcpy(xattr_name + prefixlen, name); 43462306a36Sopenharmony_ci res = __hfsplus_setxattr(inode, xattr_name, value, size, flags); 43562306a36Sopenharmony_ci kfree(xattr_name); 43662306a36Sopenharmony_ci return res; 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic ssize_t hfsplus_getxattr_finder_info(struct inode *inode, 44062306a36Sopenharmony_ci void *value, size_t size) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci ssize_t res = 0; 44362306a36Sopenharmony_ci struct hfs_find_data fd; 44462306a36Sopenharmony_ci u16 entry_type; 44562306a36Sopenharmony_ci u16 folder_rec_len = sizeof(struct DInfo) + sizeof(struct DXInfo); 44662306a36Sopenharmony_ci u16 file_rec_len = sizeof(struct FInfo) + sizeof(struct FXInfo); 44762306a36Sopenharmony_ci u16 record_len = max(folder_rec_len, file_rec_len); 44862306a36Sopenharmony_ci u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)]; 44962306a36Sopenharmony_ci u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)]; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (size >= record_len) { 45262306a36Sopenharmony_ci res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd); 45362306a36Sopenharmony_ci if (res) { 45462306a36Sopenharmony_ci pr_err("can't init xattr find struct\n"); 45562306a36Sopenharmony_ci return res; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd); 45862306a36Sopenharmony_ci if (res) 45962306a36Sopenharmony_ci goto end_getxattr_finder_info; 46062306a36Sopenharmony_ci entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (entry_type == HFSPLUS_FOLDER) { 46362306a36Sopenharmony_ci hfs_bnode_read(fd.bnode, folder_finder_info, 46462306a36Sopenharmony_ci fd.entryoffset + 46562306a36Sopenharmony_ci offsetof(struct hfsplus_cat_folder, user_info), 46662306a36Sopenharmony_ci folder_rec_len); 46762306a36Sopenharmony_ci memcpy(value, folder_finder_info, folder_rec_len); 46862306a36Sopenharmony_ci res = folder_rec_len; 46962306a36Sopenharmony_ci } else if (entry_type == HFSPLUS_FILE) { 47062306a36Sopenharmony_ci hfs_bnode_read(fd.bnode, file_finder_info, 47162306a36Sopenharmony_ci fd.entryoffset + 47262306a36Sopenharmony_ci offsetof(struct hfsplus_cat_file, user_info), 47362306a36Sopenharmony_ci file_rec_len); 47462306a36Sopenharmony_ci memcpy(value, file_finder_info, file_rec_len); 47562306a36Sopenharmony_ci res = file_rec_len; 47662306a36Sopenharmony_ci } else { 47762306a36Sopenharmony_ci res = -EOPNOTSUPP; 47862306a36Sopenharmony_ci goto end_getxattr_finder_info; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci } else 48162306a36Sopenharmony_ci res = size ? -ERANGE : record_len; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ciend_getxattr_finder_info: 48462306a36Sopenharmony_ci if (size >= record_len) 48562306a36Sopenharmony_ci hfs_find_exit(&fd); 48662306a36Sopenharmony_ci return res; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cissize_t __hfsplus_getxattr(struct inode *inode, const char *name, 49062306a36Sopenharmony_ci void *value, size_t size) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct hfs_find_data fd; 49362306a36Sopenharmony_ci hfsplus_attr_entry *entry; 49462306a36Sopenharmony_ci __be32 xattr_record_type; 49562306a36Sopenharmony_ci u32 record_type; 49662306a36Sopenharmony_ci u16 record_length = 0; 49762306a36Sopenharmony_ci ssize_t res; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if ((!S_ISREG(inode->i_mode) && 50062306a36Sopenharmony_ci !S_ISDIR(inode->i_mode)) || 50162306a36Sopenharmony_ci HFSPLUS_IS_RSRC(inode)) 50262306a36Sopenharmony_ci return -EOPNOTSUPP; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (!strcmp_xattr_finder_info(name)) 50562306a36Sopenharmony_ci return hfsplus_getxattr_finder_info(inode, value, size); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (!HFSPLUS_SB(inode->i_sb)->attr_tree) 50862306a36Sopenharmony_ci return -EOPNOTSUPP; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci entry = hfsplus_alloc_attr_entry(); 51162306a36Sopenharmony_ci if (!entry) { 51262306a36Sopenharmony_ci pr_err("can't allocate xattr entry\n"); 51362306a36Sopenharmony_ci return -ENOMEM; 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd); 51762306a36Sopenharmony_ci if (res) { 51862306a36Sopenharmony_ci pr_err("can't init xattr find struct\n"); 51962306a36Sopenharmony_ci goto failed_getxattr_init; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci res = hfsplus_find_attr(inode->i_sb, inode->i_ino, name, &fd); 52362306a36Sopenharmony_ci if (res) { 52462306a36Sopenharmony_ci if (res == -ENOENT) 52562306a36Sopenharmony_ci res = -ENODATA; 52662306a36Sopenharmony_ci else 52762306a36Sopenharmony_ci pr_err("xattr searching failed\n"); 52862306a36Sopenharmony_ci goto out; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci hfs_bnode_read(fd.bnode, &xattr_record_type, 53262306a36Sopenharmony_ci fd.entryoffset, sizeof(xattr_record_type)); 53362306a36Sopenharmony_ci record_type = be32_to_cpu(xattr_record_type); 53462306a36Sopenharmony_ci if (record_type == HFSPLUS_ATTR_INLINE_DATA) { 53562306a36Sopenharmony_ci record_length = hfs_bnode_read_u16(fd.bnode, 53662306a36Sopenharmony_ci fd.entryoffset + 53762306a36Sopenharmony_ci offsetof(struct hfsplus_attr_inline_data, 53862306a36Sopenharmony_ci length)); 53962306a36Sopenharmony_ci if (record_length > HFSPLUS_MAX_INLINE_DATA_SIZE) { 54062306a36Sopenharmony_ci pr_err("invalid xattr record size\n"); 54162306a36Sopenharmony_ci res = -EIO; 54262306a36Sopenharmony_ci goto out; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci } else if (record_type == HFSPLUS_ATTR_FORK_DATA || 54562306a36Sopenharmony_ci record_type == HFSPLUS_ATTR_EXTENTS) { 54662306a36Sopenharmony_ci pr_err("only inline data xattr are supported\n"); 54762306a36Sopenharmony_ci res = -EOPNOTSUPP; 54862306a36Sopenharmony_ci goto out; 54962306a36Sopenharmony_ci } else { 55062306a36Sopenharmony_ci pr_err("invalid xattr record\n"); 55162306a36Sopenharmony_ci res = -EIO; 55262306a36Sopenharmony_ci goto out; 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (size) { 55662306a36Sopenharmony_ci hfs_bnode_read(fd.bnode, entry, fd.entryoffset, 55762306a36Sopenharmony_ci offsetof(struct hfsplus_attr_inline_data, 55862306a36Sopenharmony_ci raw_bytes) + record_length); 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if (size >= record_length) { 56262306a36Sopenharmony_ci memcpy(value, entry->inline_data.raw_bytes, record_length); 56362306a36Sopenharmony_ci res = record_length; 56462306a36Sopenharmony_ci } else 56562306a36Sopenharmony_ci res = size ? -ERANGE : record_length; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ciout: 56862306a36Sopenharmony_ci hfs_find_exit(&fd); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_cifailed_getxattr_init: 57162306a36Sopenharmony_ci hfsplus_destroy_attr_entry(entry); 57262306a36Sopenharmony_ci return res; 57362306a36Sopenharmony_ci} 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_cissize_t hfsplus_getxattr(struct inode *inode, const char *name, 57662306a36Sopenharmony_ci void *value, size_t size, 57762306a36Sopenharmony_ci const char *prefix, size_t prefixlen) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci int res; 58062306a36Sopenharmony_ci char *xattr_name; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci xattr_name = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 1, 58362306a36Sopenharmony_ci GFP_KERNEL); 58462306a36Sopenharmony_ci if (!xattr_name) 58562306a36Sopenharmony_ci return -ENOMEM; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci strcpy(xattr_name, prefix); 58862306a36Sopenharmony_ci strcpy(xattr_name + prefixlen, name); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci res = __hfsplus_getxattr(inode, xattr_name, value, size); 59162306a36Sopenharmony_ci kfree(xattr_name); 59262306a36Sopenharmony_ci return res; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic inline int can_list(const char *xattr_name) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci if (!xattr_name) 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci return strncmp(xattr_name, XATTR_TRUSTED_PREFIX, 60262306a36Sopenharmony_ci XATTR_TRUSTED_PREFIX_LEN) || 60362306a36Sopenharmony_ci capable(CAP_SYS_ADMIN); 60462306a36Sopenharmony_ci} 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_cistatic ssize_t hfsplus_listxattr_finder_info(struct dentry *dentry, 60762306a36Sopenharmony_ci char *buffer, size_t size) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci ssize_t res; 61062306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 61162306a36Sopenharmony_ci struct hfs_find_data fd; 61262306a36Sopenharmony_ci u16 entry_type; 61362306a36Sopenharmony_ci u8 folder_finder_info[sizeof(struct DInfo) + sizeof(struct DXInfo)]; 61462306a36Sopenharmony_ci u8 file_finder_info[sizeof(struct FInfo) + sizeof(struct FXInfo)]; 61562306a36Sopenharmony_ci unsigned long len, found_bit; 61662306a36Sopenharmony_ci int xattr_name_len, symbols_count; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci res = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &fd); 61962306a36Sopenharmony_ci if (res) { 62062306a36Sopenharmony_ci pr_err("can't init xattr find struct\n"); 62162306a36Sopenharmony_ci return res; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd); 62562306a36Sopenharmony_ci if (res) 62662306a36Sopenharmony_ci goto end_listxattr_finder_info; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci entry_type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset); 62962306a36Sopenharmony_ci if (entry_type == HFSPLUS_FOLDER) { 63062306a36Sopenharmony_ci len = sizeof(struct DInfo) + sizeof(struct DXInfo); 63162306a36Sopenharmony_ci hfs_bnode_read(fd.bnode, folder_finder_info, 63262306a36Sopenharmony_ci fd.entryoffset + 63362306a36Sopenharmony_ci offsetof(struct hfsplus_cat_folder, user_info), 63462306a36Sopenharmony_ci len); 63562306a36Sopenharmony_ci found_bit = find_first_bit((void *)folder_finder_info, len*8); 63662306a36Sopenharmony_ci } else if (entry_type == HFSPLUS_FILE) { 63762306a36Sopenharmony_ci len = sizeof(struct FInfo) + sizeof(struct FXInfo); 63862306a36Sopenharmony_ci hfs_bnode_read(fd.bnode, file_finder_info, 63962306a36Sopenharmony_ci fd.entryoffset + 64062306a36Sopenharmony_ci offsetof(struct hfsplus_cat_file, user_info), 64162306a36Sopenharmony_ci len); 64262306a36Sopenharmony_ci found_bit = find_first_bit((void *)file_finder_info, len*8); 64362306a36Sopenharmony_ci } else { 64462306a36Sopenharmony_ci res = -EOPNOTSUPP; 64562306a36Sopenharmony_ci goto end_listxattr_finder_info; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (found_bit >= (len*8)) 64962306a36Sopenharmony_ci res = 0; 65062306a36Sopenharmony_ci else { 65162306a36Sopenharmony_ci symbols_count = sizeof(HFSPLUS_XATTR_FINDER_INFO_NAME) - 1; 65262306a36Sopenharmony_ci xattr_name_len = 65362306a36Sopenharmony_ci name_len(HFSPLUS_XATTR_FINDER_INFO_NAME, symbols_count); 65462306a36Sopenharmony_ci if (!buffer || !size) { 65562306a36Sopenharmony_ci if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME)) 65662306a36Sopenharmony_ci res = xattr_name_len; 65762306a36Sopenharmony_ci } else if (can_list(HFSPLUS_XATTR_FINDER_INFO_NAME)) { 65862306a36Sopenharmony_ci if (size < xattr_name_len) 65962306a36Sopenharmony_ci res = -ERANGE; 66062306a36Sopenharmony_ci else { 66162306a36Sopenharmony_ci res = copy_name(buffer, 66262306a36Sopenharmony_ci HFSPLUS_XATTR_FINDER_INFO_NAME, 66362306a36Sopenharmony_ci symbols_count); 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ciend_listxattr_finder_info: 66962306a36Sopenharmony_ci hfs_find_exit(&fd); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci return res; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cissize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size) 67562306a36Sopenharmony_ci{ 67662306a36Sopenharmony_ci ssize_t err; 67762306a36Sopenharmony_ci ssize_t res; 67862306a36Sopenharmony_ci struct inode *inode = d_inode(dentry); 67962306a36Sopenharmony_ci struct hfs_find_data fd; 68062306a36Sopenharmony_ci struct hfsplus_attr_key attr_key; 68162306a36Sopenharmony_ci char *strbuf; 68262306a36Sopenharmony_ci int xattr_name_len; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if ((!S_ISREG(inode->i_mode) && 68562306a36Sopenharmony_ci !S_ISDIR(inode->i_mode)) || 68662306a36Sopenharmony_ci HFSPLUS_IS_RSRC(inode)) 68762306a36Sopenharmony_ci return -EOPNOTSUPP; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci res = hfsplus_listxattr_finder_info(dentry, buffer, size); 69062306a36Sopenharmony_ci if (res < 0) 69162306a36Sopenharmony_ci return res; 69262306a36Sopenharmony_ci else if (!HFSPLUS_SB(inode->i_sb)->attr_tree) 69362306a36Sopenharmony_ci return (res == 0) ? -EOPNOTSUPP : res; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->attr_tree, &fd); 69662306a36Sopenharmony_ci if (err) { 69762306a36Sopenharmony_ci pr_err("can't init xattr find struct\n"); 69862306a36Sopenharmony_ci return err; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN + 70262306a36Sopenharmony_ci XATTR_MAC_OSX_PREFIX_LEN + 1, GFP_KERNEL); 70362306a36Sopenharmony_ci if (!strbuf) { 70462306a36Sopenharmony_ci res = -ENOMEM; 70562306a36Sopenharmony_ci goto out; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci err = hfsplus_find_attr(inode->i_sb, inode->i_ino, NULL, &fd); 70962306a36Sopenharmony_ci if (err) { 71062306a36Sopenharmony_ci if (err == -ENOENT) { 71162306a36Sopenharmony_ci if (res == 0) 71262306a36Sopenharmony_ci res = -ENODATA; 71362306a36Sopenharmony_ci goto end_listxattr; 71462306a36Sopenharmony_ci } else { 71562306a36Sopenharmony_ci res = err; 71662306a36Sopenharmony_ci goto end_listxattr; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci for (;;) { 72162306a36Sopenharmony_ci u16 key_len = hfs_bnode_read_u16(fd.bnode, fd.keyoffset); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (key_len == 0 || key_len > fd.tree->max_key_len) { 72462306a36Sopenharmony_ci pr_err("invalid xattr key length: %d\n", key_len); 72562306a36Sopenharmony_ci res = -EIO; 72662306a36Sopenharmony_ci goto end_listxattr; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci hfs_bnode_read(fd.bnode, &attr_key, 73062306a36Sopenharmony_ci fd.keyoffset, key_len + sizeof(key_len)); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (be32_to_cpu(attr_key.cnid) != inode->i_ino) 73362306a36Sopenharmony_ci goto end_listxattr; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci xattr_name_len = NLS_MAX_CHARSET_SIZE * HFSPLUS_ATTR_MAX_STRLEN; 73662306a36Sopenharmony_ci if (hfsplus_uni2asc(inode->i_sb, 73762306a36Sopenharmony_ci (const struct hfsplus_unistr *)&fd.key->attr.key_name, 73862306a36Sopenharmony_ci strbuf, &xattr_name_len)) { 73962306a36Sopenharmony_ci pr_err("unicode conversion failed\n"); 74062306a36Sopenharmony_ci res = -EIO; 74162306a36Sopenharmony_ci goto end_listxattr; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci if (!buffer || !size) { 74562306a36Sopenharmony_ci if (can_list(strbuf)) 74662306a36Sopenharmony_ci res += name_len(strbuf, xattr_name_len); 74762306a36Sopenharmony_ci } else if (can_list(strbuf)) { 74862306a36Sopenharmony_ci if (size < (res + name_len(strbuf, xattr_name_len))) { 74962306a36Sopenharmony_ci res = -ERANGE; 75062306a36Sopenharmony_ci goto end_listxattr; 75162306a36Sopenharmony_ci } else 75262306a36Sopenharmony_ci res += copy_name(buffer + res, 75362306a36Sopenharmony_ci strbuf, xattr_name_len); 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci if (hfs_brec_goto(&fd, 1)) 75762306a36Sopenharmony_ci goto end_listxattr; 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ciend_listxattr: 76162306a36Sopenharmony_ci kfree(strbuf); 76262306a36Sopenharmony_ciout: 76362306a36Sopenharmony_ci hfs_find_exit(&fd); 76462306a36Sopenharmony_ci return res; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_cistatic int hfsplus_removexattr(struct inode *inode, const char *name) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci int err; 77062306a36Sopenharmony_ci struct hfs_find_data cat_fd; 77162306a36Sopenharmony_ci u16 flags; 77262306a36Sopenharmony_ci u16 cat_entry_type; 77362306a36Sopenharmony_ci int is_xattr_acl_deleted; 77462306a36Sopenharmony_ci int is_all_xattrs_deleted; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci if (!HFSPLUS_SB(inode->i_sb)->attr_tree) 77762306a36Sopenharmony_ci return -EOPNOTSUPP; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci if (!strcmp_xattr_finder_info(name)) 78062306a36Sopenharmony_ci return -EOPNOTSUPP; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci err = hfs_find_init(HFSPLUS_SB(inode->i_sb)->cat_tree, &cat_fd); 78362306a36Sopenharmony_ci if (err) { 78462306a36Sopenharmony_ci pr_err("can't init xattr find struct\n"); 78562306a36Sopenharmony_ci return err; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &cat_fd); 78962306a36Sopenharmony_ci if (err) { 79062306a36Sopenharmony_ci pr_err("catalog searching failed\n"); 79162306a36Sopenharmony_ci goto end_removexattr; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci err = hfsplus_delete_attr(inode, name); 79562306a36Sopenharmony_ci if (err) 79662306a36Sopenharmony_ci goto end_removexattr; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci is_xattr_acl_deleted = !strcmp_xattr_acl(name); 79962306a36Sopenharmony_ci is_all_xattrs_deleted = !hfsplus_attr_exists(inode, NULL); 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci if (!is_xattr_acl_deleted && !is_all_xattrs_deleted) 80262306a36Sopenharmony_ci goto end_removexattr; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci cat_entry_type = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci if (cat_entry_type == HFSPLUS_FOLDER) { 80762306a36Sopenharmony_ci flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset + 80862306a36Sopenharmony_ci offsetof(struct hfsplus_cat_folder, flags)); 80962306a36Sopenharmony_ci if (is_xattr_acl_deleted) 81062306a36Sopenharmony_ci flags &= ~HFSPLUS_ACL_EXISTS; 81162306a36Sopenharmony_ci if (is_all_xattrs_deleted) 81262306a36Sopenharmony_ci flags &= ~HFSPLUS_XATTR_EXISTS; 81362306a36Sopenharmony_ci hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset + 81462306a36Sopenharmony_ci offsetof(struct hfsplus_cat_folder, flags), 81562306a36Sopenharmony_ci flags); 81662306a36Sopenharmony_ci hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY); 81762306a36Sopenharmony_ci } else if (cat_entry_type == HFSPLUS_FILE) { 81862306a36Sopenharmony_ci flags = hfs_bnode_read_u16(cat_fd.bnode, cat_fd.entryoffset + 81962306a36Sopenharmony_ci offsetof(struct hfsplus_cat_file, flags)); 82062306a36Sopenharmony_ci if (is_xattr_acl_deleted) 82162306a36Sopenharmony_ci flags &= ~HFSPLUS_ACL_EXISTS; 82262306a36Sopenharmony_ci if (is_all_xattrs_deleted) 82362306a36Sopenharmony_ci flags &= ~HFSPLUS_XATTR_EXISTS; 82462306a36Sopenharmony_ci hfs_bnode_write_u16(cat_fd.bnode, cat_fd.entryoffset + 82562306a36Sopenharmony_ci offsetof(struct hfsplus_cat_file, flags), 82662306a36Sopenharmony_ci flags); 82762306a36Sopenharmony_ci hfsplus_mark_inode_dirty(inode, HFSPLUS_I_CAT_DIRTY); 82862306a36Sopenharmony_ci } else { 82962306a36Sopenharmony_ci pr_err("invalid catalog entry type\n"); 83062306a36Sopenharmony_ci err = -EIO; 83162306a36Sopenharmony_ci goto end_removexattr; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ciend_removexattr: 83562306a36Sopenharmony_ci hfs_find_exit(&cat_fd); 83662306a36Sopenharmony_ci return err; 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_cistatic int hfsplus_osx_getxattr(const struct xattr_handler *handler, 84062306a36Sopenharmony_ci struct dentry *unused, struct inode *inode, 84162306a36Sopenharmony_ci const char *name, void *buffer, size_t size) 84262306a36Sopenharmony_ci{ 84362306a36Sopenharmony_ci /* 84462306a36Sopenharmony_ci * Don't allow retrieving properly prefixed attributes 84562306a36Sopenharmony_ci * by prepending them with "osx." 84662306a36Sopenharmony_ci */ 84762306a36Sopenharmony_ci if (is_known_namespace(name)) 84862306a36Sopenharmony_ci return -EOPNOTSUPP; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci /* 85162306a36Sopenharmony_ci * osx is the namespace we use to indicate an unprefixed 85262306a36Sopenharmony_ci * attribute on the filesystem (like the ones that OS X 85362306a36Sopenharmony_ci * creates), so we pass the name through unmodified (after 85462306a36Sopenharmony_ci * ensuring it doesn't conflict with another namespace). 85562306a36Sopenharmony_ci */ 85662306a36Sopenharmony_ci return __hfsplus_getxattr(inode, name, buffer, size); 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_cistatic int hfsplus_osx_setxattr(const struct xattr_handler *handler, 86062306a36Sopenharmony_ci struct mnt_idmap *idmap, 86162306a36Sopenharmony_ci struct dentry *unused, struct inode *inode, 86262306a36Sopenharmony_ci const char *name, const void *buffer, 86362306a36Sopenharmony_ci size_t size, int flags) 86462306a36Sopenharmony_ci{ 86562306a36Sopenharmony_ci /* 86662306a36Sopenharmony_ci * Don't allow setting properly prefixed attributes 86762306a36Sopenharmony_ci * by prepending them with "osx." 86862306a36Sopenharmony_ci */ 86962306a36Sopenharmony_ci if (is_known_namespace(name)) 87062306a36Sopenharmony_ci return -EOPNOTSUPP; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* 87362306a36Sopenharmony_ci * osx is the namespace we use to indicate an unprefixed 87462306a36Sopenharmony_ci * attribute on the filesystem (like the ones that OS X 87562306a36Sopenharmony_ci * creates), so we pass the name through unmodified (after 87662306a36Sopenharmony_ci * ensuring it doesn't conflict with another namespace). 87762306a36Sopenharmony_ci */ 87862306a36Sopenharmony_ci return __hfsplus_setxattr(inode, name, buffer, size, flags); 87962306a36Sopenharmony_ci} 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ciconst struct xattr_handler hfsplus_xattr_osx_handler = { 88262306a36Sopenharmony_ci .prefix = XATTR_MAC_OSX_PREFIX, 88362306a36Sopenharmony_ci .get = hfsplus_osx_getxattr, 88462306a36Sopenharmony_ci .set = hfsplus_osx_setxattr, 88562306a36Sopenharmony_ci}; 886