bfind.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * linux/fs/hfs/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 "btree.h"
  13. int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd)
  14. {
  15. void *ptr;
  16. if (!tree || !fd)
  17. return -EINVAL;
  18. fd->tree = tree;
  19. fd->bnode = NULL;
  20. ptr = kzalloc(tree->max_key_len * 2 + 4, GFP_KERNEL);
  21. if (!ptr)
  22. return -ENOMEM;
  23. fd->search_key = ptr;
  24. fd->key = ptr + tree->max_key_len + 2;
  25. hfs_dbg(BNODE_REFS, "find_init: %d (%p)\n",
  26. tree->cnid, __builtin_return_address(0));
  27. switch (tree->cnid) {
  28. case HFS_CAT_CNID:
  29. mutex_lock_nested(&tree->tree_lock, CATALOG_BTREE_MUTEX);
  30. break;
  31. case HFS_EXT_CNID:
  32. mutex_lock_nested(&tree->tree_lock, EXTENTS_BTREE_MUTEX);
  33. break;
  34. case HFS_ATTR_CNID:
  35. mutex_lock_nested(&tree->tree_lock, ATTR_BTREE_MUTEX);
  36. break;
  37. default:
  38. return -EINVAL;
  39. }
  40. return 0;
  41. }
  42. void hfs_find_exit(struct hfs_find_data *fd)
  43. {
  44. hfs_bnode_put(fd->bnode);
  45. kfree(fd->search_key);
  46. hfs_dbg(BNODE_REFS, "find_exit: %d (%p)\n",
  47. fd->tree->cnid, __builtin_return_address(0));
  48. mutex_unlock(&fd->tree->tree_lock);
  49. fd->tree = NULL;
  50. }
  51. /* Find the record in bnode that best matches key (not greater than...)*/
  52. int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd)
  53. {
  54. int cmpval;
  55. u16 off, len, keylen;
  56. int rec;
  57. int b, e;
  58. int res;
  59. b = 0;
  60. e = bnode->num_recs - 1;
  61. res = -ENOENT;
  62. do {
  63. rec = (e + b) / 2;
  64. len = hfs_brec_lenoff(bnode, rec, &off);
  65. keylen = hfs_brec_keylen(bnode, rec);
  66. if (keylen == 0) {
  67. res = -EINVAL;
  68. goto fail;
  69. }
  70. hfs_bnode_read(bnode, fd->key, off, keylen);
  71. cmpval = bnode->tree->keycmp(fd->key, fd->search_key);
  72. if (!cmpval) {
  73. e = rec;
  74. res = 0;
  75. goto done;
  76. }
  77. if (cmpval < 0)
  78. b = rec + 1;
  79. else
  80. e = rec - 1;
  81. } while (b <= e);
  82. if (rec != e && e >= 0) {
  83. len = hfs_brec_lenoff(bnode, e, &off);
  84. keylen = hfs_brec_keylen(bnode, e);
  85. if (keylen == 0) {
  86. res = -EINVAL;
  87. goto fail;
  88. }
  89. hfs_bnode_read(bnode, fd->key, off, keylen);
  90. }
  91. done:
  92. fd->record = e;
  93. fd->keyoffset = off;
  94. fd->keylength = keylen;
  95. fd->entryoffset = off + keylen;
  96. fd->entrylength = len - keylen;
  97. fail:
  98. return res;
  99. }
  100. /* Traverse a B*Tree from the root to a leaf finding best fit to key */
  101. /* Return allocated copy of node found, set recnum to best record */
  102. int hfs_brec_find(struct hfs_find_data *fd)
  103. {
  104. struct hfs_btree *tree;
  105. struct hfs_bnode *bnode;
  106. u32 nidx, parent;
  107. __be32 data;
  108. int height, res;
  109. fd->record = -1;
  110. fd->keyoffset = -1;
  111. fd->keylength = -1;
  112. fd->entryoffset = -1;
  113. fd->entrylength = -1;
  114. tree = fd->tree;
  115. if (fd->bnode)
  116. hfs_bnode_put(fd->bnode);
  117. fd->bnode = NULL;
  118. nidx = tree->root;
  119. if (!nidx)
  120. return -ENOENT;
  121. height = tree->depth;
  122. res = 0;
  123. parent = 0;
  124. for (;;) {
  125. bnode = hfs_bnode_find(tree, nidx);
  126. if (IS_ERR(bnode)) {
  127. res = PTR_ERR(bnode);
  128. bnode = NULL;
  129. break;
  130. }
  131. if (bnode->height != height)
  132. goto invalid;
  133. if (bnode->type != (--height ? HFS_NODE_INDEX : HFS_NODE_LEAF))
  134. goto invalid;
  135. bnode->parent = parent;
  136. res = __hfs_brec_find(bnode, fd);
  137. if (!height)
  138. break;
  139. if (fd->record < 0)
  140. goto release;
  141. parent = nidx;
  142. hfs_bnode_read(bnode, &data, fd->entryoffset, 4);
  143. nidx = be32_to_cpu(data);
  144. hfs_bnode_put(bnode);
  145. }
  146. fd->bnode = bnode;
  147. return res;
  148. invalid:
  149. pr_err("inconsistency in B*Tree (%d,%d,%d,%u,%u)\n",
  150. height, bnode->height, bnode->type, nidx, parent);
  151. res = -EIO;
  152. release:
  153. hfs_bnode_put(bnode);
  154. return res;
  155. }
  156. int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len)
  157. {
  158. int res;
  159. res = hfs_brec_find(fd);
  160. if (res)
  161. return res;
  162. if (fd->entrylength > rec_len)
  163. return -EINVAL;
  164. hfs_bnode_read(fd->bnode, rec, fd->entryoffset, fd->entrylength);
  165. return 0;
  166. }
  167. int hfs_brec_goto(struct hfs_find_data *fd, int cnt)
  168. {
  169. struct hfs_btree *tree;
  170. struct hfs_bnode *bnode;
  171. int idx, res = 0;
  172. u16 off, len, keylen;
  173. bnode = fd->bnode;
  174. tree = bnode->tree;
  175. if (cnt < 0) {
  176. cnt = -cnt;
  177. while (cnt > fd->record) {
  178. cnt -= fd->record + 1;
  179. fd->record = bnode->num_recs - 1;
  180. idx = bnode->prev;
  181. if (!idx) {
  182. res = -ENOENT;
  183. goto out;
  184. }
  185. hfs_bnode_put(bnode);
  186. bnode = hfs_bnode_find(tree, idx);
  187. if (IS_ERR(bnode)) {
  188. res = PTR_ERR(bnode);
  189. bnode = NULL;
  190. goto out;
  191. }
  192. }
  193. fd->record -= cnt;
  194. } else {
  195. while (cnt >= bnode->num_recs - fd->record) {
  196. cnt -= bnode->num_recs - fd->record;
  197. fd->record = 0;
  198. idx = bnode->next;
  199. if (!idx) {
  200. res = -ENOENT;
  201. goto out;
  202. }
  203. hfs_bnode_put(bnode);
  204. bnode = hfs_bnode_find(tree, idx);
  205. if (IS_ERR(bnode)) {
  206. res = PTR_ERR(bnode);
  207. bnode = NULL;
  208. goto out;
  209. }
  210. }
  211. fd->record += cnt;
  212. }
  213. len = hfs_brec_lenoff(bnode, fd->record, &off);
  214. keylen = hfs_brec_keylen(bnode, fd->record);
  215. if (keylen == 0) {
  216. res = -EINVAL;
  217. goto out;
  218. }
  219. fd->keyoffset = off;
  220. fd->keylength = keylen;
  221. fd->entryoffset = off + keylen;
  222. fd->entrylength = len - keylen;
  223. hfs_bnode_read(bnode, fd->key, off, keylen);
  224. out:
  225. fd->bnode = bnode;
  226. return res;
  227. }