Initial public busybox upstream commit
[busybox4maemo] / e2fsprogs / old_e2fsprogs / ext2fs / icount.c
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
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
41 struct ext2_icount_el {
42         ext2_ino_t      ino;
43         __u16   count;
44 };
45
46 struct 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
57 void ext2fs_free_icount(ext2_icount_t icount)
58 {
59         if (!icount)
60                 return;
61
62         icount->magic = 0;
63         ext2fs_free_mem(&icount->list);
64         ext2fs_free_inode_bitmap(icount->single);
65         ext2fs_free_inode_bitmap(icount->multiple);
66         ext2fs_free_mem(&icount);
67 }
68
69 errcode_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         }
82
83         retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount);
84         if (retval)
85                 return retval;
86         memset(icount, 0, sizeof(struct ext2_icount));
87
88         retval = ext2fs_allocate_inode_bitmap(fs, 0,
89                                               &icount->single);
90         if (retval)
91                 goto errout;
92
93         if (flags & EXT2_ICOUNT_OPT_INCREMENT) {
94                 retval = ext2fs_allocate_inode_bitmap(fs, 0,
95                                                       &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         }
114
115         bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el));
116         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
140 errout:
141         ext2fs_free_icount(icount);
142         return retval;
143 }
144
145 errcode_t ext2fs_create_icount(ext2_filsys fs, int flags,
146                                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
154  *      specified position.
155  */
156 static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount,
157                                             ext2_ino_t ino, int pos)
158 {
159         struct ext2_icount_el   *el;
160         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;
167                         new_size = (ext2_ino_t) (icount->count *
168                                 ((float) icount->num_inodes / new_size));
169                 }
170                 if (new_size < (icount->size + 100))
171                         new_size = icount->size + 100;
172                 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
197  *      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.
199  */
200 static 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;
216
217         if (icount->cursor >= icount->count)
218                 icount->cursor = 0;
219         if (ino == icount->list[icount->cursor].ino)
220                 return &icount->list[icount->cursor++];
221         low = 0;
222         high = (int) icount->count-1;
223         while (low <= high) {
224                 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;
235                         else
236                                 range = ((float) (ino - lowval)) /
237                                         (highval - lowval);
238                         mid = low + ((int) (range * (high-low)));
239                 }
240                 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
258 errcode_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";
263
264         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
281 errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret)
282 {
283         struct ext2_icount_el   *el;
284
285         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
308 errcode_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
373 errcode_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;
400
401         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
416 errcode_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
461 ext2_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 }