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