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