1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * linux/fs/hfsplus/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 "hfsplus_fs.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",
272753cc28SAnton Salikhmetov tree->cnid, __builtin_return_address(0));
28*f04da10dSChao Yu mutex_lock_nested(&tree->tree_lock,
29*f04da10dSChao Yu hfsplus_btree_lock_class(tree));
301da177e4SLinus Torvalds return 0;
311da177e4SLinus Torvalds }
321da177e4SLinus Torvalds
hfs_find_exit(struct hfs_find_data * fd)331da177e4SLinus Torvalds void hfs_find_exit(struct hfs_find_data *fd)
341da177e4SLinus Torvalds {
351da177e4SLinus Torvalds hfs_bnode_put(fd->bnode);
361da177e4SLinus Torvalds kfree(fd->search_key);
37c2b3e1f7SJoe Perches hfs_dbg(BNODE_REFS, "find_exit: %d (%p)\n",
382753cc28SAnton Salikhmetov fd->tree->cnid, __builtin_return_address(0));
39467c3d9cSThomas Gleixner mutex_unlock(&fd->tree->tree_lock);
401da177e4SLinus Torvalds fd->tree = NULL;
411da177e4SLinus Torvalds }
421da177e4SLinus Torvalds
hfs_find_1st_rec_by_cnid(struct hfs_bnode * bnode,struct hfs_find_data * fd,int * begin,int * end,int * cur_rec)43324ef39aSVyacheslav Dubeyko int hfs_find_1st_rec_by_cnid(struct hfs_bnode *bnode,
44324ef39aSVyacheslav Dubeyko struct hfs_find_data *fd,
45324ef39aSVyacheslav Dubeyko int *begin,
46324ef39aSVyacheslav Dubeyko int *end,
47324ef39aSVyacheslav Dubeyko int *cur_rec)
48324ef39aSVyacheslav Dubeyko {
495f3726f9SVyacheslav Dubeyko __be32 cur_cnid;
505f3726f9SVyacheslav Dubeyko __be32 search_cnid;
51324ef39aSVyacheslav Dubeyko
52324ef39aSVyacheslav Dubeyko if (bnode->tree->cnid == HFSPLUS_EXT_CNID) {
53324ef39aSVyacheslav Dubeyko cur_cnid = fd->key->ext.cnid;
54324ef39aSVyacheslav Dubeyko search_cnid = fd->search_key->ext.cnid;
55324ef39aSVyacheslav Dubeyko } else if (bnode->tree->cnid == HFSPLUS_CAT_CNID) {
56324ef39aSVyacheslav Dubeyko cur_cnid = fd->key->cat.parent;
57324ef39aSVyacheslav Dubeyko search_cnid = fd->search_key->cat.parent;
58324ef39aSVyacheslav Dubeyko } else if (bnode->tree->cnid == HFSPLUS_ATTR_CNID) {
59324ef39aSVyacheslav Dubeyko cur_cnid = fd->key->attr.cnid;
60324ef39aSVyacheslav Dubeyko search_cnid = fd->search_key->attr.cnid;
615f3726f9SVyacheslav Dubeyko } else {
625f3726f9SVyacheslav Dubeyko cur_cnid = 0; /* used-uninitialized warning */
635f3726f9SVyacheslav Dubeyko search_cnid = 0;
64324ef39aSVyacheslav Dubeyko BUG();
655f3726f9SVyacheslav Dubeyko }
66324ef39aSVyacheslav Dubeyko
67324ef39aSVyacheslav Dubeyko if (cur_cnid == search_cnid) {
68324ef39aSVyacheslav Dubeyko (*end) = (*cur_rec);
69324ef39aSVyacheslav Dubeyko if ((*begin) == (*end))
70324ef39aSVyacheslav Dubeyko return 1;
71324ef39aSVyacheslav Dubeyko } else {
72324ef39aSVyacheslav Dubeyko if (be32_to_cpu(cur_cnid) < be32_to_cpu(search_cnid))
73324ef39aSVyacheslav Dubeyko (*begin) = (*cur_rec) + 1;
74324ef39aSVyacheslav Dubeyko else
75324ef39aSVyacheslav Dubeyko (*end) = (*cur_rec) - 1;
76324ef39aSVyacheslav Dubeyko }
77324ef39aSVyacheslav Dubeyko
78324ef39aSVyacheslav Dubeyko return 0;
79324ef39aSVyacheslav Dubeyko }
80324ef39aSVyacheslav Dubeyko
hfs_find_rec_by_key(struct hfs_bnode * bnode,struct hfs_find_data * fd,int * begin,int * end,int * cur_rec)81324ef39aSVyacheslav Dubeyko int hfs_find_rec_by_key(struct hfs_bnode *bnode,
82324ef39aSVyacheslav Dubeyko struct hfs_find_data *fd,
83324ef39aSVyacheslav Dubeyko int *begin,
84324ef39aSVyacheslav Dubeyko int *end,
85324ef39aSVyacheslav Dubeyko int *cur_rec)
861da177e4SLinus Torvalds {
871da177e4SLinus Torvalds int cmpval;
88324ef39aSVyacheslav Dubeyko
89324ef39aSVyacheslav Dubeyko cmpval = bnode->tree->keycmp(fd->key, fd->search_key);
90324ef39aSVyacheslav Dubeyko if (!cmpval) {
91324ef39aSVyacheslav Dubeyko (*end) = (*cur_rec);
92324ef39aSVyacheslav Dubeyko return 1;
93324ef39aSVyacheslav Dubeyko }
94324ef39aSVyacheslav Dubeyko if (cmpval < 0)
95324ef39aSVyacheslav Dubeyko (*begin) = (*cur_rec) + 1;
96324ef39aSVyacheslav Dubeyko else
97324ef39aSVyacheslav Dubeyko *(end) = (*cur_rec) - 1;
98324ef39aSVyacheslav Dubeyko
99324ef39aSVyacheslav Dubeyko return 0;
100324ef39aSVyacheslav Dubeyko }
101324ef39aSVyacheslav Dubeyko
102324ef39aSVyacheslav Dubeyko /* Find the record in bnode that best matches key (not greater than...)*/
__hfs_brec_find(struct hfs_bnode * bnode,struct hfs_find_data * fd,search_strategy_t rec_found)103324ef39aSVyacheslav Dubeyko int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd,
104324ef39aSVyacheslav Dubeyko search_strategy_t rec_found)
105324ef39aSVyacheslav Dubeyko {
1061da177e4SLinus Torvalds u16 off, len, keylen;
1071da177e4SLinus Torvalds int rec;
1081da177e4SLinus Torvalds int b, e;
1091da177e4SLinus Torvalds int res;
1101da177e4SLinus Torvalds
1117ce844a2SFabian Frederick BUG_ON(!rec_found);
1121da177e4SLinus Torvalds b = 0;
1131da177e4SLinus Torvalds e = bnode->num_recs - 1;
1141da177e4SLinus Torvalds res = -ENOENT;
1151da177e4SLinus Torvalds do {
1161da177e4SLinus Torvalds rec = (e + b) / 2;
1171da177e4SLinus Torvalds len = hfs_brec_lenoff(bnode, rec, &off);
1181da177e4SLinus Torvalds keylen = hfs_brec_keylen(bnode, rec);
1199250f925SEric Sandeen if (keylen == 0) {
1209250f925SEric Sandeen res = -EINVAL;
1219250f925SEric Sandeen goto fail;
1229250f925SEric Sandeen }
1231da177e4SLinus Torvalds hfs_bnode_read(bnode, fd->key, off, keylen);
124324ef39aSVyacheslav Dubeyko if (rec_found(bnode, fd, &b, &e, &rec)) {
1251da177e4SLinus Torvalds res = 0;
1261da177e4SLinus Torvalds goto done;
1271da177e4SLinus Torvalds }
1281da177e4SLinus Torvalds } while (b <= e);
129324ef39aSVyacheslav Dubeyko
1301da177e4SLinus Torvalds if (rec != e && e >= 0) {
1311da177e4SLinus Torvalds len = hfs_brec_lenoff(bnode, e, &off);
1321da177e4SLinus Torvalds keylen = hfs_brec_keylen(bnode, e);
1339250f925SEric Sandeen if (keylen == 0) {
1349250f925SEric Sandeen res = -EINVAL;
1359250f925SEric Sandeen goto fail;
1369250f925SEric Sandeen }
1371da177e4SLinus Torvalds hfs_bnode_read(bnode, fd->key, off, keylen);
1381da177e4SLinus Torvalds }
139324ef39aSVyacheslav Dubeyko
1401da177e4SLinus Torvalds done:
1411da177e4SLinus Torvalds fd->record = e;
1421da177e4SLinus Torvalds fd->keyoffset = off;
1431da177e4SLinus Torvalds fd->keylength = keylen;
1441da177e4SLinus Torvalds fd->entryoffset = off + keylen;
1451da177e4SLinus Torvalds fd->entrylength = len - keylen;
146324ef39aSVyacheslav Dubeyko
1479250f925SEric Sandeen fail:
1481da177e4SLinus Torvalds return res;
1491da177e4SLinus Torvalds }
1501da177e4SLinus Torvalds
1511da177e4SLinus Torvalds /* Traverse a B*Tree from the root to a leaf finding best fit to key */
1521da177e4SLinus Torvalds /* Return allocated copy of node found, set recnum to best record */
hfs_brec_find(struct hfs_find_data * fd,search_strategy_t do_key_compare)153324ef39aSVyacheslav Dubeyko int hfs_brec_find(struct hfs_find_data *fd, search_strategy_t do_key_compare)
1541da177e4SLinus Torvalds {
1551da177e4SLinus Torvalds struct hfs_btree *tree;
1561da177e4SLinus Torvalds struct hfs_bnode *bnode;
1571da177e4SLinus Torvalds u32 nidx, parent;
1581da177e4SLinus Torvalds __be32 data;
1591da177e4SLinus Torvalds int height, res;
1601da177e4SLinus Torvalds
1611da177e4SLinus Torvalds tree = fd->tree;
1621da177e4SLinus Torvalds if (fd->bnode)
1631da177e4SLinus Torvalds hfs_bnode_put(fd->bnode);
1641da177e4SLinus Torvalds fd->bnode = NULL;
1651da177e4SLinus Torvalds nidx = tree->root;
1661da177e4SLinus Torvalds if (!nidx)
1671da177e4SLinus Torvalds return -ENOENT;
1681da177e4SLinus Torvalds height = tree->depth;
1691da177e4SLinus Torvalds res = 0;
1701da177e4SLinus Torvalds parent = 0;
1711da177e4SLinus Torvalds for (;;) {
1721da177e4SLinus Torvalds bnode = hfs_bnode_find(tree, nidx);
1731da177e4SLinus Torvalds if (IS_ERR(bnode)) {
1741da177e4SLinus Torvalds res = PTR_ERR(bnode);
1751da177e4SLinus Torvalds bnode = NULL;
1761da177e4SLinus Torvalds break;
1771da177e4SLinus Torvalds }
1781da177e4SLinus Torvalds if (bnode->height != height)
1791da177e4SLinus Torvalds goto invalid;
1801da177e4SLinus Torvalds if (bnode->type != (--height ? HFS_NODE_INDEX : HFS_NODE_LEAF))
1811da177e4SLinus Torvalds goto invalid;
1821da177e4SLinus Torvalds bnode->parent = parent;
1831da177e4SLinus Torvalds
184324ef39aSVyacheslav Dubeyko res = __hfs_brec_find(bnode, fd, do_key_compare);
1851da177e4SLinus Torvalds if (!height)
1861da177e4SLinus Torvalds break;
1871da177e4SLinus Torvalds if (fd->record < 0)
1881da177e4SLinus Torvalds goto release;
1891da177e4SLinus Torvalds
1901da177e4SLinus Torvalds parent = nidx;
1911da177e4SLinus Torvalds hfs_bnode_read(bnode, &data, fd->entryoffset, 4);
1921da177e4SLinus Torvalds nidx = be32_to_cpu(data);
1931da177e4SLinus Torvalds hfs_bnode_put(bnode);
1941da177e4SLinus Torvalds }
1951da177e4SLinus Torvalds fd->bnode = bnode;
1961da177e4SLinus Torvalds return res;
1971da177e4SLinus Torvalds
1981da177e4SLinus Torvalds invalid:
199d6142673SJoe Perches pr_err("inconsistency in B*Tree (%d,%d,%d,%u,%u)\n",
2001da177e4SLinus Torvalds height, bnode->height, bnode->type, nidx, parent);
2011da177e4SLinus Torvalds res = -EIO;
2021da177e4SLinus Torvalds release:
2031da177e4SLinus Torvalds hfs_bnode_put(bnode);
2041da177e4SLinus Torvalds return res;
2051da177e4SLinus Torvalds }
2061da177e4SLinus Torvalds
hfs_brec_read(struct hfs_find_data * fd,void * rec,int rec_len)2071da177e4SLinus Torvalds int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len)
2081da177e4SLinus Torvalds {
2091da177e4SLinus Torvalds int res;
2101da177e4SLinus Torvalds
211324ef39aSVyacheslav Dubeyko res = hfs_brec_find(fd, hfs_find_rec_by_key);
2121da177e4SLinus Torvalds if (res)
2131da177e4SLinus Torvalds return res;
2141da177e4SLinus Torvalds if (fd->entrylength > rec_len)
2151da177e4SLinus Torvalds return -EINVAL;
2161da177e4SLinus Torvalds hfs_bnode_read(fd->bnode, rec, fd->entryoffset, fd->entrylength);
2171da177e4SLinus Torvalds return 0;
2181da177e4SLinus Torvalds }
2191da177e4SLinus Torvalds
hfs_brec_goto(struct hfs_find_data * fd,int cnt)2201da177e4SLinus Torvalds int hfs_brec_goto(struct hfs_find_data *fd, int cnt)
2211da177e4SLinus Torvalds {
2221da177e4SLinus Torvalds struct hfs_btree *tree;
2231da177e4SLinus Torvalds struct hfs_bnode *bnode;
2241da177e4SLinus Torvalds int idx, res = 0;
2251da177e4SLinus Torvalds u16 off, len, keylen;
2261da177e4SLinus Torvalds
2271da177e4SLinus Torvalds bnode = fd->bnode;
2281da177e4SLinus Torvalds tree = bnode->tree;
2291da177e4SLinus Torvalds
2301da177e4SLinus Torvalds if (cnt < 0) {
2311da177e4SLinus Torvalds cnt = -cnt;
2321da177e4SLinus Torvalds while (cnt > fd->record) {
2331da177e4SLinus Torvalds cnt -= fd->record + 1;
2341da177e4SLinus Torvalds fd->record = bnode->num_recs - 1;
2351da177e4SLinus Torvalds idx = bnode->prev;
2361da177e4SLinus Torvalds if (!idx) {
2371da177e4SLinus Torvalds res = -ENOENT;
2381da177e4SLinus Torvalds goto out;
2391da177e4SLinus Torvalds }
2401da177e4SLinus Torvalds hfs_bnode_put(bnode);
2411da177e4SLinus Torvalds bnode = hfs_bnode_find(tree, idx);
2421da177e4SLinus Torvalds if (IS_ERR(bnode)) {
2431da177e4SLinus Torvalds res = PTR_ERR(bnode);
2441da177e4SLinus Torvalds bnode = NULL;
2451da177e4SLinus Torvalds goto out;
2461da177e4SLinus Torvalds }
2471da177e4SLinus Torvalds }
2481da177e4SLinus Torvalds fd->record -= cnt;
2491da177e4SLinus Torvalds } else {
2501da177e4SLinus Torvalds while (cnt >= bnode->num_recs - fd->record) {
2511da177e4SLinus Torvalds cnt -= bnode->num_recs - fd->record;
2521da177e4SLinus Torvalds fd->record = 0;
2531da177e4SLinus Torvalds idx = bnode->next;
2541da177e4SLinus Torvalds if (!idx) {
2551da177e4SLinus Torvalds res = -ENOENT;
2561da177e4SLinus Torvalds goto out;
2571da177e4SLinus Torvalds }
2581da177e4SLinus Torvalds hfs_bnode_put(bnode);
2591da177e4SLinus Torvalds bnode = hfs_bnode_find(tree, idx);
2601da177e4SLinus Torvalds if (IS_ERR(bnode)) {
2611da177e4SLinus Torvalds res = PTR_ERR(bnode);
2621da177e4SLinus Torvalds bnode = NULL;
2631da177e4SLinus Torvalds goto out;
2641da177e4SLinus Torvalds }
2651da177e4SLinus Torvalds }
2661da177e4SLinus Torvalds fd->record += cnt;
2671da177e4SLinus Torvalds }
2681da177e4SLinus Torvalds
2691da177e4SLinus Torvalds len = hfs_brec_lenoff(bnode, fd->record, &off);
2701da177e4SLinus Torvalds keylen = hfs_brec_keylen(bnode, fd->record);
2719250f925SEric Sandeen if (keylen == 0) {
2729250f925SEric Sandeen res = -EINVAL;
2739250f925SEric Sandeen goto out;
2749250f925SEric Sandeen }
2751da177e4SLinus Torvalds fd->keyoffset = off;
2761da177e4SLinus Torvalds fd->keylength = keylen;
2771da177e4SLinus Torvalds fd->entryoffset = off + keylen;
2781da177e4SLinus Torvalds fd->entrylength = len - keylen;
2791da177e4SLinus Torvalds hfs_bnode_read(bnode, fd->key, off, keylen);
2801da177e4SLinus Torvalds out:
2811da177e4SLinus Torvalds fd->bnode = bnode;
2821da177e4SLinus Torvalds return res;
2831da177e4SLinus Torvalds }
284