icount.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * icount.c --- an efficient inode count abstraction
  4. *
  5. * Copyright (C) 1997 Theodore Ts'o.
  6. *
  7. * %Begin-Header%
  8. * This file may be redistributed under the terms of the GNU Public
  9. * License.
  10. * %End-Header%
  11. */
  12. #if HAVE_UNISTD_H
  13. #include <unistd.h>
  14. #endif
  15. #include <string.h>
  16. #include <stdio.h>
  17. #include "ext2_fs.h"
  18. #include "ext2fs.h"
  19. /*
  20. * The data storage strategy used by icount relies on the observation
  21. * that most inode counts are either zero (for non-allocated inodes),
  22. * one (for most files), and only a few that are two or more
  23. * (directories and files that are linked to more than one directory).
  24. *
  25. * Also, e2fsck tends to load the icount data sequentially.
  26. *
  27. * So, we use an inode bitmap to indicate which inodes have a count of
  28. * one, and then use a sorted list to store the counts for inodes
  29. * which are greater than one.
  30. *
  31. * We also use an optional bitmap to indicate which inodes are already
  32. * in the sorted list, to speed up the use of this abstraction by
  33. * e2fsck's pass 2. Pass 2 increments inode counts as it finds them,
  34. * so this extra bitmap avoids searching the sorted list to see if a
  35. * particular inode is on the sorted list already.
  36. */
  37. struct ext2_icount_el {
  38. ext2_ino_t ino;
  39. __u16 count;
  40. };
  41. struct ext2_icount {
  42. errcode_t magic;
  43. ext2fs_inode_bitmap single;
  44. ext2fs_inode_bitmap multiple;
  45. ext2_ino_t count;
  46. ext2_ino_t size;
  47. ext2_ino_t num_inodes;
  48. ext2_ino_t cursor;
  49. struct ext2_icount_el *list;
  50. };
  51. void ext2fs_free_icount(ext2_icount_t icount)
  52. {
  53. if (!icount)
  54. return;
  55. icount->magic = 0;
  56. ext2fs_free_mem(&icount->list);
  57. ext2fs_free_inode_bitmap(icount->single);
  58. ext2fs_free_inode_bitmap(icount->multiple);
  59. ext2fs_free_mem(&icount);
  60. }
  61. errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size,
  62. ext2_icount_t hint, ext2_icount_t *ret)
  63. {
  64. ext2_icount_t icount;
  65. errcode_t retval;
  66. size_t bytes;
  67. ext2_ino_t i;
  68. if (hint) {
  69. EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT);
  70. if (hint->size > size)
  71. size = (size_t) hint->size;
  72. }
  73. retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount);
  74. if (retval)
  75. return retval;
  76. memset(icount, 0, sizeof(struct ext2_icount));
  77. retval = ext2fs_allocate_inode_bitmap(fs, 0,
  78. &icount->single);
  79. if (retval)
  80. goto errout;
  81. if (flags & EXT2_ICOUNT_OPT_INCREMENT) {
  82. retval = ext2fs_allocate_inode_bitmap(fs, 0,
  83. &icount->multiple);
  84. if (retval)
  85. goto errout;
  86. } else
  87. icount->multiple = 0;
  88. if (size) {
  89. icount->size = size;
  90. } else {
  91. /*
  92. * Figure out how many special case inode counts we will
  93. * have. We know we will need one for each directory;
  94. * we also need to reserve some extra room for file links
  95. */
  96. retval = ext2fs_get_num_dirs(fs, &icount->size);
  97. if (retval)
  98. goto errout;
  99. icount->size += fs->super->s_inodes_count / 50;
  100. }
  101. bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el));
  102. retval = ext2fs_get_mem(bytes, &icount->list);
  103. if (retval)
  104. goto errout;
  105. memset(icount->list, 0, bytes);
  106. icount->magic = EXT2_ET_MAGIC_ICOUNT;
  107. icount->count = 0;
  108. icount->cursor = 0;
  109. icount->num_inodes = fs->super->s_inodes_count;
  110. /*
  111. * Populate the sorted list with those entries which were
  112. * found in the hint icount (since those are ones which will
  113. * likely need to be in the sorted list this time around).
  114. */
  115. if (hint) {
  116. for (i=0; i < hint->count; i++)
  117. icount->list[i].ino = hint->list[i].ino;
  118. icount->count = hint->count;
  119. }
  120. *ret = icount;
  121. return 0;
  122. errout:
  123. ext2fs_free_icount(icount);
  124. return retval;
  125. }
  126. errcode_t ext2fs_create_icount(ext2_filsys fs, int flags,
  127. unsigned int size,
  128. ext2_icount_t *ret)
  129. {
  130. return ext2fs_create_icount2(fs, flags, size, 0, ret);
  131. }
  132. /*
  133. * insert_icount_el() --- Insert a new entry into the sorted list at a
  134. * specified position.
  135. */
  136. static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount,
  137. ext2_ino_t ino, int pos)
  138. {
  139. struct ext2_icount_el *el;
  140. errcode_t retval;
  141. ext2_ino_t new_size = 0;
  142. int num;
  143. if (icount->count >= icount->size) {
  144. if (icount->count) {
  145. new_size = icount->list[(unsigned)icount->count-1].ino;
  146. new_size = (ext2_ino_t) (icount->count *
  147. ((float) icount->num_inodes / new_size));
  148. }
  149. if (new_size < (icount->size + 100))
  150. new_size = icount->size + 100;
  151. retval = ext2fs_resize_mem((size_t) icount->size *
  152. sizeof(struct ext2_icount_el),
  153. (size_t) new_size *
  154. sizeof(struct ext2_icount_el),
  155. &icount->list);
  156. if (retval)
  157. return 0;
  158. icount->size = new_size;
  159. }
  160. num = (int) icount->count - pos;
  161. if (num < 0)
  162. return 0; /* should never happen */
  163. if (num) {
  164. memmove(&icount->list[pos+1], &icount->list[pos],
  165. sizeof(struct ext2_icount_el) * num);
  166. }
  167. icount->count++;
  168. el = &icount->list[pos];
  169. el->count = 0;
  170. el->ino = ino;
  171. return el;
  172. }
  173. /*
  174. * get_icount_el() --- given an inode number, try to find icount
  175. * information in the sorted list. If the create flag is set,
  176. * and we can't find an entry, create one in the sorted list.
  177. */
  178. static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
  179. ext2_ino_t ino, int create)
  180. {
  181. float range;
  182. int low, high, mid;
  183. ext2_ino_t lowval, highval;
  184. if (!icount || !icount->list)
  185. return 0;
  186. if (create && ((icount->count == 0) ||
  187. (ino > icount->list[(unsigned)icount->count-1].ino))) {
  188. return insert_icount_el(icount, ino, (unsigned) icount->count);
  189. }
  190. if (icount->count == 0)
  191. return 0;
  192. if (icount->cursor >= icount->count)
  193. icount->cursor = 0;
  194. if (ino == icount->list[icount->cursor].ino)
  195. return &icount->list[icount->cursor++];
  196. low = 0;
  197. high = (int) icount->count-1;
  198. while (low <= high) {
  199. if (low == high)
  200. mid = low;
  201. else {
  202. /* Interpolate for efficiency */
  203. lowval = icount->list[low].ino;
  204. highval = icount->list[high].ino;
  205. if (ino < lowval)
  206. range = 0;
  207. else if (ino > highval)
  208. range = 1;
  209. else
  210. range = ((float) (ino - lowval)) /
  211. (highval - lowval);
  212. mid = low + ((int) (range * (high-low)));
  213. }
  214. if (ino == icount->list[mid].ino) {
  215. icount->cursor = mid+1;
  216. return &icount->list[mid];
  217. }
  218. if (ino < icount->list[mid].ino)
  219. high = mid-1;
  220. else
  221. low = mid+1;
  222. }
  223. /*
  224. * If we need to create a new entry, it should be right at
  225. * low (where high will be left at low-1).
  226. */
  227. if (create)
  228. return insert_icount_el(icount, ino, low);
  229. return 0;
  230. }
  231. errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out)
  232. {
  233. errcode_t ret = 0;
  234. unsigned int i;
  235. const char *bad = "bad icount";
  236. EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
  237. if (icount->count > icount->size) {
  238. fprintf(out, "%s: count > size\n", bad);
  239. return EXT2_ET_INVALID_ARGUMENT;
  240. }
  241. for (i=1; i < icount->count; i++) {
  242. if (icount->list[i-1].ino >= icount->list[i].ino) {
  243. fprintf(out, "%s: list[%d].ino=%u, list[%d].ino=%u\n",
  244. bad, i-1, icount->list[i-1].ino,
  245. i, icount->list[i].ino);
  246. ret = EXT2_ET_INVALID_ARGUMENT;
  247. }
  248. }
  249. return ret;
  250. }
  251. errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret)
  252. {
  253. struct ext2_icount_el *el;
  254. EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
  255. if (!ino || (ino > icount->num_inodes))
  256. return EXT2_ET_INVALID_ARGUMENT;
  257. if (ext2fs_test_inode_bitmap(icount->single, ino)) {
  258. *ret = 1;
  259. return 0;
  260. }
  261. if (icount->multiple &&
  262. !ext2fs_test_inode_bitmap(icount->multiple, ino)) {
  263. *ret = 0;
  264. return 0;
  265. }
  266. el = get_icount_el(icount, ino, 0);
  267. if (!el) {
  268. *ret = 0;
  269. return 0;
  270. }
  271. *ret = el->count;
  272. return 0;
  273. }
  274. errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino,
  275. __u16 *ret)
  276. {
  277. struct ext2_icount_el *el;
  278. EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
  279. if (!ino || (ino > icount->num_inodes))
  280. return EXT2_ET_INVALID_ARGUMENT;
  281. if (ext2fs_test_inode_bitmap(icount->single, ino)) {
  282. /*
  283. * If the existing count is 1, then we know there is
  284. * no entry in the list.
  285. */
  286. el = get_icount_el(icount, ino, 1);
  287. if (!el)
  288. return EXT2_ET_NO_MEMORY;
  289. ext2fs_unmark_inode_bitmap(icount->single, ino);
  290. el->count = 2;
  291. } else if (icount->multiple) {
  292. /*
  293. * The count is either zero or greater than 1; if the
  294. * inode is set in icount->multiple, then there should
  295. * be an entry in the list, so find it using
  296. * get_icount_el().
  297. */
  298. if (ext2fs_test_inode_bitmap(icount->multiple, ino)) {
  299. el = get_icount_el(icount, ino, 1);
  300. if (!el)
  301. return EXT2_ET_NO_MEMORY;
  302. el->count++;
  303. } else {
  304. /*
  305. * The count was zero; mark the single bitmap
  306. * and return.
  307. */
  308. zero_count:
  309. ext2fs_mark_inode_bitmap(icount->single, ino);
  310. if (ret)
  311. *ret = 1;
  312. return 0;
  313. }
  314. } else {
  315. /*
  316. * The count is either zero or greater than 1; try to
  317. * find an entry in the list to determine which.
  318. */
  319. el = get_icount_el(icount, ino, 0);
  320. if (!el) {
  321. /* No entry means the count was zero */
  322. goto zero_count;
  323. }
  324. el = get_icount_el(icount, ino, 1);
  325. if (!el)
  326. return EXT2_ET_NO_MEMORY;
  327. el->count++;
  328. }
  329. if (icount->multiple)
  330. ext2fs_mark_inode_bitmap(icount->multiple, ino);
  331. if (ret)
  332. *ret = el->count;
  333. return 0;
  334. }
  335. errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
  336. __u16 *ret)
  337. {
  338. struct ext2_icount_el *el;
  339. if (!ino || (ino > icount->num_inodes))
  340. return EXT2_ET_INVALID_ARGUMENT;
  341. EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
  342. if (ext2fs_test_inode_bitmap(icount->single, ino)) {
  343. ext2fs_unmark_inode_bitmap(icount->single, ino);
  344. if (icount->multiple)
  345. ext2fs_unmark_inode_bitmap(icount->multiple, ino);
  346. else {
  347. el = get_icount_el(icount, ino, 0);
  348. if (el)
  349. el->count = 0;
  350. }
  351. if (ret)
  352. *ret = 0;
  353. return 0;
  354. }
  355. if (icount->multiple &&
  356. !ext2fs_test_inode_bitmap(icount->multiple, ino))
  357. return EXT2_ET_INVALID_ARGUMENT;
  358. el = get_icount_el(icount, ino, 0);
  359. if (!el || el->count == 0)
  360. return EXT2_ET_INVALID_ARGUMENT;
  361. el->count--;
  362. if (el->count == 1)
  363. ext2fs_mark_inode_bitmap(icount->single, ino);
  364. if ((el->count == 0) && icount->multiple)
  365. ext2fs_unmark_inode_bitmap(icount->multiple, ino);
  366. if (ret)
  367. *ret = el->count;
  368. return 0;
  369. }
  370. errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
  371. __u16 count)
  372. {
  373. struct ext2_icount_el *el;
  374. if (!ino || (ino > icount->num_inodes))
  375. return EXT2_ET_INVALID_ARGUMENT;
  376. EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
  377. if (count == 1) {
  378. ext2fs_mark_inode_bitmap(icount->single, ino);
  379. if (icount->multiple)
  380. ext2fs_unmark_inode_bitmap(icount->multiple, ino);
  381. return 0;
  382. }
  383. if (count == 0) {
  384. ext2fs_unmark_inode_bitmap(icount->single, ino);
  385. if (icount->multiple) {
  386. /*
  387. * If the icount->multiple bitmap is enabled,
  388. * we can just clear both bitmaps and we're done
  389. */
  390. ext2fs_unmark_inode_bitmap(icount->multiple, ino);
  391. } else {
  392. el = get_icount_el(icount, ino, 0);
  393. if (el)
  394. el->count = 0;
  395. }
  396. return 0;
  397. }
  398. /*
  399. * Get the icount element
  400. */
  401. el = get_icount_el(icount, ino, 1);
  402. if (!el)
  403. return EXT2_ET_NO_MEMORY;
  404. el->count = count;
  405. ext2fs_unmark_inode_bitmap(icount->single, ino);
  406. if (icount->multiple)
  407. ext2fs_mark_inode_bitmap(icount->multiple, ino);
  408. return 0;
  409. }
  410. ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount)
  411. {
  412. if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT)
  413. return 0;
  414. return icount->size;
  415. }