3
0

icount.c 11 KB

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