blob: 543187cd53740fdfb39a7fe5ba6111f5120355b5 [file] [log] [blame]
Theodore Ts'o19c78dc1997-04-29 16:17:09 +00001/*
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
Theodore Ts'o4cbe8af1997-08-10 23:07:40 +000012#if HAVE_UNISTD_H
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000013#include <unistd.h>
Theodore Ts'o4cbe8af1997-08-10 23:07:40 +000014#endif
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000015#include <string.h>
16#include <stdio.h>
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000017
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +000018#if EXT2_FLAT_INCLUDES
19#include "ext2_fs.h"
20#else
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000021#include <linux/ext2_fs.h>
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +000022#endif
23
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000024#include "ext2fs.h"
25
26/*
27 * The data storage strategy used by icount relies on the observation
28 * that most inode counts are either zero (for non-allocated inodes),
29 * one (for most files), and only a few that are two or more
30 * (directories and files that are linked to more than one directory).
31 *
32 * Also, e2fsck tends to load the icount data sequentially.
33 *
34 * So, we use an inode bitmap to indicate which inodes have a count of
35 * one, and then use a sorted list to store the counts for inodes
36 * which are greater than one.
37 *
38 * We also use an optional bitmap to indicate which inodes are already
39 * in the sorted list, to speed up the use of this abstraction by
40 * e2fsck's pass 2. Pass 2 increments inode counts as it finds them,
41 * so this extra bitmap avoids searching the sorted list to see if a
42 * particular inode is on the sorted list already.
43 */
44
45struct ext2_icount_el {
46 ino_t ino;
47 __u16 count;
48};
49
50struct ext2_icount {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +000051 errcode_t magic;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000052 ext2fs_inode_bitmap single;
53 ext2fs_inode_bitmap multiple;
54 ino_t count;
55 ino_t size;
56 ino_t num_inodes;
57 int cursor;
58 struct ext2_icount_el *list;
59};
60
61void ext2fs_free_icount(ext2_icount_t icount)
62{
63 if (!icount)
64 return;
65
66 icount->magic = 0;
67 if (icount->list)
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000068 ext2fs_free_mem((void **) &icount->list);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000069 if (icount->single)
70 ext2fs_free_inode_bitmap(icount->single);
71 if (icount->multiple)
72 ext2fs_free_inode_bitmap(icount->multiple);
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000073 ext2fs_free_mem((void **) &icount);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000074}
75
Theodore Ts'o521e3681997-04-29 17:48:10 +000076errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, int size,
77 ext2_icount_t hint, ext2_icount_t *ret)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000078{
79 ext2_icount_t icount;
80 errcode_t retval;
81 size_t bytes;
Theodore Ts'o521e3681997-04-29 17:48:10 +000082 int i;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000083
Theodore Ts'o521e3681997-04-29 17:48:10 +000084 if (hint) {
85 EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT);
86 if (hint->size > size)
Theodore Ts'o3cb6c501997-08-11 20:29:22 +000087 size = (size_t) hint->size;
Theodore Ts'o521e3681997-04-29 17:48:10 +000088 }
89
Theodore Ts'o7b4e4531997-10-26 03:41:24 +000090 retval = ext2fs_get_mem(sizeof(struct ext2_icount), (void **) &icount);
91 if (retval)
92 return retval;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000093 memset(icount, 0, sizeof(struct ext2_icount));
94
95 retval = ext2fs_allocate_inode_bitmap(fs, 0,
96 &icount->single);
97 if (retval)
98 goto errout;
99
100 if (flags & EXT2_ICOUNT_OPT_INCREMENT) {
101 retval = ext2fs_allocate_inode_bitmap(fs, 0,
102 &icount->multiple);
103 if (retval)
104 goto errout;
105 } else
106 icount->multiple = 0;
107
108 if (size) {
109 icount->size = size;
110 } else {
111 /*
112 * Figure out how many special case inode counts we will
113 * have. We know we will need one for each directory;
114 * we also need to reserve some extra room for file links
115 */
116 retval = ext2fs_get_num_dirs(fs, &icount->size);
117 if (retval)
118 goto errout;
119 icount->size += fs->super->s_inodes_count / 50;
120 }
121
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000122 bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el));
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000123#if 0
124 printf("Icount allocated %d entries, %d bytes.\n",
125 icount->size, bytes);
126#endif
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000127 retval = ext2fs_get_mem(bytes, (void **) &icount->list);
128 if (retval)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000129 goto errout;
130 memset(icount->list, 0, bytes);
131
132 icount->magic = EXT2_ET_MAGIC_ICOUNT;
133 icount->count = 0;
134 icount->cursor = 0;
135 icount->num_inodes = fs->super->s_inodes_count;
136
Theodore Ts'o521e3681997-04-29 17:48:10 +0000137 /*
138 * Populate the sorted list with those entries which were
139 * found in the hint icount (since those are ones which will
140 * likely need to be in the sorted list this time around).
141 */
142 if (hint) {
143 for (i=0; i < hint->count; i++)
144 icount->list[i].ino = hint->list[i].ino;
145 icount->count = hint->count;
146 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000147
Theodore Ts'o521e3681997-04-29 17:48:10 +0000148 *ret = icount;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000149 return 0;
150
151errout:
152 ext2fs_free_icount(icount);
153 return(retval);
154}
155
Theodore Ts'o521e3681997-04-29 17:48:10 +0000156errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, int size,
157 ext2_icount_t *ret)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000158{
Theodore Ts'o521e3681997-04-29 17:48:10 +0000159 return ext2fs_create_icount2(fs, flags, size, 0, ret);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000160}
161
162/*
Theodore Ts'o521e3681997-04-29 17:48:10 +0000163 * insert_icount_el() --- Insert a new entry into the sorted list at a
164 * specified position.
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000165 */
Theodore Ts'o521e3681997-04-29 17:48:10 +0000166static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount,
167 ino_t ino, int pos)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000168{
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000169 struct ext2_icount_el *el;
170 errcode_t retval;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000171 ino_t new_size = 0;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000172 int num;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000173
174 if (icount->count >= icount->size) {
175 if (icount->count) {
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000176 new_size = icount->list[(unsigned)icount->count-1].ino;
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +0000177 new_size = (ino_t) (icount->count *
178 ((float) new_size / icount->num_inodes));
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000179 }
180 if (new_size < (icount->size + 100))
181 new_size = icount->size + 100;
182#if 0
183 printf("Reallocating icount %d entries...\n", new_size);
184#endif
Theodore Ts'o76f875d1998-04-27 01:41:13 +0000185 retval = ext2fs_resize_mem((size_t) icount->size *
186 sizeof(struct ext2_icount_el),
187 (size_t) new_size *
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000188 sizeof(struct ext2_icount_el),
189 (void **) &icount->list);
190 if (retval)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000191 return 0;
192 icount->size = new_size;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000193 }
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000194 num = (int) icount->count - pos;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000195 if (num < 0)
196 return 0; /* should never happen */
197 if (num) {
198 memmove(&icount->list[pos+1], &icount->list[pos],
199 sizeof(struct ext2_icount_el) * num);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000200 }
Theodore Ts'o521e3681997-04-29 17:48:10 +0000201 icount->count++;
202 el = &icount->list[pos];
203 el->count = 0;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000204 el->ino = ino;
205 return el;
206}
Theodore Ts'o521e3681997-04-29 17:48:10 +0000207
208/*
209 * get_icount_el() --- given an inode number, try to find icount
210 * information in the sorted list. If the create flag is set,
211 * and we can't find an entry, create one in the sorted list.
212 */
213static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
214 ino_t ino, int create)
215{
216 float range;
217 int low, high, mid;
218 ino_t lowval, highval;
219
220 if (!icount || !icount->list)
221 return 0;
222
223 if (create && ((icount->count == 0) ||
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000224 (ino > icount->list[(unsigned)icount->count-1].ino))) {
225 return insert_icount_el(icount, ino, (unsigned) icount->count);
Theodore Ts'o521e3681997-04-29 17:48:10 +0000226 }
227 if (icount->count == 0)
228 return 0;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000229
Theodore Ts'o521e3681997-04-29 17:48:10 +0000230 if (icount->cursor >= icount->count)
231 icount->cursor = 0;
232 if (ino == icount->list[icount->cursor].ino)
233 return &icount->list[icount->cursor++];
234#if 0
235 printf("Non-cursor get_icount_el: %u\n", ino);
236#endif
237 low = 0;
Theodore Ts'o3cb6c501997-08-11 20:29:22 +0000238 high = (int) icount->count-1;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000239 while (low <= high) {
240#if 0
241 mid = (low+high)/2;
242#else
243 if (low == high)
244 mid = low;
245 else {
246 /* Interpolate for efficiency */
247 lowval = icount->list[low].ino;
248 highval = icount->list[high].ino;
249
250 if (ino < lowval)
251 range = 0;
252 else if (ino > highval)
253 range = 1;
254 else
255 range = ((float) (ino - lowval)) /
256 (highval - lowval);
257 mid = low + ((int) (range * (high-low)));
258 }
259#endif
260 if (ino == icount->list[mid].ino) {
261 icount->cursor = mid+1;
262 return &icount->list[mid];
263 }
264 if (ino < icount->list[mid].ino)
265 high = mid-1;
266 else
267 low = mid+1;
268 }
269 /*
270 * If we need to create a new entry, it should be right at
271 * low (where high will be left at low-1).
272 */
273 if (create)
274 return insert_icount_el(icount, ino, low);
275 return 0;
276}
277
278errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out)
279{
280 errcode_t ret = 0;
281 int i;
282 const char *bad = "bad icount";
283
284 EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
285
286 if (icount->count > icount->size) {
287 fprintf(out, "%s: count > size\n", bad);
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000288 return EXT2_ET_INVALID_ARGUMENT;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000289 }
290 for (i=1; i < icount->count; i++) {
291 if (icount->list[i-1].ino >= icount->list[i].ino) {
Theodore Ts'od163b091997-10-03 17:42:28 +0000292 fprintf(out, "%s: list[%d].ino=%ld, list[%d].ino=%ld\n",
Theodore Ts'o521e3681997-04-29 17:48:10 +0000293 bad, i-1, icount->list[i-1].ino,
294 i, icount->list[i].ino);
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000295 ret = EXT2_ET_INVALID_ARGUMENT;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000296 }
297 }
298 return ret;
299}
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000300
301errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ino_t ino, __u16 *ret)
302{
303 struct ext2_icount_el *el;
304
305 EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
306
Theodore Ts'o521e3681997-04-29 17:48:10 +0000307 if (!ino || (ino > icount->num_inodes))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000308 return EXT2_ET_INVALID_ARGUMENT;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000309
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000310 if (ext2fs_test_inode_bitmap(icount->single, ino)) {
311 *ret = 1;
312 return 0;
313 }
Theodore Ts'o521e3681997-04-29 17:48:10 +0000314 if (icount->multiple &&
315 !ext2fs_test_inode_bitmap(icount->multiple, ino)) {
316 *ret = 0;
317 return 0;
318 }
319 el = get_icount_el(icount, ino, 0);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000320 if (!el) {
321 *ret = 0;
322 return 0;
323 }
324 *ret = el->count;
325 return 0;
326}
327
328errcode_t ext2fs_icount_increment(ext2_icount_t icount, ino_t ino,
329 __u16 *ret)
330{
331 struct ext2_icount_el *el;
332
333 EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
334
Theodore Ts'o521e3681997-04-29 17:48:10 +0000335 if (!ino || (ino > icount->num_inodes))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000336 return EXT2_ET_INVALID_ARGUMENT;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000337
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000338 if (ext2fs_test_inode_bitmap(icount->single, ino)) {
339 /*
340 * If the existing count is 1, then we know there is
Theodore Ts'o521e3681997-04-29 17:48:10 +0000341 * no entry in the list.
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000342 */
Theodore Ts'o521e3681997-04-29 17:48:10 +0000343 el = get_icount_el(icount, ino, 1);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000344 if (!el)
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000345 return EXT2_ET_NO_MEMORY;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000346 ext2fs_unmark_inode_bitmap(icount->single, ino);
347 el->count = 2;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000348 } else if (icount->multiple) {
349 /*
350 * The count is either zero or greater than 1; if the
351 * inode is set in icount->multiple, then there should
352 * be an entry in the list, so find it using
353 * get_icount_el().
354 */
355 if (ext2fs_test_inode_bitmap(icount->multiple, ino)) {
Theodore Ts'o521e3681997-04-29 17:48:10 +0000356 el = get_icount_el(icount, ino, 1);
357 if (!el)
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000358 return EXT2_ET_NO_MEMORY;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000359 el->count++;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000360 } else {
361 /*
362 * The count was zero; mark the single bitmap
363 * and return.
364 */
365 zero_count:
366 ext2fs_mark_inode_bitmap(icount->single, ino);
367 if (ret)
368 *ret = 1;
369 return 0;
370 }
371 } else {
372 /*
373 * The count is either zero or greater than 1; try to
374 * find an entry in the list to determine which.
375 */
Theodore Ts'o521e3681997-04-29 17:48:10 +0000376 el = get_icount_el(icount, ino, 0);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000377 if (!el) {
378 /* No entry means the count was zero */
379 goto zero_count;
380 }
Theodore Ts'o521e3681997-04-29 17:48:10 +0000381 el = get_icount_el(icount, ino, 1);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000382 if (!el)
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000383 return EXT2_ET_NO_MEMORY;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000384 el->count++;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000385 }
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000386 if (icount->multiple)
387 ext2fs_mark_inode_bitmap(icount->multiple, ino);
388 if (ret)
389 *ret = el->count;
390 return 0;
391}
392
393errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ino_t ino,
394 __u16 *ret)
395{
396 struct ext2_icount_el *el;
397
Theodore Ts'o521e3681997-04-29 17:48:10 +0000398 if (!ino || (ino > icount->num_inodes))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000399 return EXT2_ET_INVALID_ARGUMENT;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000400
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000401 EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
402
403 if (ext2fs_test_inode_bitmap(icount->single, ino)) {
404 ext2fs_unmark_inode_bitmap(icount->single, ino);
405 if (icount->multiple)
406 ext2fs_unmark_inode_bitmap(icount->multiple, ino);
407 else {
Theodore Ts'o521e3681997-04-29 17:48:10 +0000408 el = get_icount_el(icount, ino, 0);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000409 if (el)
410 el->count = 0;
411 }
412 if (ret)
413 *ret = 0;
414 return 0;
415 }
416
Theodore Ts'o521e3681997-04-29 17:48:10 +0000417 if (icount->multiple &&
418 !ext2fs_test_inode_bitmap(icount->multiple, ino))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000419 return EXT2_ET_INVALID_ARGUMENT;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000420
421 el = get_icount_el(icount, ino, 0);
422 if (!el || el->count == 0)
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000423 return EXT2_ET_INVALID_ARGUMENT;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000424
425 el->count--;
426 if (el->count == 1)
427 ext2fs_mark_inode_bitmap(icount->single, ino);
428 if ((el->count == 0) && icount->multiple)
429 ext2fs_unmark_inode_bitmap(icount->multiple, ino);
430
431 if (ret)
432 *ret = el->count;
433 return 0;
434}
435
436errcode_t ext2fs_icount_store(ext2_icount_t icount, ino_t ino,
437 __u16 count)
438{
439 struct ext2_icount_el *el;
440
Theodore Ts'o521e3681997-04-29 17:48:10 +0000441 if (!ino || (ino > icount->num_inodes))
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000442 return EXT2_ET_INVALID_ARGUMENT;
Theodore Ts'o521e3681997-04-29 17:48:10 +0000443
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000444 EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
445
446 if (count == 1) {
447 ext2fs_mark_inode_bitmap(icount->single, ino);
448 if (icount->multiple)
449 ext2fs_unmark_inode_bitmap(icount->multiple, ino);
450 return 0;
451 }
452 if (count == 0) {
453 ext2fs_unmark_inode_bitmap(icount->single, ino);
454 if (icount->multiple) {
455 /*
456 * If the icount->multiple bitmap is enabled,
457 * we can just clear both bitmaps and we're done
458 */
459 ext2fs_unmark_inode_bitmap(icount->multiple, ino);
460 } else {
Theodore Ts'o521e3681997-04-29 17:48:10 +0000461 el = get_icount_el(icount, ino, 0);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000462 if (el)
463 el->count = 0;
464 }
465 return 0;
466 }
467
468 /*
469 * Get the icount element
470 */
Theodore Ts'o521e3681997-04-29 17:48:10 +0000471 el = get_icount_el(icount, ino, 1);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000472 if (!el)
Theodore Ts'o1f0b6c11997-10-31 06:07:47 +0000473 return EXT2_ET_NO_MEMORY;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000474 el->count = count;
475 ext2fs_unmark_inode_bitmap(icount->single, ino);
476 if (icount->multiple)
477 ext2fs_mark_inode_bitmap(icount->multiple, ino);
478 return 0;
479}
480
481ino_t ext2fs_get_icount_size(ext2_icount_t icount)
482{
483 if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT)
484 return 0;
485
486 return icount->size;
487}