162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/fs/hfs/bfind.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2001 662306a36Sopenharmony_ci * Brad Boyer (flar@allandria.com) 762306a36Sopenharmony_ci * (C) 2003 Ardis Technologies <roman@ardistech.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Search routines for btrees 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include "btree.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ciint hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd) 1662306a36Sopenharmony_ci{ 1762306a36Sopenharmony_ci void *ptr; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci fd->tree = tree; 2062306a36Sopenharmony_ci fd->bnode = NULL; 2162306a36Sopenharmony_ci ptr = kmalloc(tree->max_key_len * 2 + 4, GFP_KERNEL); 2262306a36Sopenharmony_ci if (!ptr) 2362306a36Sopenharmony_ci return -ENOMEM; 2462306a36Sopenharmony_ci fd->search_key = ptr; 2562306a36Sopenharmony_ci fd->key = ptr + tree->max_key_len + 2; 2662306a36Sopenharmony_ci hfs_dbg(BNODE_REFS, "find_init: %d (%p)\n", 2762306a36Sopenharmony_ci tree->cnid, __builtin_return_address(0)); 2862306a36Sopenharmony_ci switch (tree->cnid) { 2962306a36Sopenharmony_ci case HFS_CAT_CNID: 3062306a36Sopenharmony_ci mutex_lock_nested(&tree->tree_lock, CATALOG_BTREE_MUTEX); 3162306a36Sopenharmony_ci break; 3262306a36Sopenharmony_ci case HFS_EXT_CNID: 3362306a36Sopenharmony_ci mutex_lock_nested(&tree->tree_lock, EXTENTS_BTREE_MUTEX); 3462306a36Sopenharmony_ci break; 3562306a36Sopenharmony_ci case HFS_ATTR_CNID: 3662306a36Sopenharmony_ci mutex_lock_nested(&tree->tree_lock, ATTR_BTREE_MUTEX); 3762306a36Sopenharmony_ci break; 3862306a36Sopenharmony_ci default: 3962306a36Sopenharmony_ci return -EINVAL; 4062306a36Sopenharmony_ci } 4162306a36Sopenharmony_ci return 0; 4262306a36Sopenharmony_ci} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_civoid hfs_find_exit(struct hfs_find_data *fd) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci hfs_bnode_put(fd->bnode); 4762306a36Sopenharmony_ci kfree(fd->search_key); 4862306a36Sopenharmony_ci hfs_dbg(BNODE_REFS, "find_exit: %d (%p)\n", 4962306a36Sopenharmony_ci fd->tree->cnid, __builtin_return_address(0)); 5062306a36Sopenharmony_ci mutex_unlock(&fd->tree->tree_lock); 5162306a36Sopenharmony_ci fd->tree = NULL; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* Find the record in bnode that best matches key (not greater than...)*/ 5562306a36Sopenharmony_ciint __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci int cmpval; 5862306a36Sopenharmony_ci u16 off, len, keylen; 5962306a36Sopenharmony_ci int rec; 6062306a36Sopenharmony_ci int b, e; 6162306a36Sopenharmony_ci int res; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci b = 0; 6462306a36Sopenharmony_ci e = bnode->num_recs - 1; 6562306a36Sopenharmony_ci res = -ENOENT; 6662306a36Sopenharmony_ci do { 6762306a36Sopenharmony_ci rec = (e + b) / 2; 6862306a36Sopenharmony_ci len = hfs_brec_lenoff(bnode, rec, &off); 6962306a36Sopenharmony_ci keylen = hfs_brec_keylen(bnode, rec); 7062306a36Sopenharmony_ci if (keylen == 0) { 7162306a36Sopenharmony_ci res = -EINVAL; 7262306a36Sopenharmony_ci goto fail; 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci hfs_bnode_read(bnode, fd->key, off, keylen); 7562306a36Sopenharmony_ci cmpval = bnode->tree->keycmp(fd->key, fd->search_key); 7662306a36Sopenharmony_ci if (!cmpval) { 7762306a36Sopenharmony_ci e = rec; 7862306a36Sopenharmony_ci res = 0; 7962306a36Sopenharmony_ci goto done; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci if (cmpval < 0) 8262306a36Sopenharmony_ci b = rec + 1; 8362306a36Sopenharmony_ci else 8462306a36Sopenharmony_ci e = rec - 1; 8562306a36Sopenharmony_ci } while (b <= e); 8662306a36Sopenharmony_ci if (rec != e && e >= 0) { 8762306a36Sopenharmony_ci len = hfs_brec_lenoff(bnode, e, &off); 8862306a36Sopenharmony_ci keylen = hfs_brec_keylen(bnode, e); 8962306a36Sopenharmony_ci if (keylen == 0) { 9062306a36Sopenharmony_ci res = -EINVAL; 9162306a36Sopenharmony_ci goto fail; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci hfs_bnode_read(bnode, fd->key, off, keylen); 9462306a36Sopenharmony_ci } 9562306a36Sopenharmony_cidone: 9662306a36Sopenharmony_ci fd->record = e; 9762306a36Sopenharmony_ci fd->keyoffset = off; 9862306a36Sopenharmony_ci fd->keylength = keylen; 9962306a36Sopenharmony_ci fd->entryoffset = off + keylen; 10062306a36Sopenharmony_ci fd->entrylength = len - keylen; 10162306a36Sopenharmony_cifail: 10262306a36Sopenharmony_ci return res; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* Traverse a B*Tree from the root to a leaf finding best fit to key */ 10662306a36Sopenharmony_ci/* Return allocated copy of node found, set recnum to best record */ 10762306a36Sopenharmony_ciint hfs_brec_find(struct hfs_find_data *fd) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct hfs_btree *tree; 11062306a36Sopenharmony_ci struct hfs_bnode *bnode; 11162306a36Sopenharmony_ci u32 nidx, parent; 11262306a36Sopenharmony_ci __be32 data; 11362306a36Sopenharmony_ci int height, res; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci tree = fd->tree; 11662306a36Sopenharmony_ci if (fd->bnode) 11762306a36Sopenharmony_ci hfs_bnode_put(fd->bnode); 11862306a36Sopenharmony_ci fd->bnode = NULL; 11962306a36Sopenharmony_ci nidx = tree->root; 12062306a36Sopenharmony_ci if (!nidx) 12162306a36Sopenharmony_ci return -ENOENT; 12262306a36Sopenharmony_ci height = tree->depth; 12362306a36Sopenharmony_ci res = 0; 12462306a36Sopenharmony_ci parent = 0; 12562306a36Sopenharmony_ci for (;;) { 12662306a36Sopenharmony_ci bnode = hfs_bnode_find(tree, nidx); 12762306a36Sopenharmony_ci if (IS_ERR(bnode)) { 12862306a36Sopenharmony_ci res = PTR_ERR(bnode); 12962306a36Sopenharmony_ci bnode = NULL; 13062306a36Sopenharmony_ci break; 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci if (bnode->height != height) 13362306a36Sopenharmony_ci goto invalid; 13462306a36Sopenharmony_ci if (bnode->type != (--height ? HFS_NODE_INDEX : HFS_NODE_LEAF)) 13562306a36Sopenharmony_ci goto invalid; 13662306a36Sopenharmony_ci bnode->parent = parent; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci res = __hfs_brec_find(bnode, fd); 13962306a36Sopenharmony_ci if (!height) 14062306a36Sopenharmony_ci break; 14162306a36Sopenharmony_ci if (fd->record < 0) 14262306a36Sopenharmony_ci goto release; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci parent = nidx; 14562306a36Sopenharmony_ci hfs_bnode_read(bnode, &data, fd->entryoffset, 4); 14662306a36Sopenharmony_ci nidx = be32_to_cpu(data); 14762306a36Sopenharmony_ci hfs_bnode_put(bnode); 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci fd->bnode = bnode; 15062306a36Sopenharmony_ci return res; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ciinvalid: 15362306a36Sopenharmony_ci pr_err("inconsistency in B*Tree (%d,%d,%d,%u,%u)\n", 15462306a36Sopenharmony_ci height, bnode->height, bnode->type, nidx, parent); 15562306a36Sopenharmony_ci res = -EIO; 15662306a36Sopenharmony_cirelease: 15762306a36Sopenharmony_ci hfs_bnode_put(bnode); 15862306a36Sopenharmony_ci return res; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ciint hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci int res; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci res = hfs_brec_find(fd); 16662306a36Sopenharmony_ci if (res) 16762306a36Sopenharmony_ci return res; 16862306a36Sopenharmony_ci if (fd->entrylength > rec_len) 16962306a36Sopenharmony_ci return -EINVAL; 17062306a36Sopenharmony_ci hfs_bnode_read(fd->bnode, rec, fd->entryoffset, fd->entrylength); 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ciint hfs_brec_goto(struct hfs_find_data *fd, int cnt) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct hfs_btree *tree; 17762306a36Sopenharmony_ci struct hfs_bnode *bnode; 17862306a36Sopenharmony_ci int idx, res = 0; 17962306a36Sopenharmony_ci u16 off, len, keylen; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci bnode = fd->bnode; 18262306a36Sopenharmony_ci tree = bnode->tree; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (cnt < 0) { 18562306a36Sopenharmony_ci cnt = -cnt; 18662306a36Sopenharmony_ci while (cnt > fd->record) { 18762306a36Sopenharmony_ci cnt -= fd->record + 1; 18862306a36Sopenharmony_ci fd->record = bnode->num_recs - 1; 18962306a36Sopenharmony_ci idx = bnode->prev; 19062306a36Sopenharmony_ci if (!idx) { 19162306a36Sopenharmony_ci res = -ENOENT; 19262306a36Sopenharmony_ci goto out; 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci hfs_bnode_put(bnode); 19562306a36Sopenharmony_ci bnode = hfs_bnode_find(tree, idx); 19662306a36Sopenharmony_ci if (IS_ERR(bnode)) { 19762306a36Sopenharmony_ci res = PTR_ERR(bnode); 19862306a36Sopenharmony_ci bnode = NULL; 19962306a36Sopenharmony_ci goto out; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci fd->record -= cnt; 20362306a36Sopenharmony_ci } else { 20462306a36Sopenharmony_ci while (cnt >= bnode->num_recs - fd->record) { 20562306a36Sopenharmony_ci cnt -= bnode->num_recs - fd->record; 20662306a36Sopenharmony_ci fd->record = 0; 20762306a36Sopenharmony_ci idx = bnode->next; 20862306a36Sopenharmony_ci if (!idx) { 20962306a36Sopenharmony_ci res = -ENOENT; 21062306a36Sopenharmony_ci goto out; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci hfs_bnode_put(bnode); 21362306a36Sopenharmony_ci bnode = hfs_bnode_find(tree, idx); 21462306a36Sopenharmony_ci if (IS_ERR(bnode)) { 21562306a36Sopenharmony_ci res = PTR_ERR(bnode); 21662306a36Sopenharmony_ci bnode = NULL; 21762306a36Sopenharmony_ci goto out; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci fd->record += cnt; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci len = hfs_brec_lenoff(bnode, fd->record, &off); 22462306a36Sopenharmony_ci keylen = hfs_brec_keylen(bnode, fd->record); 22562306a36Sopenharmony_ci if (keylen == 0) { 22662306a36Sopenharmony_ci res = -EINVAL; 22762306a36Sopenharmony_ci goto out; 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci fd->keyoffset = off; 23062306a36Sopenharmony_ci fd->keylength = keylen; 23162306a36Sopenharmony_ci fd->entryoffset = off + keylen; 23262306a36Sopenharmony_ci fd->entrylength = len - keylen; 23362306a36Sopenharmony_ci hfs_bnode_read(bnode, fd->key, off, keylen); 23462306a36Sopenharmony_ciout: 23562306a36Sopenharmony_ci fd->bnode = bnode; 23662306a36Sopenharmony_ci return res; 23762306a36Sopenharmony_ci} 238