18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/fs/hfs/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 "btree.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 if (!tree) 208c2ecf20Sopenharmony_ci return -EINVAL; 218c2ecf20Sopenharmony_ci fd->tree = tree; 228c2ecf20Sopenharmony_ci fd->bnode = NULL; 238c2ecf20Sopenharmony_ci ptr = kmalloc(tree->max_key_len * 2 + 4, GFP_KERNEL); 248c2ecf20Sopenharmony_ci if (!ptr) 258c2ecf20Sopenharmony_ci return -ENOMEM; 268c2ecf20Sopenharmony_ci fd->search_key = ptr; 278c2ecf20Sopenharmony_ci fd->key = ptr + tree->max_key_len + 2; 288c2ecf20Sopenharmony_ci hfs_dbg(BNODE_REFS, "find_init: %d (%p)\n", 298c2ecf20Sopenharmony_ci tree->cnid, __builtin_return_address(0)); 308c2ecf20Sopenharmony_ci switch (tree->cnid) { 318c2ecf20Sopenharmony_ci case HFS_CAT_CNID: 328c2ecf20Sopenharmony_ci mutex_lock_nested(&tree->tree_lock, CATALOG_BTREE_MUTEX); 338c2ecf20Sopenharmony_ci break; 348c2ecf20Sopenharmony_ci case HFS_EXT_CNID: 358c2ecf20Sopenharmony_ci mutex_lock_nested(&tree->tree_lock, EXTENTS_BTREE_MUTEX); 368c2ecf20Sopenharmony_ci break; 378c2ecf20Sopenharmony_ci case HFS_ATTR_CNID: 388c2ecf20Sopenharmony_ci mutex_lock_nested(&tree->tree_lock, ATTR_BTREE_MUTEX); 398c2ecf20Sopenharmony_ci break; 408c2ecf20Sopenharmony_ci default: 418c2ecf20Sopenharmony_ci return -EINVAL; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci return 0; 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_civoid hfs_find_exit(struct hfs_find_data *fd) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci hfs_bnode_put(fd->bnode); 498c2ecf20Sopenharmony_ci kfree(fd->search_key); 508c2ecf20Sopenharmony_ci hfs_dbg(BNODE_REFS, "find_exit: %d (%p)\n", 518c2ecf20Sopenharmony_ci fd->tree->cnid, __builtin_return_address(0)); 528c2ecf20Sopenharmony_ci mutex_unlock(&fd->tree->tree_lock); 538c2ecf20Sopenharmony_ci fd->tree = NULL; 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* Find the record in bnode that best matches key (not greater than...)*/ 578c2ecf20Sopenharmony_ciint __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci int cmpval; 608c2ecf20Sopenharmony_ci u16 off, len, keylen; 618c2ecf20Sopenharmony_ci int rec; 628c2ecf20Sopenharmony_ci int b, e; 638c2ecf20Sopenharmony_ci int res; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci b = 0; 668c2ecf20Sopenharmony_ci e = bnode->num_recs - 1; 678c2ecf20Sopenharmony_ci res = -ENOENT; 688c2ecf20Sopenharmony_ci do { 698c2ecf20Sopenharmony_ci rec = (e + b) / 2; 708c2ecf20Sopenharmony_ci len = hfs_brec_lenoff(bnode, rec, &off); 718c2ecf20Sopenharmony_ci keylen = hfs_brec_keylen(bnode, rec); 728c2ecf20Sopenharmony_ci if (keylen == 0) { 738c2ecf20Sopenharmony_ci res = -EINVAL; 748c2ecf20Sopenharmony_ci goto fail; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci hfs_bnode_read(bnode, fd->key, off, keylen); 778c2ecf20Sopenharmony_ci cmpval = bnode->tree->keycmp(fd->key, fd->search_key); 788c2ecf20Sopenharmony_ci if (!cmpval) { 798c2ecf20Sopenharmony_ci e = rec; 808c2ecf20Sopenharmony_ci res = 0; 818c2ecf20Sopenharmony_ci goto done; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci if (cmpval < 0) 848c2ecf20Sopenharmony_ci b = rec + 1; 858c2ecf20Sopenharmony_ci else 868c2ecf20Sopenharmony_ci e = rec - 1; 878c2ecf20Sopenharmony_ci } while (b <= e); 888c2ecf20Sopenharmony_ci if (rec != e && e >= 0) { 898c2ecf20Sopenharmony_ci len = hfs_brec_lenoff(bnode, e, &off); 908c2ecf20Sopenharmony_ci keylen = hfs_brec_keylen(bnode, e); 918c2ecf20Sopenharmony_ci if (keylen == 0) { 928c2ecf20Sopenharmony_ci res = -EINVAL; 938c2ecf20Sopenharmony_ci goto fail; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci hfs_bnode_read(bnode, fd->key, off, keylen); 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_cidone: 988c2ecf20Sopenharmony_ci fd->record = e; 998c2ecf20Sopenharmony_ci fd->keyoffset = off; 1008c2ecf20Sopenharmony_ci fd->keylength = keylen; 1018c2ecf20Sopenharmony_ci fd->entryoffset = off + keylen; 1028c2ecf20Sopenharmony_ci fd->entrylength = len - keylen; 1038c2ecf20Sopenharmony_cifail: 1048c2ecf20Sopenharmony_ci return res; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* Traverse a B*Tree from the root to a leaf finding best fit to key */ 1088c2ecf20Sopenharmony_ci/* Return allocated copy of node found, set recnum to best record */ 1098c2ecf20Sopenharmony_ciint hfs_brec_find(struct hfs_find_data *fd) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct hfs_btree *tree; 1128c2ecf20Sopenharmony_ci struct hfs_bnode *bnode; 1138c2ecf20Sopenharmony_ci u32 nidx, parent; 1148c2ecf20Sopenharmony_ci __be32 data; 1158c2ecf20Sopenharmony_ci int height, res; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci tree = fd->tree; 1188c2ecf20Sopenharmony_ci if (fd->bnode) 1198c2ecf20Sopenharmony_ci hfs_bnode_put(fd->bnode); 1208c2ecf20Sopenharmony_ci fd->bnode = NULL; 1218c2ecf20Sopenharmony_ci nidx = tree->root; 1228c2ecf20Sopenharmony_ci if (!nidx) 1238c2ecf20Sopenharmony_ci return -ENOENT; 1248c2ecf20Sopenharmony_ci height = tree->depth; 1258c2ecf20Sopenharmony_ci res = 0; 1268c2ecf20Sopenharmony_ci parent = 0; 1278c2ecf20Sopenharmony_ci for (;;) { 1288c2ecf20Sopenharmony_ci bnode = hfs_bnode_find(tree, nidx); 1298c2ecf20Sopenharmony_ci if (IS_ERR(bnode)) { 1308c2ecf20Sopenharmony_ci res = PTR_ERR(bnode); 1318c2ecf20Sopenharmony_ci bnode = NULL; 1328c2ecf20Sopenharmony_ci break; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci if (bnode->height != height) 1358c2ecf20Sopenharmony_ci goto invalid; 1368c2ecf20Sopenharmony_ci if (bnode->type != (--height ? HFS_NODE_INDEX : HFS_NODE_LEAF)) 1378c2ecf20Sopenharmony_ci goto invalid; 1388c2ecf20Sopenharmony_ci bnode->parent = parent; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci res = __hfs_brec_find(bnode, fd); 1418c2ecf20Sopenharmony_ci if (!height) 1428c2ecf20Sopenharmony_ci break; 1438c2ecf20Sopenharmony_ci if (fd->record < 0) 1448c2ecf20Sopenharmony_ci goto release; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci parent = nidx; 1478c2ecf20Sopenharmony_ci hfs_bnode_read(bnode, &data, fd->entryoffset, 4); 1488c2ecf20Sopenharmony_ci nidx = be32_to_cpu(data); 1498c2ecf20Sopenharmony_ci hfs_bnode_put(bnode); 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci fd->bnode = bnode; 1528c2ecf20Sopenharmony_ci return res; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ciinvalid: 1558c2ecf20Sopenharmony_ci pr_err("inconsistency in B*Tree (%d,%d,%d,%u,%u)\n", 1568c2ecf20Sopenharmony_ci height, bnode->height, bnode->type, nidx, parent); 1578c2ecf20Sopenharmony_ci res = -EIO; 1588c2ecf20Sopenharmony_cirelease: 1598c2ecf20Sopenharmony_ci hfs_bnode_put(bnode); 1608c2ecf20Sopenharmony_ci return res; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ciint hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci int res; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci res = hfs_brec_find(fd); 1688c2ecf20Sopenharmony_ci if (res) 1698c2ecf20Sopenharmony_ci return res; 1708c2ecf20Sopenharmony_ci if (fd->entrylength > rec_len) 1718c2ecf20Sopenharmony_ci return -EINVAL; 1728c2ecf20Sopenharmony_ci hfs_bnode_read(fd->bnode, rec, fd->entryoffset, fd->entrylength); 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ciint hfs_brec_goto(struct hfs_find_data *fd, int cnt) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci struct hfs_btree *tree; 1798c2ecf20Sopenharmony_ci struct hfs_bnode *bnode; 1808c2ecf20Sopenharmony_ci int idx, res = 0; 1818c2ecf20Sopenharmony_ci u16 off, len, keylen; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci bnode = fd->bnode; 1848c2ecf20Sopenharmony_ci tree = bnode->tree; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (cnt < 0) { 1878c2ecf20Sopenharmony_ci cnt = -cnt; 1888c2ecf20Sopenharmony_ci while (cnt > fd->record) { 1898c2ecf20Sopenharmony_ci cnt -= fd->record + 1; 1908c2ecf20Sopenharmony_ci fd->record = bnode->num_recs - 1; 1918c2ecf20Sopenharmony_ci idx = bnode->prev; 1928c2ecf20Sopenharmony_ci if (!idx) { 1938c2ecf20Sopenharmony_ci res = -ENOENT; 1948c2ecf20Sopenharmony_ci goto out; 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci hfs_bnode_put(bnode); 1978c2ecf20Sopenharmony_ci bnode = hfs_bnode_find(tree, idx); 1988c2ecf20Sopenharmony_ci if (IS_ERR(bnode)) { 1998c2ecf20Sopenharmony_ci res = PTR_ERR(bnode); 2008c2ecf20Sopenharmony_ci bnode = NULL; 2018c2ecf20Sopenharmony_ci goto out; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci fd->record -= cnt; 2058c2ecf20Sopenharmony_ci } else { 2068c2ecf20Sopenharmony_ci while (cnt >= bnode->num_recs - fd->record) { 2078c2ecf20Sopenharmony_ci cnt -= bnode->num_recs - fd->record; 2088c2ecf20Sopenharmony_ci fd->record = 0; 2098c2ecf20Sopenharmony_ci idx = bnode->next; 2108c2ecf20Sopenharmony_ci if (!idx) { 2118c2ecf20Sopenharmony_ci res = -ENOENT; 2128c2ecf20Sopenharmony_ci goto out; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci hfs_bnode_put(bnode); 2158c2ecf20Sopenharmony_ci bnode = hfs_bnode_find(tree, idx); 2168c2ecf20Sopenharmony_ci if (IS_ERR(bnode)) { 2178c2ecf20Sopenharmony_ci res = PTR_ERR(bnode); 2188c2ecf20Sopenharmony_ci bnode = NULL; 2198c2ecf20Sopenharmony_ci goto out; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci fd->record += cnt; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci len = hfs_brec_lenoff(bnode, fd->record, &off); 2268c2ecf20Sopenharmony_ci keylen = hfs_brec_keylen(bnode, fd->record); 2278c2ecf20Sopenharmony_ci if (keylen == 0) { 2288c2ecf20Sopenharmony_ci res = -EINVAL; 2298c2ecf20Sopenharmony_ci goto out; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci fd->keyoffset = off; 2328c2ecf20Sopenharmony_ci fd->keylength = keylen; 2338c2ecf20Sopenharmony_ci fd->entryoffset = off + keylen; 2348c2ecf20Sopenharmony_ci fd->entrylength = len - keylen; 2358c2ecf20Sopenharmony_ci hfs_bnode_read(bnode, fd->key, off, keylen); 2368c2ecf20Sopenharmony_ciout: 2378c2ecf20Sopenharmony_ci fd->bnode = bnode; 2388c2ecf20Sopenharmony_ci return res; 2398c2ecf20Sopenharmony_ci} 240