xref: /linux/fs/hfs/bfind.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
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