bfind.c 4.6 KB

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