bfind.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * linux/fs/hfsplus/bfind.c
  4. *
  5. * Copyright (C) 2001
  6. * Brad Boyer (flar@allandria.com)
  7. * (C) 2003 Ardis Technologies <roman@ardistech.com>
  8. *
  9. * Search routines for btrees
  10. */
  11. #include <linux/slab.h>
  12. #include "hfsplus_fs.h"
  13. int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd)
  14. {
  15. void *ptr;
  16. fd->tree = tree;
  17. fd->bnode = NULL;
  18. ptr = kmalloc(tree->max_key_len * 2 + 4, GFP_KERNEL);
  19. if (!ptr)
  20. return -ENOMEM;
  21. fd->search_key = ptr;
  22. fd->key = ptr + tree->max_key_len + 2;
  23. hfs_dbg(BNODE_REFS, "find_init: %d (%p)\n",
  24. tree->cnid, __builtin_return_address(0));
  25. switch (tree->cnid) {
  26. case HFSPLUS_CAT_CNID:
  27. mutex_lock_nested(&tree->tree_lock, CATALOG_BTREE_MUTEX);
  28. break;
  29. case HFSPLUS_EXT_CNID:
  30. mutex_lock_nested(&tree->tree_lock, EXTENTS_BTREE_MUTEX);
  31. break;
  32. case HFSPLUS_ATTR_CNID:
  33. mutex_lock_nested(&tree->tree_lock, ATTR_BTREE_MUTEX);
  34. break;
  35. default:
  36. BUG();
  37. }
  38. return 0;
  39. }
  40. void hfs_find_exit(struct hfs_find_data *fd)
  41. {
  42. hfs_bnode_put(fd->bnode);
  43. kfree(fd->search_key);
  44. hfs_dbg(BNODE_REFS, "find_exit: %d (%p)\n",
  45. fd->tree->cnid, __builtin_return_address(0));
  46. mutex_unlock(&fd->tree->tree_lock);
  47. fd->tree = NULL;
  48. }
  49. int hfs_find_1st_rec_by_cnid(struct hfs_bnode *bnode,
  50. struct hfs_find_data *fd,
  51. int *begin,
  52. int *end,
  53. int *cur_rec)
  54. {
  55. __be32 cur_cnid;
  56. __be32 search_cnid;
  57. if (bnode->tree->cnid == HFSPLUS_EXT_CNID) {
  58. cur_cnid = fd->key->ext.cnid;
  59. search_cnid = fd->search_key->ext.cnid;
  60. } else if (bnode->tree->cnid == HFSPLUS_CAT_CNID) {
  61. cur_cnid = fd->key->cat.parent;
  62. search_cnid = fd->search_key->cat.parent;
  63. } else if (bnode->tree->cnid == HFSPLUS_ATTR_CNID) {
  64. cur_cnid = fd->key->attr.cnid;
  65. search_cnid = fd->search_key->attr.cnid;
  66. } else {
  67. cur_cnid = 0; /* used-uninitialized warning */
  68. search_cnid = 0;
  69. BUG();
  70. }
  71. if (cur_cnid == search_cnid) {
  72. (*end) = (*cur_rec);
  73. if ((*begin) == (*end))
  74. return 1;
  75. } else {
  76. if (be32_to_cpu(cur_cnid) < be32_to_cpu(search_cnid))
  77. (*begin) = (*cur_rec) + 1;
  78. else
  79. (*end) = (*cur_rec) - 1;
  80. }
  81. return 0;
  82. }
  83. int hfs_find_rec_by_key(struct hfs_bnode *bnode,
  84. struct hfs_find_data *fd,
  85. int *begin,
  86. int *end,
  87. int *cur_rec)
  88. {
  89. int cmpval;
  90. cmpval = bnode->tree->keycmp(fd->key, fd->search_key);
  91. if (!cmpval) {
  92. (*end) = (*cur_rec);
  93. return 1;
  94. }
  95. if (cmpval < 0)
  96. (*begin) = (*cur_rec) + 1;
  97. else
  98. *(end) = (*cur_rec) - 1;
  99. return 0;
  100. }
  101. /* Find the record in bnode that best matches key (not greater than...)*/
  102. int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd,
  103. search_strategy_t rec_found)
  104. {
  105. u16 off, len, keylen;
  106. int rec;
  107. int b, e;
  108. int res;
  109. BUG_ON(!rec_found);
  110. b = 0;
  111. e = bnode->num_recs - 1;
  112. res = -ENOENT;
  113. do {
  114. rec = (e + b) / 2;
  115. len = hfs_brec_lenoff(bnode, rec, &off);
  116. keylen = hfs_brec_keylen(bnode, rec);
  117. if (keylen == 0) {
  118. res = -EINVAL;
  119. goto fail;
  120. }
  121. hfs_bnode_read(bnode, fd->key, off, keylen);
  122. if (rec_found(bnode, fd, &b, &e, &rec)) {
  123. res = 0;
  124. goto done;
  125. }
  126. } while (b <= e);
  127. if (rec != e && e >= 0) {
  128. len = hfs_brec_lenoff(bnode, e, &off);
  129. keylen = hfs_brec_keylen(bnode, e);
  130. if (keylen == 0) {
  131. res = -EINVAL;
  132. goto fail;
  133. }
  134. hfs_bnode_read(bnode, fd->key, off, keylen);
  135. }
  136. done:
  137. fd->record = e;
  138. fd->keyoffset = off;
  139. fd->keylength = keylen;
  140. fd->entryoffset = off + keylen;
  141. fd->entrylength = len - keylen;
  142. fail:
  143. return res;
  144. }
  145. /* Traverse a B*Tree from the root to a leaf finding best fit to key */
  146. /* Return allocated copy of node found, set recnum to best record */
  147. int hfs_brec_find(struct hfs_find_data *fd, search_strategy_t do_key_compare)
  148. {
  149. struct hfs_btree *tree;
  150. struct hfs_bnode *bnode;
  151. u32 nidx, parent;
  152. __be32 data;
  153. int height, res;
  154. tree = fd->tree;
  155. if (fd->bnode)
  156. hfs_bnode_put(fd->bnode);
  157. fd->bnode = NULL;
  158. nidx = tree->root;
  159. if (!nidx)
  160. return -ENOENT;
  161. height = tree->depth;
  162. res = 0;
  163. parent = 0;
  164. for (;;) {
  165. bnode = hfs_bnode_find(tree, nidx);
  166. if (IS_ERR(bnode)) {
  167. res = PTR_ERR(bnode);
  168. bnode = NULL;
  169. break;
  170. }
  171. if (bnode->height != height)
  172. goto invalid;
  173. if (bnode->type != (--height ? HFS_NODE_INDEX : HFS_NODE_LEAF))
  174. goto invalid;
  175. bnode->parent = parent;
  176. res = __hfs_brec_find(bnode, fd, do_key_compare);
  177. if (!height)
  178. break;
  179. if (fd->record < 0)
  180. goto release;
  181. parent = nidx;
  182. hfs_bnode_read(bnode, &data, fd->entryoffset, 4);
  183. nidx = be32_to_cpu(data);
  184. hfs_bnode_put(bnode);
  185. }
  186. fd->bnode = bnode;
  187. return res;
  188. invalid:
  189. pr_err("inconsistency in B*Tree (%d,%d,%d,%u,%u)\n",
  190. height, bnode->height, bnode->type, nidx, parent);
  191. res = -EIO;
  192. release:
  193. hfs_bnode_put(bnode);
  194. return res;
  195. }
  196. int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len)
  197. {
  198. int res;
  199. res = hfs_brec_find(fd, hfs_find_rec_by_key);
  200. if (res)
  201. return res;
  202. if (fd->entrylength > rec_len)
  203. return -EINVAL;
  204. hfs_bnode_read(fd->bnode, rec, fd->entryoffset, fd->entrylength);
  205. return 0;
  206. }
  207. int hfs_brec_goto(struct hfs_find_data *fd, int cnt)
  208. {
  209. struct hfs_btree *tree;
  210. struct hfs_bnode *bnode;
  211. int idx, res = 0;
  212. u16 off, len, keylen;
  213. bnode = fd->bnode;
  214. tree = bnode->tree;
  215. if (cnt < 0) {
  216. cnt = -cnt;
  217. while (cnt > fd->record) {
  218. cnt -= fd->record + 1;
  219. fd->record = bnode->num_recs - 1;
  220. idx = bnode->prev;
  221. if (!idx) {
  222. res = -ENOENT;
  223. goto out;
  224. }
  225. hfs_bnode_put(bnode);
  226. bnode = hfs_bnode_find(tree, idx);
  227. if (IS_ERR(bnode)) {
  228. res = PTR_ERR(bnode);
  229. bnode = NULL;
  230. goto out;
  231. }
  232. }
  233. fd->record -= cnt;
  234. } else {
  235. while (cnt >= bnode->num_recs - fd->record) {
  236. cnt -= bnode->num_recs - fd->record;
  237. fd->record = 0;
  238. idx = bnode->next;
  239. if (!idx) {
  240. res = -ENOENT;
  241. goto out;
  242. }
  243. hfs_bnode_put(bnode);
  244. bnode = hfs_bnode_find(tree, idx);
  245. if (IS_ERR(bnode)) {
  246. res = PTR_ERR(bnode);
  247. bnode = NULL;
  248. goto out;
  249. }
  250. }
  251. fd->record += cnt;
  252. }
  253. len = hfs_brec_lenoff(bnode, fd->record, &off);
  254. keylen = hfs_brec_keylen(bnode, fd->record);
  255. if (keylen == 0) {
  256. res = -EINVAL;
  257. goto out;
  258. }
  259. fd->keyoffset = off;
  260. fd->keylength = keylen;
  261. fd->entryoffset = off + keylen;
  262. fd->entrylength = len - keylen;
  263. hfs_bnode_read(bnode, fd->key, off, keylen);
  264. out:
  265. fd->bnode = bnode;
  266. return res;
  267. }