Whamcloud - gitweb
Add inherit flags for project quota
[tools/e2fsprogs.git] / lib / ext2fs / icount.c
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 Library
8  * General Public License, version 2.
9  * %End-Header%
10  */
11
12 #include "config.h"
13 #if HAVE_UNISTD_H
14 #include <unistd.h>
15 #endif
16 #include <string.h>
17 #include <stdio.h>
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <errno.h>
21
22 #include "ext2_fs.h"
23 #include "ext2fs.h"
24 #include "tdb.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
45 struct ext2_icount_el {
46         ext2_ino_t      ino;
47         __u32           count;
48 };
49
50 struct ext2_icount {
51         errcode_t               magic;
52         ext2fs_inode_bitmap     single;
53         ext2fs_inode_bitmap     multiple;
54         ext2_ino_t              count;
55         ext2_ino_t              size;
56         ext2_ino_t              num_inodes;
57         ext2_ino_t              cursor;
58         struct ext2_icount_el   *list;
59         struct ext2_icount_el   *last_lookup;
60         char                    *tdb_fn;
61         TDB_CONTEXT             *tdb;
62 };
63
64 /*
65  * We now use a 32-bit counter field because it doesn't cost us
66  * anything extra for the in-memory data structure, due to alignment
67  * padding.  But there's no point changing the interface if most of
68  * the time we only care if the number is bigger than 65,000 or not.
69  * So use the following translation function to return a 16-bit count.
70  */
71 #define icount_16_xlate(x) (((x) > 65500) ? 65500 : (x))
72
73 void ext2fs_free_icount(ext2_icount_t icount)
74 {
75         if (!icount)
76                 return;
77
78         icount->magic = 0;
79         if (icount->list)
80                 ext2fs_free_mem(&icount->list);
81         if (icount->single)
82                 ext2fs_free_inode_bitmap(icount->single);
83         if (icount->multiple)
84                 ext2fs_free_inode_bitmap(icount->multiple);
85         if (icount->tdb)
86                 tdb_close(icount->tdb);
87         if (icount->tdb_fn) {
88                 unlink(icount->tdb_fn);
89                 free(icount->tdb_fn);
90         }
91
92         ext2fs_free_mem(&icount);
93 }
94
95 static errcode_t alloc_icount(ext2_filsys fs, int flags, ext2_icount_t *ret)
96 {
97         ext2_icount_t   icount;
98         errcode_t       retval;
99
100         *ret = 0;
101
102         retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount);
103         if (retval)
104                 return retval;
105         memset(icount, 0, sizeof(struct ext2_icount));
106
107         retval = ext2fs_allocate_inode_bitmap(fs, "icount", &icount->single);
108         if (retval)
109                 goto errout;
110
111         if (flags & EXT2_ICOUNT_OPT_INCREMENT) {
112                 retval = ext2fs_allocate_inode_bitmap(fs, "icount_inc",
113                                                       &icount->multiple);
114                 if (retval)
115                         goto errout;
116         } else
117                 icount->multiple = 0;
118
119         icount->magic = EXT2_ET_MAGIC_ICOUNT;
120         icount->num_inodes = fs->super->s_inodes_count;
121
122         *ret = icount;
123         return 0;
124
125 errout:
126         ext2fs_free_icount(icount);
127         return(retval);
128 }
129
130 struct uuid {
131         __u32   time_low;
132         __u16   time_mid;
133         __u16   time_hi_and_version;
134         __u16   clock_seq;
135         __u8    node[6];
136 };
137
138 static void unpack_uuid(void *in, struct uuid *uu)
139 {
140         __u8    *ptr = in;
141         __u32   tmp;
142
143         tmp = *ptr++;
144         tmp = (tmp << 8) | *ptr++;
145         tmp = (tmp << 8) | *ptr++;
146         tmp = (tmp << 8) | *ptr++;
147         uu->time_low = tmp;
148
149         tmp = *ptr++;
150         tmp = (tmp << 8) | *ptr++;
151         uu->time_mid = tmp;
152
153         tmp = *ptr++;
154         tmp = (tmp << 8) | *ptr++;
155         uu->time_hi_and_version = tmp;
156
157         tmp = *ptr++;
158         tmp = (tmp << 8) | *ptr++;
159         uu->clock_seq = tmp;
160
161         memcpy(uu->node, ptr, 6);
162 }
163
164 static void uuid_unparse(void *uu, char *out)
165 {
166         struct uuid uuid;
167
168         unpack_uuid(uu, &uuid);
169         sprintf(out,
170                 "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
171                 uuid.time_low, uuid.time_mid, uuid.time_hi_and_version,
172                 uuid.clock_seq >> 8, uuid.clock_seq & 0xFF,
173                 uuid.node[0], uuid.node[1], uuid.node[2],
174                 uuid.node[3], uuid.node[4], uuid.node[5]);
175 }
176
177 errcode_t ext2fs_create_icount_tdb(ext2_filsys fs, char *tdb_dir,
178                                    int flags, ext2_icount_t *ret)
179 {
180         ext2_icount_t   icount;
181         errcode_t       retval;
182         char            *fn, uuid[40];
183         ext2_ino_t      num_inodes;
184         mode_t          save_umask;
185         int             fd;
186
187         retval = alloc_icount(fs, flags,  &icount);
188         if (retval)
189                 return retval;
190
191         retval = ext2fs_get_mem(strlen(tdb_dir) + 64, &fn);
192         if (retval)
193                 goto errout;
194         uuid_unparse(fs->super->s_uuid, uuid);
195         sprintf(fn, "%s/%s-icount-XXXXXX", tdb_dir, uuid);
196         save_umask = umask(077);
197         fd = mkstemp(fn);
198         if (fd < 0) {
199                 retval = errno;
200                 ext2fs_free_mem(&fn);
201                 goto errout;
202         }
203         icount->tdb_fn = fn;
204         umask(save_umask);
205         /*
206          * This is an overestimate of the size that we will need; the
207          * ideal value is the number of used inodes with a count
208          * greater than 1.  OTOH the times when we really need this is
209          * with the backup programs that use lots of hard links, in
210          * which case the number of inodes in use approaches the ideal
211          * value.
212          */
213         num_inodes = fs->super->s_inodes_count - fs->super->s_free_inodes_count;
214
215         icount->tdb = tdb_open(fn, num_inodes, TDB_NOLOCK | TDB_NOSYNC,
216                                O_RDWR | O_CREAT | O_TRUNC, 0600);
217         close(fd);
218         if (icount->tdb == NULL) {
219                 retval = errno;
220                 goto errout;
221         }
222         *ret = icount;
223         return 0;
224 errout:
225         ext2fs_free_icount(icount);
226         return(retval);
227 }
228
229 errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size,
230                                 ext2_icount_t hint, ext2_icount_t *ret)
231 {
232         ext2_icount_t   icount;
233         errcode_t       retval;
234         size_t          bytes;
235         ext2_ino_t      i;
236
237         if (hint) {
238                 EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT);
239                 if (hint->size > size)
240                         size = (size_t) hint->size;
241         }
242
243         retval = alloc_icount(fs, flags, &icount);
244         if (retval)
245                 return retval;
246
247         if (size) {
248                 icount->size = size;
249         } else {
250                 /*
251                  * Figure out how many special case inode counts we will
252                  * have.  We know we will need one for each directory;
253                  * we also need to reserve some extra room for file links
254                  */
255                 retval = ext2fs_get_num_dirs(fs, &icount->size);
256                 if (retval)
257                         goto errout;
258                 icount->size += fs->super->s_inodes_count / 50;
259         }
260
261         bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el));
262 #if 0
263         printf("Icount allocated %u entries, %d bytes.\n",
264                icount->size, bytes);
265 #endif
266         retval = ext2fs_get_array(icount->size, sizeof(struct ext2_icount_el),
267                          &icount->list);
268         if (retval)
269                 goto errout;
270         memset(icount->list, 0, bytes);
271
272         icount->count = 0;
273         icount->cursor = 0;
274
275         /*
276          * Populate the sorted list with those entries which were
277          * found in the hint icount (since those are ones which will
278          * likely need to be in the sorted list this time around).
279          */
280         if (hint) {
281                 for (i=0; i < hint->count; i++)
282                         icount->list[i].ino = hint->list[i].ino;
283                 icount->count = hint->count;
284         }
285
286         *ret = icount;
287         return 0;
288
289 errout:
290         ext2fs_free_icount(icount);
291         return(retval);
292 }
293
294 errcode_t ext2fs_create_icount(ext2_filsys fs, int flags,
295                                unsigned int size,
296                                ext2_icount_t *ret)
297 {
298         return ext2fs_create_icount2(fs, flags, size, 0, ret);
299 }
300
301 /*
302  * insert_icount_el() --- Insert a new entry into the sorted list at a
303  *      specified position.
304  */
305 static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount,
306                                             ext2_ino_t ino, int pos)
307 {
308         struct ext2_icount_el   *el;
309         errcode_t               retval;
310         ext2_ino_t              new_size = 0;
311         int                     num;
312
313         if (icount->last_lookup && icount->last_lookup->ino == ino)
314                 return icount->last_lookup;
315
316         if (icount->count >= icount->size) {
317                 if (icount->count) {
318                         new_size = icount->list[(unsigned)icount->count-1].ino;
319                         new_size = (ext2_ino_t) (icount->count *
320                                 ((float) icount->num_inodes / new_size));
321                 }
322                 if (new_size < (icount->size + 100))
323                         new_size = icount->size + 100;
324 #if 0
325                 printf("Reallocating icount %u entries...\n", new_size);
326 #endif
327                 retval = ext2fs_resize_mem((size_t) icount->size *
328                                            sizeof(struct ext2_icount_el),
329                                            (size_t) new_size *
330                                            sizeof(struct ext2_icount_el),
331                                            &icount->list);
332                 if (retval)
333                         return 0;
334                 icount->size = new_size;
335         }
336         num = (int) icount->count - pos;
337         if (num < 0)
338                 return 0;       /* should never happen */
339         if (num) {
340                 memmove(&icount->list[pos+1], &icount->list[pos],
341                         sizeof(struct ext2_icount_el) * num);
342         }
343         icount->count++;
344         el = &icount->list[pos];
345         el->count = 0;
346         el->ino = ino;
347         icount->last_lookup = el;
348         return el;
349 }
350
351 /*
352  * get_icount_el() --- given an inode number, try to find icount
353  *      information in the sorted list.  If the create flag is set,
354  *      and we can't find an entry, create one in the sorted list.
355  */
356 static struct ext2_icount_el *get_icount_el(ext2_icount_t icount,
357                                             ext2_ino_t ino, int create)
358 {
359         int     low, high, mid;
360
361         if (!icount || !icount->list)
362                 return 0;
363
364         if (create && ((icount->count == 0) ||
365                        (ino > icount->list[(unsigned)icount->count-1].ino))) {
366                 return insert_icount_el(icount, ino, (unsigned) icount->count);
367         }
368         if (icount->count == 0)
369                 return 0;
370
371         if (icount->cursor >= icount->count)
372                 icount->cursor = 0;
373         if (ino == icount->list[icount->cursor].ino)
374                 return &icount->list[icount->cursor++];
375 #if 0
376         printf("Non-cursor get_icount_el: %u\n", ino);
377 #endif
378         low = 0;
379         high = (int) icount->count-1;
380         while (low <= high) {
381                 mid = ((unsigned)low + (unsigned)high) >> 1;
382                 if (ino == icount->list[mid].ino) {
383                         icount->cursor = mid+1;
384                         return &icount->list[mid];
385                 }
386                 if (ino < icount->list[mid].ino)
387                         high = mid-1;
388                 else
389                         low = mid+1;
390         }
391         /*
392          * If we need to create a new entry, it should be right at
393          * low (where high will be left at low-1).
394          */
395         if (create)
396                 return insert_icount_el(icount, ino, low);
397         return 0;
398 }
399
400 static errcode_t set_inode_count(ext2_icount_t icount, ext2_ino_t ino,
401                                  __u32 count)
402 {
403         struct ext2_icount_el   *el;
404         TDB_DATA key, data;
405
406         if (icount->tdb) {
407                 key.dptr = (unsigned char *) &ino;
408                 key.dsize = sizeof(ext2_ino_t);
409                 data.dptr = (unsigned char *) &count;
410                 data.dsize = sizeof(__u32);
411                 if (count) {
412                         if (tdb_store(icount->tdb, key, data, TDB_REPLACE))
413                                 return tdb_error(icount->tdb) +
414                                         EXT2_ET_TDB_SUCCESS;
415                 } else {
416                         if (tdb_delete(icount->tdb, key))
417                                 return tdb_error(icount->tdb) +
418                                         EXT2_ET_TDB_SUCCESS;
419                 }
420                 return 0;
421         }
422
423         el = get_icount_el(icount, ino, 1);
424         if (!el)
425                 return EXT2_ET_NO_MEMORY;
426
427         el->count = count;
428         return 0;
429 }
430
431 static errcode_t get_inode_count(ext2_icount_t icount, ext2_ino_t ino,
432                                  __u32 *count)
433 {
434         struct ext2_icount_el   *el;
435         TDB_DATA key, data;
436
437         if (icount->tdb) {
438                 key.dptr = (unsigned char *) &ino;
439                 key.dsize = sizeof(ext2_ino_t);
440
441                 data = tdb_fetch(icount->tdb, key);
442                 if (data.dptr == NULL) {
443                         *count = 0;
444                         return tdb_error(icount->tdb) + EXT2_ET_TDB_SUCCESS;
445                 }
446
447                 *count = *((__u32 *) data.dptr);
448                 free(data.dptr);
449                 return 0;
450         }
451         el = get_icount_el(icount, ino, 0);
452         if (!el) {
453                 *count = 0;
454                 return ENOENT;
455         }
456
457         *count = el->count;
458         return 0;
459 }
460
461 errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out)
462 {
463         errcode_t       ret = 0;
464         unsigned int    i;
465         const char *bad = "bad icount";
466
467         EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
468
469         if (icount->count > icount->size) {
470                 fprintf(out, "%s: count > size\n", bad);
471                 return EXT2_ET_INVALID_ARGUMENT;
472         }
473         for (i=1; i < icount->count; i++) {
474                 if (icount->list[i-1].ino >= icount->list[i].ino) {
475                         fprintf(out, "%s: list[%d].ino=%u, list[%d].ino=%u\n",
476                                 bad, i-1, icount->list[i-1].ino,
477                                 i, icount->list[i].ino);
478                         ret = EXT2_ET_INVALID_ARGUMENT;
479                 }
480         }
481         return ret;
482 }
483
484 errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret)
485 {
486         __u32   val;
487         EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
488
489         if (!ino || (ino > icount->num_inodes))
490                 return EXT2_ET_INVALID_ARGUMENT;
491
492         if (ext2fs_test_inode_bitmap2(icount->single, ino)) {
493                 *ret = 1;
494                 return 0;
495         }
496         if (icount->multiple &&
497             !ext2fs_test_inode_bitmap2(icount->multiple, ino)) {
498                 *ret = 0;
499                 return 0;
500         }
501         get_inode_count(icount, ino, &val);
502         *ret = icount_16_xlate(val);
503         return 0;
504 }
505
506 errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino,
507                                   __u16 *ret)
508 {
509         __u32                   curr_value;
510
511         EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
512
513         if (!ino || (ino > icount->num_inodes))
514                 return EXT2_ET_INVALID_ARGUMENT;
515
516         if (ext2fs_test_inode_bitmap2(icount->single, ino)) {
517                 /*
518                  * If the existing count is 1, then we know there is
519                  * no entry in the list.
520                  */
521                 if (set_inode_count(icount, ino, 2))
522                         return EXT2_ET_NO_MEMORY;
523                 curr_value = 2;
524                 ext2fs_unmark_inode_bitmap2(icount->single, ino);
525         } else if (icount->multiple) {
526                 /*
527                  * The count is either zero or greater than 1; if the
528                  * inode is set in icount->multiple, then there should
529                  * be an entry in the list, so we need to fix it.
530                  */
531                 if (ext2fs_test_inode_bitmap2(icount->multiple, ino)) {
532                         get_inode_count(icount, ino, &curr_value);
533                         curr_value++;
534                         if (set_inode_count(icount, ino, curr_value))
535                                 return EXT2_ET_NO_MEMORY;
536                 } else {
537                         /*
538                          * The count was zero; mark the single bitmap
539                          * and return.
540                          */
541                         ext2fs_mark_inode_bitmap2(icount->single, ino);
542                         if (ret)
543                                 *ret = 1;
544                         return 0;
545                 }
546         } else {
547                 /*
548                  * The count is either zero or greater than 1; try to
549                  * find an entry in the list to determine which.
550                  */
551                 get_inode_count(icount, ino, &curr_value);
552                 curr_value++;
553                 if (set_inode_count(icount, ino, curr_value))
554                         return EXT2_ET_NO_MEMORY;
555         }
556         if (icount->multiple)
557                 ext2fs_mark_inode_bitmap2(icount->multiple, ino);
558         if (ret)
559                 *ret = icount_16_xlate(curr_value);
560         return 0;
561 }
562
563 errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
564                                   __u16 *ret)
565 {
566         __u32                   curr_value;
567
568         if (!ino || (ino > icount->num_inodes))
569                 return EXT2_ET_INVALID_ARGUMENT;
570
571         EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
572
573         if (ext2fs_test_inode_bitmap2(icount->single, ino)) {
574                 ext2fs_unmark_inode_bitmap2(icount->single, ino);
575                 if (icount->multiple)
576                         ext2fs_unmark_inode_bitmap2(icount->multiple, ino);
577                 else {
578                         set_inode_count(icount, ino, 0);
579                 }
580                 if (ret)
581                         *ret = 0;
582                 return 0;
583         }
584
585         if (icount->multiple &&
586             !ext2fs_test_inode_bitmap2(icount->multiple, ino))
587                 return EXT2_ET_INVALID_ARGUMENT;
588
589         get_inode_count(icount, ino, &curr_value);
590         if (!curr_value)
591                 return EXT2_ET_INVALID_ARGUMENT;
592         curr_value--;
593         if (set_inode_count(icount, ino, curr_value))
594                 return EXT2_ET_NO_MEMORY;
595
596         if (curr_value == 1)
597                 ext2fs_mark_inode_bitmap2(icount->single, ino);
598         if ((curr_value == 0) && icount->multiple)
599                 ext2fs_unmark_inode_bitmap2(icount->multiple, ino);
600
601         if (ret)
602                 *ret = icount_16_xlate(curr_value);
603         return 0;
604 }
605
606 errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
607                               __u16 count)
608 {
609         if (!ino || (ino > icount->num_inodes))
610                 return EXT2_ET_INVALID_ARGUMENT;
611
612         EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);
613
614         if (count == 1) {
615                 ext2fs_mark_inode_bitmap2(icount->single, ino);
616                 if (icount->multiple)
617                         ext2fs_unmark_inode_bitmap2(icount->multiple, ino);
618                 return 0;
619         }
620         if (count == 0) {
621                 ext2fs_unmark_inode_bitmap2(icount->single, ino);
622                 if (icount->multiple) {
623                         /*
624                          * If the icount->multiple bitmap is enabled,
625                          * we can just clear both bitmaps and we're done
626                          */
627                         ext2fs_unmark_inode_bitmap2(icount->multiple, ino);
628                 } else
629                         set_inode_count(icount, ino, 0);
630                 return 0;
631         }
632
633         if (set_inode_count(icount, ino, count))
634                 return EXT2_ET_NO_MEMORY;
635         ext2fs_unmark_inode_bitmap2(icount->single, ino);
636         if (icount->multiple)
637                 ext2fs_mark_inode_bitmap2(icount->multiple, ino);
638         return 0;
639 }
640
641 ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount)
642 {
643         if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT)
644                 return 0;
645
646         return icount->size;
647 }
648
649 #ifdef DEBUG
650
651 ext2_filsys     test_fs;
652 ext2_icount_t   icount;
653
654 #define EXIT            0x00
655 #define FETCH           0x01
656 #define STORE           0x02
657 #define INCREMENT       0x03
658 #define DECREMENT       0x04
659
660 struct test_program {
661         int             cmd;
662         ext2_ino_t      ino;
663         __u16           arg;
664         __u16           expected;
665 };
666
667 struct test_program prog[] = {
668         { STORE, 42, 42, 42 },
669         { STORE, 1,  1, 1 },
670         { STORE, 2,  2, 2 },
671         { STORE, 3,  3, 3 },
672         { STORE, 10, 1, 1 },
673         { STORE, 42, 0, 0 },
674         { INCREMENT, 5, 0, 1 },
675         { INCREMENT, 5, 0, 2 },
676         { INCREMENT, 5, 0, 3 },
677         { INCREMENT, 5, 0, 4 },
678         { DECREMENT, 5, 0, 3 },
679         { DECREMENT, 5, 0, 2 },
680         { DECREMENT, 5, 0, 1 },
681         { DECREMENT, 5, 0, 0 },
682         { FETCH, 10, 0, 1 },
683         { FETCH, 1, 0, 1 },
684         { FETCH, 2, 0, 2 },
685         { FETCH, 3, 0, 3 },
686         { INCREMENT, 1, 0, 2 },
687         { DECREMENT, 2, 0, 1 },
688         { DECREMENT, 2, 0, 0 },
689         { FETCH, 12, 0, 0 },
690         { EXIT, 0, 0, 0 }
691 };
692
693 struct test_program extended[] = {
694         { STORE, 1,  1, 1 },
695         { STORE, 2,  2, 2 },
696         { STORE, 3,  3, 3 },
697         { STORE, 4,  4, 4 },
698         { STORE, 5,  5, 5 },
699         { STORE, 6,  1, 1 },
700         { STORE, 7,  2, 2 },
701         { STORE, 8,  3, 3 },
702         { STORE, 9,  4, 4 },
703         { STORE, 10, 5, 5 },
704         { STORE, 11, 1, 1 },
705         { STORE, 12, 2, 2 },
706         { STORE, 13, 3, 3 },
707         { STORE, 14, 4, 4 },
708         { STORE, 15, 5, 5 },
709         { STORE, 16, 1, 1 },
710         { STORE, 17, 2, 2 },
711         { STORE, 18, 3, 3 },
712         { STORE, 19, 4, 4 },
713         { STORE, 20, 5, 5 },
714         { STORE, 21, 1, 1 },
715         { STORE, 22, 2, 2 },
716         { STORE, 23, 3, 3 },
717         { STORE, 24, 4, 4 },
718         { STORE, 25, 5, 5 },
719         { STORE, 26, 1, 1 },
720         { STORE, 27, 2, 2 },
721         { STORE, 28, 3, 3 },
722         { STORE, 29, 4, 4 },
723         { STORE, 30, 5, 5 },
724         { EXIT, 0, 0, 0 }
725 };
726
727 /*
728  * Setup the variables for doing the inode scan test.
729  */
730 static void setup(void)
731 {
732         errcode_t       retval;
733         struct ext2_super_block param;
734
735         initialize_ext2_error_table();
736
737         memset(&param, 0, sizeof(param));
738         ext2fs_blocks_count_set(&param, 12000);
739
740         retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, &param,
741                                    test_io_manager, &test_fs);
742         if (retval) {
743                 com_err("setup", retval,
744                         "while initializing filesystem");
745                 exit(1);
746         }
747         retval = ext2fs_allocate_tables(test_fs);
748         if (retval) {
749                 com_err("setup", retval,
750                         "while allocating tables for test filesystem");
751                 exit(1);
752         }
753 }
754
755 int run_test(int flags, int size, char *dir, struct test_program *prog)
756 {
757         errcode_t       retval;
758         ext2_icount_t   icount;
759         struct test_program *pc;
760         __u16           result;
761         int             problem = 0;
762
763         if (dir) {
764                 retval = ext2fs_create_icount_tdb(test_fs, dir,
765                                                   flags, &icount);
766                 if (retval) {
767                         com_err("run_test", retval,
768                                 "while creating icount using tdb");
769                         exit(1);
770                 }
771         } else {
772                 retval = ext2fs_create_icount2(test_fs, flags, size, 0,
773                                                &icount);
774                 if (retval) {
775                         com_err("run_test", retval, "while creating icount");
776                         exit(1);
777                 }
778         }
779         for (pc = prog; pc->cmd != EXIT; pc++) {
780                 switch (pc->cmd) {
781                 case FETCH:
782                         printf("icount_fetch(%u) = ", pc->ino);
783                         break;
784                 case STORE:
785                         retval = ext2fs_icount_store(icount, pc->ino, pc->arg);
786                         if (retval) {
787                                 com_err("run_test", retval,
788                                         "while calling icount_store");
789                                 exit(1);
790                         }
791                         printf("icount_store(%u, %u) = ", pc->ino, pc->arg);
792                         break;
793                 case INCREMENT:
794                         retval = ext2fs_icount_increment(icount, pc->ino, 0);
795                         if (retval) {
796                                 com_err("run_test", retval,
797                                         "while calling icount_increment");
798                                 exit(1);
799                         }
800                         printf("icount_increment(%u) = ", pc->ino);
801                         break;
802                 case DECREMENT:
803                         retval = ext2fs_icount_decrement(icount, pc->ino, 0);
804                         if (retval) {
805                                 com_err("run_test", retval,
806                                         "while calling icount_decrement");
807                                 exit(1);
808                         }
809                         printf("icount_decrement(%u) = ", pc->ino);
810                         break;
811                 }
812                 retval = ext2fs_icount_fetch(icount, pc->ino, &result);
813                 if (retval) {
814                         com_err("run_test", retval,
815                                 "while calling icount_fetch");
816                         exit(1);
817                 }
818                 printf("%u (%s)\n", result, (result == pc->expected) ?
819                        "OK" : "NOT OK");
820                 if (result != pc->expected)
821                         problem++;
822         }
823         printf("icount size is %u\n", ext2fs_get_icount_size(icount));
824         retval = ext2fs_icount_validate(icount, stdout);
825         if (retval) {
826                 com_err("run_test", retval, "while calling icount_validate");
827                 exit(1);
828         }
829         ext2fs_free_icount(icount);
830         return problem;
831 }
832
833
834 int main(int argc, char **argv)
835 {
836         int failed = 0;
837
838         setup();
839         printf("Standard icount run:\n");
840         failed += run_test(0, 0, 0, prog);
841         printf("\nMultiple bitmap test:\n");
842         failed += run_test(EXT2_ICOUNT_OPT_INCREMENT, 0, 0, prog);
843         printf("\nResizing icount:\n");
844         failed += run_test(0, 3, 0, extended);
845         printf("\nStandard icount run with tdb:\n");
846         failed += run_test(0, 0, ".", prog);
847         printf("\nMultiple bitmap test with tdb:\n");
848         failed += run_test(EXT2_ICOUNT_OPT_INCREMENT, 0, ".", prog);
849         if (failed)
850                 printf("FAILED!\n");
851         return failed;
852 }
853 #endif