18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/fs/hfsplus/bfind.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2001 68c2ecf20Sopenharmony_ci * Brad Boyer (flar@allandria.com) 78c2ecf20Sopenharmony_ci * (C) 2003 Ardis Technologies <roman@ardistech.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Search routines for btrees 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include "hfsplus_fs.h" 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ciint hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd) 168c2ecf20Sopenharmony_ci{ 178c2ecf20Sopenharmony_ci void *ptr; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci fd->tree = tree; 208c2ecf20Sopenharmony_ci fd->bnode = NULL; 218c2ecf20Sopenharmony_ci ptr = kmalloc(tree->max_key_len * 2 + 4, GFP_KERNEL); 228c2ecf20Sopenharmony_ci if (!ptr) 238c2ecf20Sopenharmony_ci return -ENOMEM; 248c2ecf20Sopenharmony_ci fd->search_key = ptr; 258c2ecf20Sopenharmony_ci fd->key = ptr + tree->max_key_len + 2; 268c2ecf20Sopenharmony_ci hfs_dbg(BNODE_REFS, "find_init: %d (%p)\n", 278c2ecf20Sopenharmony_ci tree->cnid, __builtin_return_address(0)); 288c2ecf20Sopenharmony_ci switch (tree->cnid) { 298c2ecf20Sopenharmony_ci case HFSPLUS_CAT_CNID: 308c2ecf20Sopenharmony_ci mutex_lock_nested(&tree->tree_lock, CATALOG_BTREE_MUTEX); 318c2ecf20Sopenharmony_ci break; 328c2ecf20Sopenharmony_ci case HFSPLUS_EXT_CNID: 338c2ecf20Sopenharmony_ci mutex_lock_nested(&tree->tree_lock, EXTENTS_BTREE_MUTEX); 348c2ecf20Sopenharmony_ci break; 358c2ecf20Sopenharmony_ci case HFSPLUS_ATTR_CNID: 368c2ecf20Sopenharmony_ci mutex_lock_nested(&tree->tree_lock, ATTR_BTREE_MUTEX); 378c2ecf20Sopenharmony_ci break; 388c2ecf20Sopenharmony_ci default: 398c2ecf20Sopenharmony_ci BUG(); 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci return 0; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_civoid hfs_find_exit(struct hfs_find_data *fd) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci hfs_bnode_put(fd->bnode); 478c2ecf20Sopenharmony_ci kfree(fd->search_key); 488c2ecf20Sopenharmony_ci hfs_dbg(BNODE_REFS, "find_exit: %d (%p)\n", 498c2ecf20Sopenharmony_ci fd->tree->cnid, __builtin_return_address(0)); 508c2ecf20Sopenharmony_ci mutex_unlock(&fd->tree->tree_lock); 518c2ecf20Sopenharmony_ci fd->tree = NULL; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ciint hfs_find_1st_rec_by_cnid(struct hfs_bnode *bnode, 558c2ecf20Sopenharmony_ci struct hfs_find_data *fd, 568c2ecf20Sopenharmony_ci int *begin, 578c2ecf20Sopenharmony_ci int *end, 588c2ecf20Sopenharmony_ci int *cur_rec) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci __be32 cur_cnid; 618c2ecf20Sopenharmony_ci __be32 search_cnid; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (bnode->tree->cnid == HFSPLUS_EXT_CNID) { 648c2ecf20Sopenharmony_ci cur_cnid = fd->key->ext.cnid; 658c2ecf20Sopenharmony_ci search_cnid = fd->search_key->ext.cnid; 668c2ecf20Sopenharmony_ci } else if (bnode->tree->cnid == HFSPLUS_CAT_CNID) { 678c2ecf20Sopenharmony_ci cur_cnid = fd->key->cat.parent; 688c2ecf20Sopenharmony_ci search_cnid = fd->search_key->cat.parent; 698c2ecf20Sopenharmony_ci } else if (bnode->tree->cnid == HFSPLUS_ATTR_CNID) { 708c2ecf20Sopenharmony_ci cur_cnid = fd->key->attr.cnid; 718c2ecf20Sopenharmony_ci search_cnid = fd->search_key->attr.cnid; 728c2ecf20Sopenharmony_ci } else { 738c2ecf20Sopenharmony_ci cur_cnid = 0; /* used-uninitialized warning */ 748c2ecf20Sopenharmony_ci search_cnid = 0; 758c2ecf20Sopenharmony_ci BUG(); 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (cur_cnid == search_cnid) { 798c2ecf20Sopenharmony_ci (*end) = (*cur_rec); 808c2ecf20Sopenharmony_ci if ((*begin) == (*end)) 818c2ecf20Sopenharmony_ci return 1; 828c2ecf20Sopenharmony_ci } else { 838c2ecf20Sopenharmony_ci if (be32_to_cpu(cur_cnid) < be32_to_cpu(search_cnid)) 848c2ecf20Sopenharmony_ci (*begin) = (*cur_rec) + 1; 858c2ecf20Sopenharmony_ci else 868c2ecf20Sopenharmony_ci (*end) = (*cur_rec) - 1; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return 0; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ciint hfs_find_rec_by_key(struct hfs_bnode *bnode, 938c2ecf20Sopenharmony_ci struct hfs_find_data *fd, 948c2ecf20Sopenharmony_ci int *begin, 958c2ecf20Sopenharmony_ci int *end, 968c2ecf20Sopenharmony_ci int *cur_rec) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci int cmpval; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci cmpval = bnode->tree->keycmp(fd->key, fd->search_key); 1018c2ecf20Sopenharmony_ci if (!cmpval) { 1028c2ecf20Sopenharmony_ci (*end) = (*cur_rec); 1038c2ecf20Sopenharmony_ci return 1; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci if (cmpval < 0) 1068c2ecf20Sopenharmony_ci (*begin) = (*cur_rec) + 1; 1078c2ecf20Sopenharmony_ci else 1088c2ecf20Sopenharmony_ci *(end) = (*cur_rec) - 1; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* Find the record in bnode that best matches key (not greater than...)*/ 1148c2ecf20Sopenharmony_ciint __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd, 1158c2ecf20Sopenharmony_ci search_strategy_t rec_found) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci u16 off, len, keylen; 1188c2ecf20Sopenharmony_ci int rec; 1198c2ecf20Sopenharmony_ci int b, e; 1208c2ecf20Sopenharmony_ci int res; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci BUG_ON(!rec_found); 1238c2ecf20Sopenharmony_ci b = 0; 1248c2ecf20Sopenharmony_ci e = bnode->num_recs - 1; 1258c2ecf20Sopenharmony_ci res = -ENOENT; 1268c2ecf20Sopenharmony_ci do { 1278c2ecf20Sopenharmony_ci rec = (e + b) / 2; 1288c2ecf20Sopenharmony_ci len = hfs_brec_lenoff(bnode, rec, &off); 1298c2ecf20Sopenharmony_ci keylen = hfs_brec_keylen(bnode, rec); 1308c2ecf20Sopenharmony_ci if (keylen == 0) { 1318c2ecf20Sopenharmony_ci res = -EINVAL; 1328c2ecf20Sopenharmony_ci goto fail; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci hfs_bnode_read(bnode, fd->key, off, keylen); 1358c2ecf20Sopenharmony_ci if (rec_found(bnode, fd, &b, &e, &rec)) { 1368c2ecf20Sopenharmony_ci res = 0; 1378c2ecf20Sopenharmony_ci goto done; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci } while (b <= e); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (rec != e && e >= 0) { 1428c2ecf20Sopenharmony_ci len = hfs_brec_lenoff(bnode, e, &off); 1438c2ecf20Sopenharmony_ci keylen = hfs_brec_keylen(bnode, e); 1448c2ecf20Sopenharmony_ci if (keylen == 0) { 1458c2ecf20Sopenharmony_ci res = -EINVAL; 1468c2ecf20Sopenharmony_ci goto fail; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci hfs_bnode_read(bnode, fd->key, off, keylen); 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cidone: 1528c2ecf20Sopenharmony_ci fd->record = e; 1538c2ecf20Sopenharmony_ci fd->keyoffset = off; 1548c2ecf20Sopenharmony_ci fd->keylength = keylen; 1558c2ecf20Sopenharmony_ci fd->entryoffset = off + keylen; 1568c2ecf20Sopenharmony_ci fd->entrylength = len - keylen; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cifail: 1598c2ecf20Sopenharmony_ci return res; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci/* Traverse a B*Tree from the root to a leaf finding best fit to key */ 1638c2ecf20Sopenharmony_ci/* Return allocated copy of node found, set recnum to best record */ 1648c2ecf20Sopenharmony_ciint hfs_brec_find(struct hfs_find_data *fd, search_strategy_t do_key_compare) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct hfs_btree *tree; 1678c2ecf20Sopenharmony_ci struct hfs_bnode *bnode; 1688c2ecf20Sopenharmony_ci u32 nidx, parent; 1698c2ecf20Sopenharmony_ci __be32 data; 1708c2ecf20Sopenharmony_ci int height, res; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci tree = fd->tree; 1738c2ecf20Sopenharmony_ci if (fd->bnode) 1748c2ecf20Sopenharmony_ci hfs_bnode_put(fd->bnode); 1758c2ecf20Sopenharmony_ci fd->bnode = NULL; 1768c2ecf20Sopenharmony_ci nidx = tree->root; 1778c2ecf20Sopenharmony_ci if (!nidx) 1788c2ecf20Sopenharmony_ci return -ENOENT; 1798c2ecf20Sopenharmony_ci height = tree->depth; 1808c2ecf20Sopenharmony_ci res = 0; 1818c2ecf20Sopenharmony_ci parent = 0; 1828c2ecf20Sopenharmony_ci for (;;) { 1838c2ecf20Sopenharmony_ci bnode = hfs_bnode_find(tree, nidx); 1848c2ecf20Sopenharmony_ci if (IS_ERR(bnode)) { 1858c2ecf20Sopenharmony_ci res = PTR_ERR(bnode); 1868c2ecf20Sopenharmony_ci bnode = NULL; 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci if (bnode->height != height) 1908c2ecf20Sopenharmony_ci goto invalid; 1918c2ecf20Sopenharmony_ci if (bnode->type != (--height ? HFS_NODE_INDEX : HFS_NODE_LEAF)) 1928c2ecf20Sopenharmony_ci goto invalid; 1938c2ecf20Sopenharmony_ci bnode->parent = parent; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci res = __hfs_brec_find(bnode, fd, do_key_compare); 1968c2ecf20Sopenharmony_ci if (!height) 1978c2ecf20Sopenharmony_ci break; 1988c2ecf20Sopenharmony_ci if (fd->record < 0) 1998c2ecf20Sopenharmony_ci goto release; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci parent = nidx; 2028c2ecf20Sopenharmony_ci hfs_bnode_read(bnode, &data, fd->entryoffset, 4); 2038c2ecf20Sopenharmony_ci nidx = be32_to_cpu(data); 2048c2ecf20Sopenharmony_ci hfs_bnode_put(bnode); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci fd->bnode = bnode; 2078c2ecf20Sopenharmony_ci return res; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ciinvalid: 2108c2ecf20Sopenharmony_ci pr_err("inconsistency in B*Tree (%d,%d,%d,%u,%u)\n", 2118c2ecf20Sopenharmony_ci height, bnode->height, bnode->type, nidx, parent); 2128c2ecf20Sopenharmony_ci res = -EIO; 2138c2ecf20Sopenharmony_cirelease: 2148c2ecf20Sopenharmony_ci hfs_bnode_put(bnode); 2158c2ecf20Sopenharmony_ci return res; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ciint hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci int res; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci res = hfs_brec_find(fd, hfs_find_rec_by_key); 2238c2ecf20Sopenharmony_ci if (res) 2248c2ecf20Sopenharmony_ci return res; 2258c2ecf20Sopenharmony_ci if (fd->entrylength > rec_len) 2268c2ecf20Sopenharmony_ci return -EINVAL; 2278c2ecf20Sopenharmony_ci hfs_bnode_read(fd->bnode, rec, fd->entryoffset, fd->entrylength); 2288c2ecf20Sopenharmony_ci return 0; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ciint hfs_brec_goto(struct hfs_find_data *fd, int cnt) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct hfs_btree *tree; 2348c2ecf20Sopenharmony_ci struct hfs_bnode *bnode; 2358c2ecf20Sopenharmony_ci int idx, res = 0; 2368c2ecf20Sopenharmony_ci u16 off, len, keylen; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci bnode = fd->bnode; 2398c2ecf20Sopenharmony_ci tree = bnode->tree; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (cnt < 0) { 2428c2ecf20Sopenharmony_ci cnt = -cnt; 2438c2ecf20Sopenharmony_ci while (cnt > fd->record) { 2448c2ecf20Sopenharmony_ci cnt -= fd->record + 1; 2458c2ecf20Sopenharmony_ci fd->record = bnode->num_recs - 1; 2468c2ecf20Sopenharmony_ci idx = bnode->prev; 2478c2ecf20Sopenharmony_ci if (!idx) { 2488c2ecf20Sopenharmony_ci res = -ENOENT; 2498c2ecf20Sopenharmony_ci goto out; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci hfs_bnode_put(bnode); 2528c2ecf20Sopenharmony_ci bnode = hfs_bnode_find(tree, idx); 2538c2ecf20Sopenharmony_ci if (IS_ERR(bnode)) { 2548c2ecf20Sopenharmony_ci res = PTR_ERR(bnode); 2558c2ecf20Sopenharmony_ci bnode = NULL; 2568c2ecf20Sopenharmony_ci goto out; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci fd->record -= cnt; 2608c2ecf20Sopenharmony_ci } else { 2618c2ecf20Sopenharmony_ci while (cnt >= bnode->num_recs - fd->record) { 2628c2ecf20Sopenharmony_ci cnt -= bnode->num_recs - fd->record; 2638c2ecf20Sopenharmony_ci fd->record = 0; 2648c2ecf20Sopenharmony_ci idx = bnode->next; 2658c2ecf20Sopenharmony_ci if (!idx) { 2668c2ecf20Sopenharmony_ci res = -ENOENT; 2678c2ecf20Sopenharmony_ci goto out; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci hfs_bnode_put(bnode); 2708c2ecf20Sopenharmony_ci bnode = hfs_bnode_find(tree, idx); 2718c2ecf20Sopenharmony_ci if (IS_ERR(bnode)) { 2728c2ecf20Sopenharmony_ci res = PTR_ERR(bnode); 2738c2ecf20Sopenharmony_ci bnode = NULL; 2748c2ecf20Sopenharmony_ci goto out; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci fd->record += cnt; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci len = hfs_brec_lenoff(bnode, fd->record, &off); 2818c2ecf20Sopenharmony_ci keylen = hfs_brec_keylen(bnode, fd->record); 2828c2ecf20Sopenharmony_ci if (keylen == 0) { 2838c2ecf20Sopenharmony_ci res = -EINVAL; 2848c2ecf20Sopenharmony_ci goto out; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci fd->keyoffset = off; 2878c2ecf20Sopenharmony_ci fd->keylength = keylen; 2888c2ecf20Sopenharmony_ci fd->entryoffset = off + keylen; 2898c2ecf20Sopenharmony_ci fd->entrylength = len - keylen; 2908c2ecf20Sopenharmony_ci hfs_bnode_read(bnode, fd->key, off, keylen); 2918c2ecf20Sopenharmony_ciout: 2928c2ecf20Sopenharmony_ci fd->bnode = bnode; 2938c2ecf20Sopenharmony_ci return res; 2948c2ecf20Sopenharmony_ci} 295