bfind.c 4.6 KB

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