bfind.c 6.1 KB

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