1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * linux/fs/hfs/bfind.c
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Copyright (C) 2001
61da177e4SLinus Torvalds * Brad Boyer (flar@allandria.com)
71da177e4SLinus Torvalds * (C) 2003 Ardis Technologies <roman@ardistech.com>
81da177e4SLinus Torvalds *
91da177e4SLinus Torvalds * Search routines for btrees
101da177e4SLinus Torvalds */
111da177e4SLinus Torvalds
121da177e4SLinus Torvalds #include <linux/slab.h>
131da177e4SLinus Torvalds #include "btree.h"
141da177e4SLinus Torvalds
hfs_find_init(struct hfs_btree * tree,struct hfs_find_data * fd)151da177e4SLinus Torvalds int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd)
161da177e4SLinus Torvalds {
171da177e4SLinus Torvalds void *ptr;
181da177e4SLinus Torvalds
191da177e4SLinus Torvalds fd->tree = tree;
201da177e4SLinus Torvalds fd->bnode = NULL;
211da177e4SLinus Torvalds ptr = kmalloc(tree->max_key_len * 2 + 4, GFP_KERNEL);
221da177e4SLinus Torvalds if (!ptr)
231da177e4SLinus Torvalds return -ENOMEM;
241da177e4SLinus Torvalds fd->search_key = ptr;
251da177e4SLinus Torvalds fd->key = ptr + tree->max_key_len + 2;
26c2b3e1f7SJoe Perches hfs_dbg(BNODE_REFS, "find_init: %d (%p)\n",
27c2b3e1f7SJoe Perches tree->cnid, __builtin_return_address(0));
28*b3b2177aSDesmond Cheong Zhi Xi switch (tree->cnid) {
29*b3b2177aSDesmond Cheong Zhi Xi case HFS_CAT_CNID:
30*b3b2177aSDesmond Cheong Zhi Xi mutex_lock_nested(&tree->tree_lock, CATALOG_BTREE_MUTEX);
31*b3b2177aSDesmond Cheong Zhi Xi break;
32*b3b2177aSDesmond Cheong Zhi Xi case HFS_EXT_CNID:
33*b3b2177aSDesmond Cheong Zhi Xi mutex_lock_nested(&tree->tree_lock, EXTENTS_BTREE_MUTEX);
34*b3b2177aSDesmond Cheong Zhi Xi break;
35*b3b2177aSDesmond Cheong Zhi Xi case HFS_ATTR_CNID:
36*b3b2177aSDesmond Cheong Zhi Xi mutex_lock_nested(&tree->tree_lock, ATTR_BTREE_MUTEX);
37*b3b2177aSDesmond Cheong Zhi Xi break;
38*b3b2177aSDesmond Cheong Zhi Xi default:
39*b3b2177aSDesmond Cheong Zhi Xi return -EINVAL;
40*b3b2177aSDesmond Cheong Zhi Xi }
411da177e4SLinus Torvalds return 0;
421da177e4SLinus Torvalds }
431da177e4SLinus Torvalds
hfs_find_exit(struct hfs_find_data * fd)441da177e4SLinus Torvalds void hfs_find_exit(struct hfs_find_data *fd)
451da177e4SLinus Torvalds {
461da177e4SLinus Torvalds hfs_bnode_put(fd->bnode);
471da177e4SLinus Torvalds kfree(fd->search_key);
48c2b3e1f7SJoe Perches hfs_dbg(BNODE_REFS, "find_exit: %d (%p)\n",
49c2b3e1f7SJoe Perches fd->tree->cnid, __builtin_return_address(0));
504a941035SThomas Gleixner mutex_unlock(&fd->tree->tree_lock);
511da177e4SLinus Torvalds fd->tree = NULL;
521da177e4SLinus Torvalds }
531da177e4SLinus Torvalds
541da177e4SLinus Torvalds /* Find the record in bnode that best matches key (not greater than...)*/
__hfs_brec_find(struct hfs_bnode * bnode,struct hfs_find_data * fd)551da177e4SLinus Torvalds int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd)
561da177e4SLinus Torvalds {
571da177e4SLinus Torvalds int cmpval;
581da177e4SLinus Torvalds u16 off, len, keylen;
591da177e4SLinus Torvalds int rec;
601da177e4SLinus Torvalds int b, e;
611da177e4SLinus Torvalds int res;
621da177e4SLinus Torvalds
631da177e4SLinus Torvalds b = 0;
641da177e4SLinus Torvalds e = bnode->num_recs - 1;
651da177e4SLinus Torvalds res = -ENOENT;
661da177e4SLinus Torvalds do {
671da177e4SLinus Torvalds rec = (e + b) / 2;
681da177e4SLinus Torvalds len = hfs_brec_lenoff(bnode, rec, &off);
691da177e4SLinus Torvalds keylen = hfs_brec_keylen(bnode, rec);
7055581d01SEric Sandeen if (keylen == 0) {
71cf059462SEric Sandeen res = -EINVAL;
7255581d01SEric Sandeen goto fail;
73cf059462SEric Sandeen }
741da177e4SLinus Torvalds hfs_bnode_read(bnode, fd->key, off, keylen);
751da177e4SLinus Torvalds cmpval = bnode->tree->keycmp(fd->key, fd->search_key);
761da177e4SLinus Torvalds if (!cmpval) {
771da177e4SLinus Torvalds e = rec;
781da177e4SLinus Torvalds res = 0;
791da177e4SLinus Torvalds goto done;
801da177e4SLinus Torvalds }
811da177e4SLinus Torvalds if (cmpval < 0)
821da177e4SLinus Torvalds b = rec + 1;
831da177e4SLinus Torvalds else
841da177e4SLinus Torvalds e = rec - 1;
851da177e4SLinus Torvalds } while (b <= e);
861da177e4SLinus Torvalds if (rec != e && e >= 0) {
871da177e4SLinus Torvalds len = hfs_brec_lenoff(bnode, e, &off);
881da177e4SLinus Torvalds keylen = hfs_brec_keylen(bnode, e);
8955581d01SEric Sandeen if (keylen == 0) {
90cf059462SEric Sandeen res = -EINVAL;
9155581d01SEric Sandeen goto fail;
92cf059462SEric Sandeen }
931da177e4SLinus Torvalds hfs_bnode_read(bnode, fd->key, off, keylen);
941da177e4SLinus Torvalds }
951da177e4SLinus Torvalds done:
961da177e4SLinus Torvalds fd->record = e;
971da177e4SLinus Torvalds fd->keyoffset = off;
981da177e4SLinus Torvalds fd->keylength = keylen;
991da177e4SLinus Torvalds fd->entryoffset = off + keylen;
1001da177e4SLinus Torvalds fd->entrylength = len - keylen;
10155581d01SEric Sandeen fail:
1021da177e4SLinus Torvalds return res;
1031da177e4SLinus Torvalds }
1041da177e4SLinus Torvalds
1051da177e4SLinus Torvalds /* Traverse a B*Tree from the root to a leaf finding best fit to key */
1061da177e4SLinus Torvalds /* Return allocated copy of node found, set recnum to best record */
hfs_brec_find(struct hfs_find_data * fd)1071da177e4SLinus Torvalds int hfs_brec_find(struct hfs_find_data *fd)
1081da177e4SLinus Torvalds {
1091da177e4SLinus Torvalds struct hfs_btree *tree;
1101da177e4SLinus Torvalds struct hfs_bnode *bnode;
1111da177e4SLinus Torvalds u32 nidx, parent;
1121da177e4SLinus Torvalds __be32 data;
1131da177e4SLinus Torvalds int height, res;
1141da177e4SLinus Torvalds
1151da177e4SLinus Torvalds tree = fd->tree;
1161da177e4SLinus Torvalds if (fd->bnode)
1171da177e4SLinus Torvalds hfs_bnode_put(fd->bnode);
1181da177e4SLinus Torvalds fd->bnode = NULL;
1191da177e4SLinus Torvalds nidx = tree->root;
1201da177e4SLinus Torvalds if (!nidx)
1211da177e4SLinus Torvalds return -ENOENT;
1221da177e4SLinus Torvalds height = tree->depth;
1231da177e4SLinus Torvalds res = 0;
1241da177e4SLinus Torvalds parent = 0;
1251da177e4SLinus Torvalds for (;;) {
1261da177e4SLinus Torvalds bnode = hfs_bnode_find(tree, nidx);
1271da177e4SLinus Torvalds if (IS_ERR(bnode)) {
1281da177e4SLinus Torvalds res = PTR_ERR(bnode);
1291da177e4SLinus Torvalds bnode = NULL;
1301da177e4SLinus Torvalds break;
1311da177e4SLinus Torvalds }
1321da177e4SLinus Torvalds if (bnode->height != height)
1331da177e4SLinus Torvalds goto invalid;
1341da177e4SLinus Torvalds if (bnode->type != (--height ? HFS_NODE_INDEX : HFS_NODE_LEAF))
1351da177e4SLinus Torvalds goto invalid;
1361da177e4SLinus Torvalds bnode->parent = parent;
1371da177e4SLinus Torvalds
1381da177e4SLinus Torvalds res = __hfs_brec_find(bnode, fd);
1391da177e4SLinus Torvalds if (!height)
1401da177e4SLinus Torvalds break;
1411da177e4SLinus Torvalds if (fd->record < 0)
1421da177e4SLinus Torvalds goto release;
1431da177e4SLinus Torvalds
1441da177e4SLinus Torvalds parent = nidx;
1451da177e4SLinus Torvalds hfs_bnode_read(bnode, &data, fd->entryoffset, 4);
1461da177e4SLinus Torvalds nidx = be32_to_cpu(data);
1471da177e4SLinus Torvalds hfs_bnode_put(bnode);
1481da177e4SLinus Torvalds }
1491da177e4SLinus Torvalds fd->bnode = bnode;
1501da177e4SLinus Torvalds return res;
1511da177e4SLinus Torvalds
1521da177e4SLinus Torvalds invalid:
153d6142673SJoe Perches pr_err("inconsistency in B*Tree (%d,%d,%d,%u,%u)\n",
1541da177e4SLinus Torvalds height, bnode->height, bnode->type, nidx, parent);
1551da177e4SLinus Torvalds res = -EIO;
1561da177e4SLinus Torvalds release:
1571da177e4SLinus Torvalds hfs_bnode_put(bnode);
1581da177e4SLinus Torvalds return res;
1591da177e4SLinus Torvalds }
1601da177e4SLinus Torvalds
hfs_brec_read(struct hfs_find_data * fd,void * rec,int rec_len)1611da177e4SLinus Torvalds int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len)
1621da177e4SLinus Torvalds {
1631da177e4SLinus Torvalds int res;
1641da177e4SLinus Torvalds
1651da177e4SLinus Torvalds res = hfs_brec_find(fd);
1661da177e4SLinus Torvalds if (res)
1671da177e4SLinus Torvalds return res;
1681da177e4SLinus Torvalds if (fd->entrylength > rec_len)
1691da177e4SLinus Torvalds return -EINVAL;
1701da177e4SLinus Torvalds hfs_bnode_read(fd->bnode, rec, fd->entryoffset, fd->entrylength);
1711da177e4SLinus Torvalds return 0;
1721da177e4SLinus Torvalds }
1731da177e4SLinus Torvalds
hfs_brec_goto(struct hfs_find_data * fd,int cnt)1741da177e4SLinus Torvalds int hfs_brec_goto(struct hfs_find_data *fd, int cnt)
1751da177e4SLinus Torvalds {
1761da177e4SLinus Torvalds struct hfs_btree *tree;
1771da177e4SLinus Torvalds struct hfs_bnode *bnode;
1781da177e4SLinus Torvalds int idx, res = 0;
1791da177e4SLinus Torvalds u16 off, len, keylen;
1801da177e4SLinus Torvalds
1811da177e4SLinus Torvalds bnode = fd->bnode;
1821da177e4SLinus Torvalds tree = bnode->tree;
1831da177e4SLinus Torvalds
1841da177e4SLinus Torvalds if (cnt < 0) {
1851da177e4SLinus Torvalds cnt = -cnt;
1861da177e4SLinus Torvalds while (cnt > fd->record) {
1871da177e4SLinus Torvalds cnt -= fd->record + 1;
1881da177e4SLinus Torvalds fd->record = bnode->num_recs - 1;
1891da177e4SLinus Torvalds idx = bnode->prev;
1901da177e4SLinus Torvalds if (!idx) {
1911da177e4SLinus Torvalds res = -ENOENT;
1921da177e4SLinus Torvalds goto out;
1931da177e4SLinus Torvalds }
1941da177e4SLinus Torvalds hfs_bnode_put(bnode);
1951da177e4SLinus Torvalds bnode = hfs_bnode_find(tree, idx);
1961da177e4SLinus Torvalds if (IS_ERR(bnode)) {
1971da177e4SLinus Torvalds res = PTR_ERR(bnode);
1981da177e4SLinus Torvalds bnode = NULL;
1991da177e4SLinus Torvalds goto out;
2001da177e4SLinus Torvalds }
2011da177e4SLinus Torvalds }
2021da177e4SLinus Torvalds fd->record -= cnt;
2031da177e4SLinus Torvalds } else {
2041da177e4SLinus Torvalds while (cnt >= bnode->num_recs - fd->record) {
2051da177e4SLinus Torvalds cnt -= bnode->num_recs - fd->record;
2061da177e4SLinus Torvalds fd->record = 0;
2071da177e4SLinus Torvalds idx = bnode->next;
2081da177e4SLinus Torvalds if (!idx) {
2091da177e4SLinus Torvalds res = -ENOENT;
2101da177e4SLinus Torvalds goto out;
2111da177e4SLinus Torvalds }
2121da177e4SLinus Torvalds hfs_bnode_put(bnode);
2131da177e4SLinus Torvalds bnode = hfs_bnode_find(tree, idx);
2141da177e4SLinus Torvalds if (IS_ERR(bnode)) {
2151da177e4SLinus Torvalds res = PTR_ERR(bnode);
2161da177e4SLinus Torvalds bnode = NULL;
2171da177e4SLinus Torvalds goto out;
2181da177e4SLinus Torvalds }
2191da177e4SLinus Torvalds }
2201da177e4SLinus Torvalds fd->record += cnt;
2211da177e4SLinus Torvalds }
2221da177e4SLinus Torvalds
2231da177e4SLinus Torvalds len = hfs_brec_lenoff(bnode, fd->record, &off);
2241da177e4SLinus Torvalds keylen = hfs_brec_keylen(bnode, fd->record);
22555581d01SEric Sandeen if (keylen == 0) {
226cf059462SEric Sandeen res = -EINVAL;
227cf059462SEric Sandeen goto out;
228cf059462SEric Sandeen }
2291da177e4SLinus Torvalds fd->keyoffset = off;
2301da177e4SLinus Torvalds fd->keylength = keylen;
2311da177e4SLinus Torvalds fd->entryoffset = off + keylen;
2321da177e4SLinus Torvalds fd->entrylength = len - keylen;
2331da177e4SLinus Torvalds hfs_bnode_read(bnode, fd->key, off, keylen);
2341da177e4SLinus Torvalds out:
2351da177e4SLinus Torvalds fd->bnode = bnode;
2361da177e4SLinus Torvalds return res;
2371da177e4SLinus Torvalds }
238