]> code.ossystems Code Review - openembedded-core.git/blob
79e82c8c73ca341aa93fa24cb880b91cc10b1823
[openembedded-core.git] /
1 Upstream-Status: inappropriate
2
3 From 29a36b0b91ee009ee4219934c7a70278b1361834 Mon Sep 17 00:00:00 2001
4 From: Corey Minyard <cminyard@mvista.com>
5 Date: Sun, 5 Jun 2011 15:24:57 -0500
6 Subject: [PATCH 10/19] Convert over to keeping the filesystem on disk
7
8 This makes the actual filesystem be on disk and not in memory.  It
9 adds caching of the filesystem data to help keep oft-accessed blocks
10 in memory and byteswapped.
11 ---
12  cache.h     |  128 ++++++++++++++++++++
13  genext2fs.c |  377 +++++++++++++++++++++++++++++++++++++++++++++++++----------
14  list.h      |   78 ++++++++++++
15  3 files changed, 521 insertions(+), 62 deletions(-)
16  create mode 100644 cache.h
17  create mode 100644 list.h
18
19 diff --git a/cache.h b/cache.h
20 new file mode 100644
21 index 0000000..5275be6
22 --- /dev/null
23 +++ b/cache.h
24 @@ -0,0 +1,128 @@
25 +#ifndef __CACHE_H__
26 +#define __CACHE_H__
27 +
28 +#include "list.h"
29 +
30 +#define CACHE_LISTS 256
31 +
32 +typedef struct
33 +{
34 +       list_elem link;
35 +       list_elem lru_link;
36 +} cache_link;
37 +
38 +typedef struct
39 +{
40 +       /* LRU list holds unused items */
41 +       unsigned int lru_entries;
42 +       list_elem lru_list;
43 +       unsigned int max_free_entries;
44 +
45 +       unsigned int entries;
46 +       list_elem lists[CACHE_LISTS];
47 +       unsigned int (*elem_val)(cache_link *elem);
48 +       void (*freed)(cache_link *elem);
49 +} listcache;
50 +
51 +static inline void
52 +cache_add(listcache *c, cache_link *elem)
53 +{
54 +       unsigned int hash = c->elem_val(elem) % CACHE_LISTS;
55 +       int delcount = c->lru_entries - c->max_free_entries;
56 +
57 +       if (delcount > 0) {
58 +               /* Delete some unused items. */
59 +               list_elem *lru, *next;
60 +               cache_link *l;
61 +               list_for_each_elem_safe(&c->lru_list, lru, next) {
62 +                       l = container_of(lru, cache_link, lru_link);
63 +                       list_del(lru);
64 +                       list_del(&l->link);
65 +                       c->entries--;
66 +                       c->lru_entries--;
67 +                       c->freed(l);
68 +                       delcount--;
69 +                       if (delcount <= 0)
70 +                               break;
71 +               }
72 +       }
73 +
74 +       c->entries++;
75 +       list_item_init(&elem->lru_link); /* Mark it not in the LRU list */
76 +       list_add_after(&c->lists[hash], &elem->link);
77 +}
78 +
79 +static inline void
80 +cache_item_set_unused(listcache *c, cache_link *elem)
81 +{
82 +       list_add_before(&c->lru_list, &elem->lru_link);
83 +       c->lru_entries++;
84 +}
85 +
86 +static inline cache_link *
87 +cache_find(listcache *c, unsigned int val)
88 +{
89 +       unsigned int hash = val % CACHE_LISTS;
90 +       list_elem *elem;
91 +
92 +       list_for_each_elem(&c->lists[hash], elem) {
93 +               cache_link *l = container_of(elem, cache_link, link);
94 +               if (c->elem_val(l) == val) {
95 +                       if (!list_empty(&l->lru_link)) {
96 +                               /* It's in the unused list, remove it. */
97 +                               list_del(&l->lru_link);
98 +                               list_item_init(&l->lru_link);
99 +                               c->lru_entries--;
100 +                       }
101 +                       return l;
102 +               }
103 +       }
104 +       return NULL;
105 +}
106 +
107 +static inline int
108 +cache_flush(listcache *c)
109 +{
110 +       list_elem *elem, *next;
111 +       cache_link *l;
112 +       int i;
113 +
114 +       list_for_each_elem_safe(&c->lru_list, elem, next) {
115 +               l = container_of(elem, cache_link, lru_link);
116 +               list_del(elem);
117 +               list_del(&l->link);
118 +               c->entries--;
119 +               c->lru_entries--;
120 +               c->freed(l);
121 +       }
122 +
123 +       for (i = 0; i < CACHE_LISTS; i++) {
124 +               list_for_each_elem_safe(&c->lists[i], elem, next) {
125 +                       l = container_of(elem, cache_link, link);
126 +                       list_del(&l->link);
127 +                       c->entries--;
128 +                       c->freed(l);
129 +               }
130 +       }
131 +
132 +       return c->entries || c->lru_entries;
133 +}
134 +
135 +static inline void
136 +cache_init(listcache *c, unsigned int max_free_entries,
137 +          unsigned int (*elem_val)(cache_link *elem),
138 +          void (*freed)(cache_link *elem))
139 +{
140 +       int i;
141 +
142 +       c->entries = 0;
143 +       c->lru_entries = 0;
144 +       c->max_free_entries = max_free_entries;
145 +       list_init(&c->lru_list);
146 +       for (i = 0; i < CACHE_LISTS; i++)
147 +               list_init(&c->lists[i]);
148 +       c->elem_val = elem_val;
149 +       c->freed = freed;
150 +}
151 +
152 +#endif /* __CACHE_H__ */
153 diff --git a/genext2fs.c b/genext2fs.c
154 index 51403a2..f79438d 100644
155 --- a/genext2fs.c
156 +++ b/genext2fs.c
157 @@ -142,6 +142,8 @@
158  # include <limits.h>
159  #endif
160  
161 +#include "cache.h"
162 +
163  struct stats {
164         unsigned long nblocks;
165         unsigned long ninodes;
166 @@ -600,6 +602,7 @@ struct hdlinks_s
167  #if BLOCKSIZE == 1024
168  typedef struct
169  {
170 +       FILE *f;
171         uint8 *data;
172         superblock *sb;
173         groupdescriptor *gd;
174 @@ -607,6 +610,10 @@ typedef struct
175         int swapit;
176         int32 hdlink_cnt;
177         struct hdlinks_s hdlinks;
178 +
179 +       listcache blks;
180 +       listcache inodes;
181 +       listcache blkmaps;
182  } filesystem;
183  #else
184  #error UNHANDLED BLOCKSIZE
185 @@ -848,45 +855,150 @@ allocated(block b, uint32 item)
186  // by the user.
187  typedef struct
188  {
189 -       int dummy;
190 +       cache_link link;
191 +
192 +       filesystem *fs;
193 +       uint32 blk;
194 +       uint8 *b;
195 +       uint32 usecount;
196  } blk_info;
197  
198 +#define MAX_FREE_CACHE_BLOCKS 100
199 +
200 +static uint32
201 +blk_elem_val(cache_link *elem)
202 +{
203 +       blk_info *bi = container_of(elem, blk_info, link);
204 +       return bi->blk;
205 +}
206 +
207 +static void
208 +blk_freed(cache_link *elem)
209 +{
210 +       blk_info *bi = container_of(elem, blk_info, link);
211 +
212 +       if (fseeko(bi->fs->f, ((off_t) bi->blk) * BLOCKSIZE, SEEK_SET))
213 +               perror_msg_and_die("fseek");
214 +       if (fwrite(bi->b, BLOCKSIZE, 1, bi->fs->f) != 1)
215 +               perror_msg_and_die("get_blk: write");
216 +       free(bi->b);
217 +       free(bi);
218 +}
219 +
220  // Return a given block from a filesystem.  Make sure to call
221  // put_blk when you are done with it.
222  static inline uint8 *
223  get_blk(filesystem *fs, uint32 blk, blk_info **rbi)
224  {
225 -       return fs->data + blk*BLOCKSIZE;
226 +       cache_link *curr;
227 +       blk_info *bi;
228 +
229 +       if (blk < fs->nheadblocks)
230 +               error_msg_and_die("Internal error, request for head block");
231 +       if (blk >= fs->sb->s_blocks_count)
232 +               error_msg_and_die("Internal error, block out of range");
233 +
234 +       curr = cache_find(&fs->blks, blk);
235 +       if (curr) {
236 +               bi = container_of(curr, blk_info, link);
237 +               bi->usecount++;
238 +               goto out;
239 +       }
240 +
241 +       bi = malloc(sizeof(*bi));
242 +       if (!bi)
243 +               error_msg_and_die("get_blk: out of memory");
244 +       bi->fs = fs;
245 +       bi->blk = blk;
246 +       bi->usecount = 1;
247 +       bi->b = malloc(BLOCKSIZE);
248 +       if (!bi->b)
249 +               error_msg_and_die("get_blk: out of memory");
250 +       cache_add(&fs->blks, &bi->link);
251 +       if (fseeko(fs->f, ((off_t) blk) * BLOCKSIZE, SEEK_SET))
252 +               perror_msg_and_die("fseek");
253 +       if (fread(bi->b, BLOCKSIZE, 1, fs->f) != 1) {
254 +               if (ferror(fs->f))
255 +                       perror_msg_and_die("fread");
256 +               memset(bi->b, 0, BLOCKSIZE);
257 +       }
258 +
259 +out:
260 +       *rbi = bi;
261 +       return bi->b;
262  }
263  
264  static inline void
265  put_blk(blk_info *bi)
266  {
267 +       if (bi->usecount == 0)
268 +               error_msg_and_die("Internal error: put_blk usecount zero");
269 +       bi->usecount--;
270 +       if (bi->usecount == 0)
271 +               /* Free happens in the cache code */
272 +               cache_item_set_unused(&bi->fs->blks, &bi->link);
273  }
274  
275  // Used by get_blkmap/put_blkmap to hold information about an block map
276  // owned by the user.
277  typedef struct
278  {
279 +       cache_link link;
280 +
281         filesystem *fs;
282 +       uint32 blk;
283         uint8 *b;
284         blk_info *bi;
285 +       uint32 usecount;
286  } blkmap_info;
287  
288 +#define MAX_FREE_CACHE_BLOCKMAPS 100
289 +
290 +static uint32
291 +blkmap_elem_val(cache_link *elem)
292 +{
293 +       blkmap_info *bmi = container_of(elem, blkmap_info, link);
294 +       return bmi->blk;
295 +}
296 +
297 +static void
298 +blkmap_freed(cache_link *elem)
299 +{
300 +       blkmap_info *bmi = container_of(elem, blkmap_info, link);
301 +
302 +       if (bmi->fs->swapit)
303 +               swap_block(bmi->b);
304 +       put_blk(bmi->bi);
305 +       free(bmi);
306 +}
307 +
308  // Return a given block map from a filesystem.  Make sure to call
309  // put_blkmap when you are done with it.
310  static inline uint32 *
311  get_blkmap(filesystem *fs, uint32 blk, blkmap_info **rbmi)
312  {
313         blkmap_info *bmi;
314 +       cache_link *curr;
315 +
316 +       curr = cache_find(&fs->blkmaps, blk);
317 +       if (curr) {
318 +               bmi = container_of(curr, blkmap_info, link);
319 +               bmi->usecount++;
320 +               goto out;
321 +       }
322  
323         bmi = malloc(sizeof(*bmi));
324         if (!bmi)
325                 error_msg_and_die("get_blkmap: out of memory");
326         bmi->fs = fs;
327 +       bmi->blk = blk;
328         bmi->b = get_blk(fs, blk, &bmi->bi);
329 -       if (bmi->fs->swapit)
330 +       bmi->usecount = 1;
331 +       cache_add(&fs->blkmaps, &bmi->link);
332 +
333 +       if (fs->swapit)
334                 swap_block(bmi->b);
335 +out:
336         *rbmi = bmi;
337         return (uint32 *) bmi->b;
338  }
339 @@ -894,42 +1006,83 @@ get_blkmap(filesystem *fs, uint32 blk, blkmap_info **rbmi)
340  static inline void
341  put_blkmap(blkmap_info *bmi)
342  {
343 -       if (bmi->fs->swapit)
344 -               swap_block(bmi->b);
345 -       put_blk(bmi->bi);
346 -       free(bmi);
347 +       if (bmi->usecount == 0)
348 +               error_msg_and_die("Internal error: put_blkmap usecount zero");
349 +
350 +       bmi->usecount--;
351 +       if (bmi->usecount == 0)
352 +               /* Free happens in the cache code */
353 +               cache_item_set_unused(&bmi->fs->blkmaps, &bmi->link);
354  }
355  
356  // Used by get_nod/put_nod to hold information about an inode owned
357  // by the user.
358  typedef struct
359  {
360 +       cache_link link;
361 +
362         filesystem *fs;
363 +       uint32 nod;
364 +       uint8 *b;
365         blk_info *bi;
366         inode *itab;
367 +       uint32 usecount;
368  } nod_info;
369  
370 +#define MAX_FREE_CACHE_INODES 100
371 +
372 +static uint32
373 +inode_elem_val(cache_link *elem)
374 +{
375 +       nod_info *ni = container_of(elem, nod_info, link);
376 +       return ni->nod;
377 +}
378 +
379 +static void
380 +inode_freed(cache_link *elem)
381 +{
382 +       nod_info *ni = container_of(elem, nod_info, link);
383 +
384 +       if (ni->fs->swapit)
385 +               swap_nod(ni->itab);
386 +       put_blk(ni->bi);
387 +       free(ni);
388 +}
389 +
390  // Return a given inode from a filesystem.  Make sure to call put_nod()
391  // when you are done with the inode.
392  static inline inode *
393  get_nod(filesystem *fs, uint32 nod, nod_info **rni)
394  {
395         int grp, offset, boffset;
396 +       cache_link *curr;
397         nod_info *ni;
398 -       uint8 *b;
399  
400 -       offset = GRP_IBM_OFFSET(fs,nod) - 1;
401 -       boffset = offset / (BLOCKSIZE / sizeof(inode));
402 -       offset %= BLOCKSIZE / sizeof(inode);
403 -       grp = GRP_GROUP_OF_INODE(fs,nod);
404 +       curr = cache_find(&fs->inodes, nod);
405 +       if (curr) {
406 +               ni = container_of(curr, nod_info, link);
407 +               ni->usecount++;
408 +               goto out;
409 +       }
410 +
411         ni = malloc(sizeof(*ni));
412         if (!ni)
413                 error_msg_and_die("get_nod: out of memory");
414         ni->fs = fs;
415 -       b = get_blk(fs, fs->gd[grp].bg_inode_table + boffset, &ni->bi);
416 -       ni->itab = ((inode *) b) + offset;
417 +       ni->nod = nod;
418 +       ni->usecount = 1;
419 +       cache_add(&fs->inodes, &ni->link);
420 +
421 +       offset = GRP_IBM_OFFSET(fs, nod) - 1;
422 +       boffset = offset / (BLOCKSIZE / sizeof(inode));
423 +       offset %= BLOCKSIZE / sizeof(inode);
424 +       grp = GRP_GROUP_OF_INODE(fs,nod);
425 +       ni->b = get_blk(fs, fs->gd[grp].bg_inode_table + boffset, &ni->bi);
426 +       ni->itab = ((inode *) ni->b) + offset;
427         if (fs->swapit)
428                 swap_nod(ni->itab);
429 +
430 +out:
431         *rni = ni;
432         return ni->itab;
433  }
434 @@ -937,10 +1090,13 @@ get_nod(filesystem *fs, uint32 nod, nod_info **rni)
435  static inline void
436  put_nod(nod_info *ni)
437  {
438 -       if (ni->fs->swapit)
439 -               swap_nod(ni->itab);
440 -       put_blk(ni->bi);
441 -       free(ni);
442 +       if (ni->usecount == 0)
443 +               error_msg_and_die("Internal error: put_nod usecount zero");
444 +
445 +       ni->usecount--;
446 +       if (ni->usecount == 0)
447 +               /* Free happens in the cache code */
448 +               cache_item_set_unused(&ni->fs->inodes, &ni->link);
449  }
450  
451  // Used to hold state information while walking a directory inode.
452 @@ -2090,40 +2246,61 @@ add2fs_from_dir(filesystem *fs, uint32 this_nod, int squash_uids, int squash_per
453         closedir(dh);
454  }
455  
456 -// endianness swap of the whole filesystem
457  static void
458 -swap_goodfs(filesystem *fs)
459 +swap_gds(filesystem *fs)
460  {
461         uint32 i;
462 -
463         for(i=0;i<GRP_NBGROUPS(fs);i++)
464                 swap_gd(&(fs->gd[i]));
465 -       swap_sb(fs->sb);
466  }
467  
468 +// Copy size blocks from src to dst, putting holes in the output
469 +// file (if possible) if the input block is all zeros.
470  static void
471 -swap_badfs(filesystem *fs)
472 +copy_file(filesystem *fs, FILE *dst, FILE *src, size_t size)
473  {
474 -       uint32 i;
475 -       swap_sb(fs->sb);
476 -       for(i=0;i<GRP_NBGROUPS(fs);i++)
477 -               swap_gd(&(fs->gd[i]));
478 +       uint8 *b;
479 +
480 +       b = malloc(BLOCKSIZE);
481 +       if (!b)
482 +               error_msg_and_die("copy_file: out of memory");
483 +       if (fseek(src, 0, SEEK_SET))
484 +               perror_msg_and_die("fseek");
485 +       if (ftruncate(fileno(dst), 0))
486 +               perror_msg_and_die("copy_file: ftruncate");
487 +       while (size > 0) {
488 +               if (fread(b, BLOCKSIZE, 1, src) != 1)
489 +                       perror_msg_and_die("copy failed on read");
490 +               if ((dst != stdout) && is_blk_empty(b)) {
491 +                       /* Empty block, just skip it */
492 +                       if (fseek(dst, BLOCKSIZE, SEEK_CUR))
493 +                               perror_msg_and_die("fseek");
494 +               } else {
495 +                       if (fwrite(b, BLOCKSIZE, 1, dst) != 1)
496 +                               perror_msg_and_die("copy failed on write");
497 +               }
498 +               size --;
499 +       }
500  }
501  
502  // Allocate a new filesystem structure, allocate internal memory,
503  // and initialize the contents.
504  static filesystem *
505 -alloc_fs(uint32 nbblocks, int swapit)
506 +alloc_fs(int swapit, char *fname, uint32 nbblocks, FILE *srcfile)
507  {
508         filesystem *fs;
509 +       struct stat srcstat, dststat;
510  
511         fs = malloc(sizeof(*fs));
512         if (!fs)
513                 error_msg_and_die("not enough memory for filesystem");
514         memset(fs, 0, sizeof(*fs));
515         fs->swapit = swapit;
516 -       if(!(fs->data = calloc(nbblocks, BLOCKSIZE)))
517 -               error_msg_and_die("not enough memory for filesystem");
518 +       cache_init(&fs->blks, MAX_FREE_CACHE_BLOCKS, blk_elem_val, blk_freed);
519 +       cache_init(&fs->blkmaps, MAX_FREE_CACHE_BLOCKMAPS,
520 +                  blkmap_elem_val, blkmap_freed);
521 +       cache_init(&fs->inodes, MAX_FREE_CACHE_INODES,
522 +                  inode_elem_val, inode_freed);
523         fs->hdlink_cnt = HDLINK_CNT;
524         fs->hdlinks.hdl = calloc(sizeof(struct hdlink_s), fs->hdlink_cnt);
525         if (!fs->hdlinks.hdl)
526 @@ -2131,12 +2308,44 @@ alloc_fs(uint32 nbblocks, int swapit)
527         fs->hdlinks.count = 0 ;
528         fs->sb = (superblock *) (fs->data + BLOCKSIZE);
529         fs->gd = (groupdescriptor *) (fs->sb + 1);
530 +
531 +       if (strcmp(fname, "-") == 0)
532 +               fs->f = tmpfile();
533 +       else if (srcfile) {
534 +               if (fstat(fileno(srcfile), &srcstat))
535 +                       perror_msg_and_die("fstat srcfile");
536 +               if (stat(fname, &dststat))
537 +                       perror_msg_and_die("stat-ing %s", fname);
538 +               if (srcstat.st_ino == dststat.st_ino) {
539 +                       // source and destination are the same file, don't
540 +                       // truncate or copy, just use the file.
541 +                       fs->f = fopen(fname, "r+b");
542 +               } else {
543 +                       fs->f = fopen(fname, "w+b");
544 +                       if (fs->f)
545 +                               copy_file(fs, fs->f, srcfile,
546 +                                         nbblocks * BLOCKSIZE);
547 +               }
548 +       } else
549 +               fs->f = fopen(fname, "w+b");
550 +       if (!fs->f)
551 +               perror_msg_and_die("opening %s", fname);
552         return fs;
553  }
554  
555 +/* Make sure the output file is the right size */
556 +static void
557 +set_file_size(filesystem *fs)
558 +{
559 +       if (ftruncate(fileno(fs->f),
560 +                     ((off_t) fs->sb->s_blocks_count) * BLOCKSIZE))
561 +               perror_msg_and_die("set_file_size: ftruncate");
562 +}
563 +
564  // initialize an empty filesystem
565  static filesystem *
566 -init_fs(int nbblocks, int nbinodes, int nbresrvd, int holes, uint32 fs_timestamp, int swapit)
567 +init_fs(int nbblocks, int nbinodes, int nbresrvd, int holes,
568 +       uint32 fs_timestamp, int swapit, char *fname)
569  {
570         uint32 i;
571         filesystem *fs;
572 @@ -2184,10 +2393,16 @@ init_fs(int nbblocks, int nbinodes, int nbresrvd, int holes, uint32 fs_timestamp
573         free_blocks = nbblocks - overhead_per_group*nbgroups - 1 /*boot block*/;
574         free_blocks_per_group = nbblocks_per_group - overhead_per_group;
575  
576 -       fs = alloc_fs(nbblocks, swapit);
577 +       fs = alloc_fs(swapit, fname, nbblocks, NULL);
578         fs->nheadblocks = (((nbgroups * sizeof(groupdescriptor))
579                             + sizeof(superblock) + (BLOCKSIZE - 1))
580                            / BLOCKSIZE);
581 +       fs->sb = (superblock *) malloc(BLOCKSIZE);
582 +       if (!fs->sb)
583 +               error_msg_and_die("error allocating header memory");
584 +       fs->gd = (groupdescriptor *) calloc(fs->nheadblocks - 1, BLOCKSIZE);
585 +       if (!fs->gd)
586 +               error_msg_and_die("error allocating header memory");
587  
588         // create the superblock for an empty filesystem
589         fs->sb->s_inodes_count = nbinodes_per_group * nbgroups;
590 @@ -2205,6 +2420,10 @@ init_fs(int nbblocks, int nbinodes, int nbresrvd, int holes, uint32 fs_timestamp
591         fs->sb->s_magic = EXT2_MAGIC_NUMBER;
592         fs->sb->s_lastcheck = fs_timestamp;
593  
594 +       fs->sb->s_reserved[200] = 0;
595 +
596 +       set_file_size(fs);
597 +
598         // set up groupdescriptors
599         for(i=0, bbmpos=gdsz+2, ibmpos=bbmpos+1, itblpos=ibmpos+1;
600                 i<nbgroups;
601 @@ -2315,27 +2534,49 @@ init_fs(int nbblocks, int nbinodes, int nbresrvd, int holes, uint32 fs_timestamp
602  
603  // loads a filesystem from disk
604  static filesystem *
605 -load_fs(FILE * fh, int swapit)
606 +load_fs(FILE * fh, int swapit, char *fname)
607  {
608 -       size_t fssize;
609 +       off_t fssize;
610         filesystem *fs;
611 -       if((fseek(fh, 0, SEEK_END) < 0) || ((ssize_t)(fssize = ftell(fh)) == -1))
612 +
613 +       if((fseek(fh, 0, SEEK_END) < 0) || ((fssize = ftello(fh)) == -1))
614                 perror_msg_and_die("input filesystem image");
615         rewind(fh);
616 -       fssize = (fssize + BLOCKSIZE - 1) / BLOCKSIZE;
617 +       if ((fssize % BLOCKSIZE) != 0)
618 +               error_msg_and_die("Input file not a multiple of block size");
619 +       fssize /= BLOCKSIZE;
620         if(fssize < 16) // totally arbitrary
621                 error_msg_and_die("too small filesystem");
622 -       fs = alloc_fs(fssize, swapit);
623 -       if(fread(fs->data, BLOCKSIZE, fssize, fh) != fssize)
624 -               perror_msg_and_die("input filesystem image");
625 -
626 +       fs = alloc_fs(swapit, fname, fssize, fh);
627 +
628 +       /* Read and check the superblock, then read the superblock
629 +        * and all the group descriptors */
630 +       fs->sb = malloc(BLOCKSIZE);
631 +       if (!fs->sb)
632 +               error_msg_and_die("error allocating header memory");
633 +       if (fseek(fs->f, BLOCKSIZE, SEEK_SET))
634 +               perror_msg_and_die("fseek");
635 +       if (fread(fs->sb, BLOCKSIZE, 1, fs->f) != 1)
636 +               perror_msg_and_die("fread filesystem image superblock");
637         if(swapit)
638 -               swap_badfs(fs);
639 +               swap_sb(fs->sb);
640         if(fs->sb->s_rev_level || (fs->sb->s_magic != EXT2_MAGIC_NUMBER))
641                 error_msg_and_die("not a suitable ext2 filesystem");
642         fs->nheadblocks = (((GRP_NBGROUPS(fs) * sizeof(groupdescriptor))
643                             + sizeof(superblock) + (BLOCKSIZE - 1))
644                            / BLOCKSIZE);
645 +
646 +       fs->gd = calloc(fs->nheadblocks - 1, BLOCKSIZE);
647 +       if (!fs->gd)
648 +               error_msg_and_die("error allocating header memory");
649 +       if (fread(fs->gd, BLOCKSIZE, fs->nheadblocks - 1, fs->f)
650 +           != (fs->nheadblocks - 1))
651 +               perror_msg_and_die("fread filesystem image group descriptors");
652 +
653 +       if(swapit)
654 +               swap_gds(fs);
655 +
656 +       set_file_size(fs);
657         return fs;
658  }
659  
660 @@ -2343,7 +2584,9 @@ static void
661  free_fs(filesystem *fs)
662  {
663         free(fs->hdlinks.hdl);
664 -       free(fs->data);
665 +       fclose(fs->f);
666 +       free(fs->sb);
667 +       free(fs->gd);
668         free(fs);
669  }
670  
671 @@ -2631,16 +2874,30 @@ print_fs(filesystem *fs)
672  }
673  
674  static void
675 -dump_fs(filesystem *fs, FILE * fh, int swapit)
676 -{
677 -       uint32 nbblocks = fs->sb->s_blocks_count;
678 +finish_fs(filesystem *fs)
679 +{
680 +       if (cache_flush(&fs->inodes))
681 +               error_msg_and_die("entry mismatch on inode cache flush");
682 +       if (cache_flush(&fs->blkmaps))
683 +               error_msg_and_die("entry mismatch on blockmap cache flush");
684 +       if (cache_flush(&fs->blks))
685 +               error_msg_and_die("entry mismatch on block cache flush");
686         fs->sb->s_reserved[200] = 0;
687 -       if(swapit)
688 -               swap_goodfs(fs);
689 -       if(fwrite(fs->data, BLOCKSIZE, nbblocks, fh) < nbblocks)
690 -               perror_msg_and_die("output filesystem image");
691 -       if(swapit)
692 -               swap_badfs(fs);
693 +       if(fs->swapit) {
694 +               swap_sb(fs->sb);
695 +               swap_gds(fs);
696 +       }
697 +       if (fseek(fs->f, BLOCKSIZE, SEEK_SET))
698 +               perror_msg_and_die("fseek");
699 +       if(fwrite(fs->sb, BLOCKSIZE, 1, fs->f) != 1)
700 +               perror_msg_and_die("output filesystem superblock");
701 +       if(fwrite(fs->gd, BLOCKSIZE, fs->nheadblocks - 1, fs->f)
702 +          != (fs->nheadblocks - 1))
703 +               perror_msg_and_die("output filesystem group descriptors");
704 +       if(fs->swapit) {
705 +               swap_sb(fs->sb);
706 +               swap_gds(fs);
707 +       }
708  }
709  
710  static void
711 @@ -2851,11 +3108,11 @@ main(int argc, char **argv)
712                 if(strcmp(fsin, "-"))
713                 {
714                         FILE * fh = xfopen(fsin, "rb");
715 -                       fs = load_fs(fh, bigendian);
716 +                       fs = load_fs(fh, bigendian, fsout);
717                         fclose(fh);
718                 }
719                 else
720 -                       fs = load_fs(stdin, bigendian);
721 +                       fs = load_fs(stdin, bigendian, fsout);
722         }
723         else
724         {
725 @@ -2886,7 +3143,7 @@ main(int argc, char **argv)
726                 if(fs_timestamp == -1)
727                         fs_timestamp = time(NULL);
728                 fs = init_fs(nbblocks, nbinodes, nbresrvd, holes, fs_timestamp,
729 -                            bigendian);
730 +                            bigendian, fsout);
731         }
732         
733         populate_fs(fs, dopt, didx, squash_uids, squash_perms, fs_timestamp, NULL);
734 @@ -2925,14 +3182,10 @@ main(int argc, char **argv)
735                 flist_blocks(fs, nod, fh);
736                 fclose(fh);
737         }
738 -       if(strcmp(fsout, "-"))
739 -       {
740 -               FILE * fh = xfopen(fsout, "wb");
741 -               dump_fs(fs, fh, bigendian);
742 -               fclose(fh);
743 -       }
744 -       else
745 -               dump_fs(fs, stdout, bigendian);
746 +       finish_fs(fs);
747 +       if(strcmp(fsout, "-") == 0)
748 +               copy_file(fs, stdout, fs->f, fs->sb->s_blocks_count);
749 +
750         free_fs(fs);
751         return 0;
752  }
753 diff --git a/list.h b/list.h
754 new file mode 100644
755 index 0000000..52bb181
756 --- /dev/null
757 +++ b/list.h
758 @@ -0,0 +1,78 @@
759 +#ifndef __LIST_H__
760 +#define __LIST_H__
761 +
762 +#if STDC_HEADERS
763 +# include <stdlib.h>
764 +# include <stddef.h>
765 +#else
766 +# if HAVE_STDLIB_H
767 +#  include <stdlib.h>
768 +# endif
769 +# if HAVE_STDDEF_H
770 +#  include <stddef.h>
771 +# endif
772 +#endif
773 +
774 +#ifndef offsetof
775 +#define offsetof(st, m) \
776 +     ((size_t) ( (char *)&((st *)(0))->m - (char *)0 ))
777 +#endif
778 +
779 +#define container_of(ptr, type, member) ({ \
780 +                const typeof( ((type *)0)->member ) *__mptr = (ptr); \
781 +                (type *)( (char *)__mptr - offsetof(type,member) );})
782 +
783 +typedef struct list_elem
784 +{
785 +       struct list_elem *next;
786 +       struct list_elem *prev;
787 +} list_elem;
788 +
789 +static inline void list_init(list_elem *list)
790 +{
791 +       list->next = list;
792 +       list->prev = list;
793 +}
794 +
795 +static inline void list_add_after(list_elem *pos, list_elem *elem)
796 +{
797 +       elem->next = pos->next;
798 +       elem->prev = pos;
799 +       pos->next->prev = elem;
800 +       pos->next = elem;
801 +}
802 +
803 +static inline void list_add_before(list_elem *pos, list_elem *elem)
804 +{
805 +       elem->prev = pos->prev;
806 +       elem->next = pos;
807 +       pos->prev->next = elem;
808 +       pos->prev = elem;
809 +}
810 +
811 +static inline void list_del(list_elem *elem)
812 +{
813 +       elem->next->prev = elem->prev;
814 +       elem->prev->next = elem->next;
815 +}
816 +
817 +static inline void list_item_init(list_elem *elem)
818 +{
819 +       elem->next = elem;
820 +       elem->prev = elem;
821 +}
822 +
823 +static inline int list_empty(list_elem *elem)
824 +{
825 +       return elem->next == elem;
826 +}
827 +
828 +#define list_for_each_elem(list, curr)                 \
829 +       for ((curr) = (list)->next; (curr) != (list); (curr) = (curr)->next)
830 +
831 +#define list_for_each_elem_safe(list, curr, next)      \
832 +       for ((curr) = (list)->next, (next) = (curr)->next;      \
833 +            (curr) != (list);                                  \
834 +            (curr) = (next), (next) = (curr)->next)
835 +
836 +#endif /* __LIST_H__ */
837 -- 
838 1.7.4.1
839