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