xref: /openbmc/linux/fs/hfsplus/bfind.c (revision 7e24a55b2122746c2eef192296fc84624354f895)
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