755ee9dee273d1ed371c9450bd9d797f03d7843c
[packages/trusty/cirros-testvm.git] / cirros-testvm / src-cirros / buildroot-2015.05 / package / genext2fs / 0001-update-genext2fs.c-to-rev-1.118.patch
1 [PATCH] update genext2fs.c to CVS rev 1.118
2
3 See http://genext2fs.cvs.sourceforge.net/viewvc/genext2fs/genext2fs/genext2fs.c?view=log
4 for details.
5
6 Numerous bugfixes, large file and filesystem support, rev 1 filesystems,
7 volume id support, block size, ..
8
9 Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk>
10 ---
11  cache.h     |  128 ++++
12  genext2fs.c | 1870 ++++++++++++++++++++++++++++++++++++++++++------------------
13  list.h      |   78 ++
14  3 files changed, 1527 insertions(+), 549 deletions(-)
15
16 Index: genext2fs-1.4.1/genext2fs.c
17 ===================================================================
18 --- genext2fs-1.4.1.orig/genext2fs.c
19 +++ genext2fs-1.4.1/genext2fs.c
20 @@ -53,6 +53,12 @@
21  //                     along with -q, -P, -U
22  
23  
24 +/*
25 + * Allow fseeko/off_t to be 64-bit offsets to allow filesystems and
26 + * individual files >2GB.
27 + */
28 +#define _FILE_OFFSET_BITS 64
29 +
30  #include <config.h>
31  #include <stdio.h>
32  
33 @@ -107,10 +113,8 @@
34  
35  #if HAVE_DIRENT_H
36  # include <dirent.h>
37 -# define NAMLEN(dirent) strlen((dirent)->d_name)
38  #else
39  # define dirent direct
40 -# define NAMLEN(dirent) (dirent)->d_namlen
41  # if HAVE_SYS_NDIR_H
42  #  include <sys/ndir.h>
43  # endif
44 @@ -144,6 +148,8 @@
45  # include <limits.h>
46  #endif
47  
48 +#include "cache.h"
49 +
50  struct stats {
51         unsigned long nblocks;
52         unsigned long ninodes;
53 @@ -151,13 +157,42 @@
54  
55  // block size
56  
57 -#define BLOCKSIZE         1024
58 +static int blocksize = 1024;
59 +
60 +#define SUPERBLOCK_OFFSET      1024
61 +#define SUPERBLOCK_SIZE                1024
62 +
63 +#define BLOCKSIZE         blocksize
64  #define BLOCKS_PER_GROUP  8192
65  #define INODES_PER_GROUP  8192
66  /* Percentage of blocks that are reserved.*/
67  #define RESERVED_BLOCKS       5/100
68  #define MAX_RESERVED_BLOCKS  25/100
69  
70 +/* The default value for s_creator_os. */
71 +#if defined(__linux__)    &&    defined(EXT2_OS_LINUX)
72 +#define CREATOR_OS EXT2_OS_LINUX
73 +#define CREATOR_OS_NAME "linux"
74 +#else
75 +#if defined(__GNU__)     &&     defined(EXT2_OS_HURD)
76 +#define CREATOR_OS EXT2_OS_HURD
77 +#define CREATOR_OS_NAME "hurd"
78 +#else
79 +#if defined(__FreeBSD__) &&     defined(EXT2_OS_FREEBSD)
80 +#define CREATOR_OS EXT2_OS_FREEBSD
81 +#define CREATOR_OS_NAME "freebsd"
82 +#else
83 +#if defined(LITES)         &&   defined(EXT2_OS_LITES)
84 +#define CREATOR_OS EXT2_OS_LITES
85 +#define CREATOR_OS_NAME "lites"
86 +#else
87 +#define CREATOR_OS EXT2_OS_LINUX /* by default */
88 +#define CREATOR_OS_NAME "linux"
89 +#endif /* defined(LITES) && defined(EXT2_OS_LITES) */
90 +#endif /* defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) */
91 +#endif /* defined(__GNU__)     && defined(EXT2_OS_HURD) */
92 +#endif /* defined(__linux__)   && defined(EXT2_OS_LINUX) */
93 +
94  
95  // inode block size (why is it != BLOCKSIZE ?!?)
96  /* The field i_blocks in the ext2 inode stores the number of data blocks
97 @@ -190,6 +225,14 @@
98  #define EXT2_TIND_BLOCK    14                    // triple indirect block
99  #define EXT2_INIT_BLOCK    0xFFFFFFFF            // just initialized (not really a block address)
100  
101 +// codes for operating systems
102 +
103 +#define EXT2_OS_LINUX           0
104 +#define EXT2_OS_HURD            1
105 +#define EXT2_OS_MASIX           2
106 +#define EXT2_OS_FREEBSD         3
107 +#define EXT2_OS_LITES           4
108 +
109  // end of a block walk
110  
111  #define WALK_END           0xFFFFFFFE
112 @@ -227,44 +270,46 @@
113  #define FM_IWOTH   0000002     // write
114  #define FM_IXOTH   0000001     // execute
115  
116 -// options
117 -
118 -#define OP_HOLES     0x01       // make files with holes
119 -
120  /* Defines for accessing group details */
121  
122  // Number of groups in the filesystem
123  #define GRP_NBGROUPS(fs) \
124 -       (((fs)->sb.s_blocks_count - fs->sb.s_first_data_block + \
125 -         (fs)->sb.s_blocks_per_group - 1) / (fs)->sb.s_blocks_per_group)
126 +       (((fs)->sb->s_blocks_count - fs->sb->s_first_data_block + \
127 +         (fs)->sb->s_blocks_per_group - 1) / (fs)->sb->s_blocks_per_group)
128  
129  // Get group block bitmap (bbm) given the group number
130 -#define GRP_GET_GROUP_BBM(fs,grp) ( get_blk((fs),(fs)->gd[(grp)].bg_block_bitmap) )
131 +#define GRP_GET_GROUP_BBM(fs,grp,bi) (get_blk((fs),(grp)->bg_block_bitmap,(bi)))
132 +#define GRP_PUT_GROUP_BBM(bi) ( put_blk((bi)) )
133  
134  // Get group inode bitmap (ibm) given the group number
135 -#define GRP_GET_GROUP_IBM(fs,grp) ( get_blk((fs),(fs)->gd[(grp)].bg_inode_bitmap) )
136 -               
137 +#define GRP_GET_GROUP_IBM(fs,grp,bi) (get_blk((fs), (grp)->bg_inode_bitmap,(bi)))
138 +#define GRP_PUT_GROUP_IBM(bi) ( put_blk((bi)) )
139 +
140  // Given an inode number find the group it belongs to
141 -#define GRP_GROUP_OF_INODE(fs,nod) ( ((nod)-1) / (fs)->sb.s_inodes_per_group)
142 +#define GRP_GROUP_OF_INODE(fs,nod) ( ((nod)-1) / (fs)->sb->s_inodes_per_group)
143  
144  //Given an inode number get the inode bitmap that covers it
145 -#define GRP_GET_INODE_BITMAP(fs,nod) \
146 -       ( GRP_GET_GROUP_IBM((fs),GRP_GROUP_OF_INODE((fs),(nod))) )
147 +#define GRP_GET_INODE_BITMAP(fs,nod,bi,gi)                             \
148 +       ( GRP_GET_GROUP_IBM((fs),get_gd(fs,GRP_GROUP_OF_INODE((fs),(nod)),gi),bi) )
149 +#define GRP_PUT_INODE_BITMAP(bi,gi)            \
150 +       ( GRP_PUT_GROUP_IBM((bi)),put_gd((gi)) )
151  
152  //Given an inode number find its offset within the inode bitmap that covers it
153  #define GRP_IBM_OFFSET(fs,nod) \
154 -       ( (nod) - GRP_GROUP_OF_INODE((fs),(nod))*(fs)->sb.s_inodes_per_group )
155 +       ( (nod) - GRP_GROUP_OF_INODE((fs),(nod))*(fs)->sb->s_inodes_per_group )
156  
157  // Given a block number find the group it belongs to
158 -#define GRP_GROUP_OF_BLOCK(fs,blk) ( ((blk)-1) / (fs)->sb.s_blocks_per_group)
159 +#define GRP_GROUP_OF_BLOCK(fs,blk) ( ((blk)-1) / (fs)->sb->s_blocks_per_group)
160         
161 -//Given a block number get the block bitmap that covers it
162 -#define GRP_GET_BLOCK_BITMAP(fs,blk) \
163 -       ( GRP_GET_GROUP_BBM((fs),GRP_GROUP_OF_BLOCK((fs),(blk))) )
164 +//Given a block number get/put the block bitmap that covers it
165 +#define GRP_GET_BLOCK_BITMAP(fs,blk,bi,gi)                             \
166 +       ( GRP_GET_GROUP_BBM((fs),get_gd(fs,GRP_GROUP_OF_BLOCK((fs),(blk)),(gi)),(bi)) )
167 +#define GRP_PUT_BLOCK_BITMAP(bi,gi)            \
168 +       ( GRP_PUT_GROUP_BBM((bi)),put_gd((gi)) )
169  
170  //Given a block number find its offset within the block bitmap that covers it
171  #define GRP_BBM_OFFSET(fs,blk) \
172 -       ( (blk) - GRP_GROUP_OF_BLOCK((fs),(blk))*(fs)->sb.s_blocks_per_group )
173 +       ( (blk) - GRP_GROUP_OF_BLOCK((fs),(blk))*(fs)->sb->s_blocks_per_group )
174  
175  
176  // used types
177 @@ -286,7 +331,9 @@
178  // older solaris. Note that this is still not very portable, in that
179  // the return value cannot be trusted.
180  
181 -#if SCANF_CAN_MALLOC
182 +#if 0 // SCANF_CAN_MALLOC
183 +// C99 define "a" for floating point, so you can have runtime surprise
184 +// according the library versions
185  # define SCANF_PREFIX "a"
186  # define SCANF_STRING(s) (&s)
187  #else
188 @@ -430,6 +477,17 @@
189                         ((val<<8)&0xFF0000) | (val<<24));
190  }
191  
192 +static inline int
193 +is_blk_empty(uint8 *b)
194 +{
195 +       uint32 i;
196 +       uint32 *v = (uint32 *) b;
197 +
198 +       for(i = 0; i < BLOCKSIZE / 4; i++)
199 +               if (*v++)
200 +                       return 0;
201 +       return 1;
202 +}
203  
204  // on-disk structures
205  // this trick makes me declare things only once
206 @@ -460,7 +518,22 @@
207         udecl32(s_creator_os)          /* Indicator of which OS created the filesystem */ \
208         udecl32(s_rev_level)           /* The revision level of the filesystem */ \
209         udecl16(s_def_resuid)          /* The default uid for reserved blocks */ \
210 -       udecl16(s_def_resgid)          /* The default gid for reserved blocks */
211 +       udecl16(s_def_resgid)          /* The default gid for reserved blocks */ \
212 +       /* rev 1 version fields start here */ \
213 +       udecl32(s_first_ino)            /* First non-reserved inode */  \
214 +       udecl16(s_inode_size)           /* size of inode structure */   \
215 +       udecl16(s_block_group_nr)       /* block group # of this superblock */ \
216 +       udecl32(s_feature_compat)       /* compatible feature set */    \
217 +       udecl32(s_feature_incompat)     /* incompatible feature set */  \
218 +       udecl32(s_feature_ro_compat)    /* readonly-compatible feature set */ \
219 +       utdecl8(s_uuid,16)              /* 128-bit uuid for volume */   \
220 +       utdecl8(s_volume_name,16)       /* volume name */               \
221 +       utdecl8(s_last_mounted,64)      /* directory where last mounted */ \
222 +       udecl32(s_algorithm_usage_bitmap) /* For compression */
223 +
224 +#define EXT2_GOOD_OLD_FIRST_INO        11
225 +#define EXT2_GOOD_OLD_INODE_SIZE 128
226 +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE      0x0002
227  
228  #define groupdescriptor_decl \
229         udecl32(bg_block_bitmap)       /* Block number of the block bitmap */ \
230 @@ -500,6 +573,7 @@
231  
232  #define decl8(x) int8 x;
233  #define udecl8(x) uint8 x;
234 +#define utdecl8(x,n) uint8 x[n];
235  #define decl16(x) int16 x;
236  #define udecl16(x) uint16 x;
237  #define decl32(x) int32 x;
238 @@ -509,7 +583,7 @@
239  typedef struct
240  {
241         superblock_decl
242 -       uint32 s_reserved[235];       // Reserved
243 +       uint32 s_reserved[205];       // Reserved
244  } superblock;
245  
246  typedef struct
247 @@ -527,10 +601,9 @@
248  typedef struct
249  {
250         directory_decl
251 -       char d_name[0];
252  } directory;
253  
254 -typedef uint8 block[BLOCKSIZE];
255 +typedef uint8 *block;
256  
257  /* blockwalker fields:
258     The blockwalker is used to access all the blocks of a file (including
259 @@ -567,23 +640,41 @@
260         uint32 bptind;
261  } blockwalker;
262  
263 +#define HDLINK_CNT   16
264 +struct hdlink_s
265 +{
266 +       uint32  src_inode;
267 +       uint32  dst_nod;
268 +};
269 +
270 +struct hdlinks_s
271 +{
272 +       int32 count;
273 +       struct hdlink_s *hdl;
274 +};
275  
276  /* Filesystem structure that support groups */
277 -#if BLOCKSIZE == 1024
278  typedef struct
279  {
280 -       block zero;            // The famous block 0
281 -       superblock sb;         // The superblock
282 -       groupdescriptor gd[0]; // The group descriptors
283 +       FILE *f;
284 +       superblock *sb;
285 +       int swapit;
286 +       int32 hdlink_cnt;
287 +       struct hdlinks_s hdlinks;
288 +
289 +       int holes;
290 +
291 +       listcache blks;
292 +       listcache gds;
293 +       listcache inodes;
294 +       listcache blkmaps;
295  } filesystem;
296 -#else
297 -#error UNHANDLED BLOCKSIZE
298 -#endif
299  
300  // now the endianness swap
301  
302  #undef decl8
303  #undef udecl8
304 +#undef utdecl8
305  #undef decl16
306  #undef udecl16
307  #undef decl32
308 @@ -592,28 +683,13 @@
309  
310  #define decl8(x)
311  #define udecl8(x)
312 +#define utdecl8(x,n)
313  #define decl16(x) this->x = swab16(this->x);
314  #define udecl16(x) this->x = swab16(this->x);
315  #define decl32(x) this->x = swab32(this->x);
316  #define udecl32(x) this->x = swab32(this->x);
317  #define utdecl32(x,n) { int i; for(i=0; i<n; i++) this->x[i] = swab32(this->x[i]); }
318  
319 -#define HDLINK_CNT   16
320 -static int32 hdlink_cnt = HDLINK_CNT;
321 -struct hdlink_s
322 -{
323 -       uint32  src_inode;
324 -       uint32  dst_nod;
325 -};
326 -
327 -struct hdlinks_s 
328 -{
329 -       int32 count;
330 -       struct hdlink_s *hdl;
331 -};
332 -
333 -static struct hdlinks_s hdlinks;
334 -
335  static void
336  swap_sb(superblock *sb)
337  {
338 @@ -633,9 +709,24 @@
339  static void
340  swap_nod(inode *nod)
341  {
342 +       uint32 nblk;
343 +
344  #define this nod
345         inode_decl
346  #undef this
347 +
348 +       // block and character inodes store the major and minor in the
349 +       // i_block, so we need to unswap to get those.  Also, if it's
350 +       // zero iblocks, put the data back like it belongs.
351 +       nblk = nod->i_blocks / INOBLK;
352 +       if ((nod->i_size && !nblk)
353 +           || ((nod->i_mode & FM_IFBLK) == FM_IFBLK)
354 +           || ((nod->i_mode & FM_IFCHR) == FM_IFCHR))
355 +       {
356 +               int i;
357 +               for(i = 0; i <= EXT2_TIND_BLOCK; i++)
358 +                       nod->i_block[i] = swab32(nod->i_block[i]);
359 +       }
360  }
361  
362  static void
363 @@ -657,6 +748,7 @@
364  
365  #undef decl8
366  #undef udecl8
367 +#undef utdecl8
368  #undef decl16
369  #undef udecl16
370  #undef decl32
371 @@ -770,15 +862,15 @@
372  }
373  
374  int
375 -is_hardlink(ino_t inode)
376 +is_hardlink(filesystem *fs, ino_t inode)
377  {
378         int i;
379  
380 -       for(i = 0; i < hdlinks.count; i++) {
381 -               if(hdlinks.hdl[i].src_inode == inode)
382 +       for(i = 0; i < fs->hdlinks.count; i++) {
383 +               if(fs->hdlinks.hdl[i].src_inode == inode)
384                         return i;
385         }
386 -       return -1;              
387 +       return -1;
388  }
389  
390  // printf helper macro
391 @@ -789,6 +881,8 @@
392  get_workblk(void)
393  {
394         unsigned char* b=calloc(1,BLOCKSIZE);
395 +       if (!b)
396 +               error_msg_and_die("get_workblk() failed, out of memory");
397         return b;
398  }
399  static inline void
400 @@ -811,24 +905,464 @@
401         return b[(item-1) / 8] & (1 << ((item-1) % 8));
402  }
403  
404 -// return a given block from a filesystem
405 +// Used by get_blk/put_blk to hold information about a block owned
406 +// by the user.
407 +typedef struct
408 +{
409 +       cache_link link;
410 +
411 +       filesystem *fs;
412 +       uint32 blk;
413 +       uint8 *b;
414 +       uint32 usecount;
415 +} blk_info;
416 +
417 +#define MAX_FREE_CACHE_BLOCKS 100
418 +
419 +static uint32
420 +blk_elem_val(cache_link *elem)
421 +{
422 +       blk_info *bi = container_of(elem, blk_info, link);
423 +       return bi->blk;
424 +}
425 +
426 +static void
427 +blk_freed(cache_link *elem)
428 +{
429 +       blk_info *bi = container_of(elem, blk_info, link);
430 +
431 +       if (fseeko(bi->fs->f, ((off_t) bi->blk) * BLOCKSIZE, SEEK_SET))
432 +               perror_msg_and_die("fseek");
433 +       if (fwrite(bi->b, BLOCKSIZE, 1, bi->fs->f) != 1)
434 +               perror_msg_and_die("get_blk: write");
435 +       free(bi->b);
436 +       free(bi);
437 +}
438 +
439 +// Return a given block from a filesystem.  Make sure to call
440 +// put_blk when you are done with it.
441  static inline uint8 *
442 -get_blk(filesystem *fs, uint32 blk)
443 +get_blk(filesystem *fs, uint32 blk, blk_info **rbi)
444  {
445 -       return (uint8*)fs + blk*BLOCKSIZE;
446 +       cache_link *curr;
447 +       blk_info *bi;
448 +
449 +       if (blk >= fs->sb->s_blocks_count)
450 +               error_msg_and_die("Internal error, block out of range");
451 +
452 +       curr = cache_find(&fs->blks, blk);
453 +       if (curr) {
454 +               bi = container_of(curr, blk_info, link);
455 +               bi->usecount++;
456 +               goto out;
457 +       }
458 +
459 +       bi = malloc(sizeof(*bi));
460 +       if (!bi)
461 +               error_msg_and_die("get_blk: out of memory");
462 +       bi->fs = fs;
463 +       bi->blk = blk;
464 +       bi->usecount = 1;
465 +       bi->b = malloc(BLOCKSIZE);
466 +       if (!bi->b)
467 +               error_msg_and_die("get_blk: out of memory");
468 +       cache_add(&fs->blks, &bi->link);
469 +       if (fseeko(fs->f, ((off_t) blk) * BLOCKSIZE, SEEK_SET))
470 +               perror_msg_and_die("fseek");
471 +       if (fread(bi->b, BLOCKSIZE, 1, fs->f) != 1) {
472 +               if (ferror(fs->f))
473 +                       perror_msg_and_die("fread");
474 +               memset(bi->b, 0, BLOCKSIZE);
475 +       }
476 +
477 +out:
478 +       *rbi = bi;
479 +       return bi->b;
480  }
481  
482  // return a given inode from a filesystem
483 -static inline inode *
484 -get_nod(filesystem *fs, uint32 nod)
485 +static inline void
486 +put_blk(blk_info *bi)
487 +{
488 +       if (bi->usecount == 0)
489 +               error_msg_and_die("Internal error: put_blk usecount zero");
490 +       bi->usecount--;
491 +       if (bi->usecount == 0)
492 +               /* Free happens in the cache code */
493 +               cache_item_set_unused(&bi->fs->blks, &bi->link);
494 +}
495 +
496 +typedef struct
497  {
498 -       int grp,offset;
499 +       cache_link link;
500 +
501 +       filesystem *fs;
502 +       int gds;
503 +       blk_info *bi;
504 +       groupdescriptor *gd;
505 +       uint32 usecount;
506 +} gd_info;
507 +
508 +#define MAX_FREE_CACHE_GDS 100
509 +
510 +static uint32
511 +gd_elem_val(cache_link *elem)
512 +{
513 +       gd_info *gi = container_of(elem, gd_info, link);
514 +       return gi->gds;
515 +}
516 +
517 +static void
518 +gd_freed(cache_link *elem)
519 +{
520 +       gd_info *gi = container_of(elem, gd_info, link);
521 +
522 +       if (gi->fs->swapit)
523 +               swap_gd(gi->gd);
524 +       put_blk(gi->bi);
525 +       free(gi);
526 +}
527 +
528 +#define GDS_START ((SUPERBLOCK_OFFSET + SUPERBLOCK_SIZE + BLOCKSIZE - 1) / BLOCKSIZE)
529 +#define GDS_PER_BLOCK (BLOCKSIZE / sizeof(groupdescriptor))
530 +// the group descriptors are aligned on the block size
531 +static inline groupdescriptor *
532 +get_gd(filesystem *fs, uint32 no, gd_info **rgi)
533 +{
534 +       uint32 gdblk;
535 +       uint32 offset;
536 +       gd_info *gi;
537 +       cache_link *curr;
538 +
539 +       curr = cache_find(&fs->gds, no);
540 +       if (curr) {
541 +               gi = container_of(curr, gd_info, link);
542 +               gi->usecount++;
543 +               goto out;
544 +       }
545 +
546 +       gi = malloc(sizeof(*gi));
547 +       if (!gi)
548 +               error_msg_and_die("get_gd: out of memory");
549 +       gi->fs = fs;
550 +       gi->gds = no;
551 +       gi->usecount = 1;
552 +       gdblk = GDS_START + (no / GDS_PER_BLOCK);
553 +       offset = no % GDS_PER_BLOCK;
554 +       gi->gd = ((groupdescriptor *) get_blk(fs, gdblk, &gi->bi)) + offset;
555 +       cache_add(&fs->gds, &gi->link);
556 +       if (fs->swapit)
557 +               swap_gd(gi->gd);
558 + out:
559 +       *rgi = gi;
560 +
561 +       return gi->gd;
562 +}
563 +
564 +static inline void
565 +put_gd(gd_info *gi)
566 +{
567 +       if (gi->usecount == 0)
568 +               error_msg_and_die("Internal error: put_gd usecount zero");
569 +
570 +       gi->usecount--;
571 +       if (gi->usecount == 0)
572 +               /* Free happens in the cache code */
573 +               cache_item_set_unused(&gi->fs->gds, &gi->link);
574 +}
575 +
576 +// Used by get_blkmap/put_blkmap to hold information about an block map
577 +// owned by the user.
578 +typedef struct
579 +{
580 +       cache_link link;
581 +
582 +       filesystem *fs;
583 +       uint32 blk;
584 +       uint8 *b;
585 +       blk_info *bi;
586 +       uint32 usecount;
587 +} blkmap_info;
588 +
589 +#define MAX_FREE_CACHE_BLOCKMAPS 100
590 +
591 +static uint32
592 +blkmap_elem_val(cache_link *elem)
593 +{
594 +       blkmap_info *bmi = container_of(elem, blkmap_info, link);
595 +       return bmi->blk;
596 +}
597 +
598 +static void
599 +blkmap_freed(cache_link *elem)
600 +{
601 +       blkmap_info *bmi = container_of(elem, blkmap_info, link);
602 +
603 +       if (bmi->fs->swapit)
604 +               swap_block(bmi->b);
605 +       put_blk(bmi->bi);
606 +       free(bmi);
607 +}
608 +
609 +// Return a given block map from a filesystem.  Make sure to call
610 +// put_blkmap when you are done with it.
611 +static inline uint32 *
612 +get_blkmap(filesystem *fs, uint32 blk, blkmap_info **rbmi)
613 +{
614 +       blkmap_info *bmi;
615 +       cache_link *curr;
616 +
617 +       curr = cache_find(&fs->blkmaps, blk);
618 +       if (curr) {
619 +               bmi = container_of(curr, blkmap_info, link);
620 +               bmi->usecount++;
621 +               goto out;
622 +       }
623 +
624 +       bmi = malloc(sizeof(*bmi));
625 +       if (!bmi)
626 +               error_msg_and_die("get_blkmap: out of memory");
627 +       bmi->fs = fs;
628 +       bmi->blk = blk;
629 +       bmi->b = get_blk(fs, blk, &bmi->bi);
630 +       bmi->usecount = 1;
631 +       cache_add(&fs->blkmaps, &bmi->link);
632 +
633 +       if (fs->swapit)
634 +               swap_block(bmi->b);
635 + out:
636 +       *rbmi = bmi;
637 +       return (uint32 *) bmi->b;
638 +}
639 +
640 +static inline void
641 +put_blkmap(blkmap_info *bmi)
642 +{
643 +       if (bmi->usecount == 0)
644 +               error_msg_and_die("Internal error: put_blkmap usecount zero");
645 +
646 +       bmi->usecount--;
647 +       if (bmi->usecount == 0)
648 +               /* Free happens in the cache code */
649 +               cache_item_set_unused(&bmi->fs->blkmaps, &bmi->link);
650 +}
651 +
652 +// Used by get_nod/put_nod to hold information about an inode owned
653 +// by the user.
654 +typedef struct
655 +{
656 +       cache_link link;
657 +
658 +       filesystem *fs;
659 +       uint32 nod;
660 +       uint8 *b;
661 +       blk_info *bi;
662         inode *itab;
663 +       uint32 usecount;
664 +} nod_info;
665 +
666 +#define MAX_FREE_CACHE_INODES 100
667 +
668 +static uint32
669 +inode_elem_val(cache_link *elem)
670 +{
671 +       nod_info *ni = container_of(elem, nod_info, link);
672 +       return ni->nod;
673 +}
674 +
675 +static void
676 +inode_freed(cache_link *elem)
677 +{
678 +       nod_info *ni = container_of(elem, nod_info, link);
679 +
680 +       if (ni->fs->swapit)
681 +               swap_nod(ni->itab);
682 +       put_blk(ni->bi);
683 +       free(ni);
684 +}
685 +
686 +#define INODES_PER_BLOCK (BLOCKSIZE / sizeof(inode))
687  
688 -       offset = GRP_IBM_OFFSET(fs,nod);
689 +// return a given inode from a filesystem
690 +static inline inode *
691 +get_nod(filesystem *fs, uint32 nod, nod_info **rni)
692 +{
693 +       uint32 grp, boffset, offset;
694 +       cache_link *curr;
695 +       groupdescriptor *gd;
696 +       gd_info *gi;
697 +       nod_info *ni;
698 +
699 +       curr = cache_find(&fs->inodes, nod);
700 +       if (curr) {
701 +               ni = container_of(curr, nod_info, link);
702 +               ni->usecount++;
703 +               goto out;
704 +       }
705 +
706 +       ni = malloc(sizeof(*ni));
707 +       if (!ni)
708 +               error_msg_and_die("get_nod: out of memory");
709 +       ni->fs = fs;
710 +       ni->nod = nod;
711 +       ni->usecount = 1;
712 +       cache_add(&fs->inodes, &ni->link);
713 +
714 +       offset = GRP_IBM_OFFSET(fs,nod) - 1;
715 +       boffset = offset / INODES_PER_BLOCK;
716 +       offset %= INODES_PER_BLOCK;
717         grp = GRP_GROUP_OF_INODE(fs,nod);
718 -       itab = (inode *)get_blk(fs, fs->gd[grp].bg_inode_table);
719 -       return itab+offset-1;
720 +       gd = get_gd(fs, grp, &gi);
721 +       ni->b = get_blk(fs, gd->bg_inode_table + boffset, &ni->bi);
722 +       ni->itab = ((inode *) ni->b) + offset;
723 +       if (fs->swapit)
724 +               swap_nod(ni->itab);
725 +       put_gd(gi);
726 + out:
727 +       *rni = ni;
728 +       return ni->itab;
729 +}
730 +
731 +static inline void
732 +put_nod(nod_info *ni)
733 +{
734 +       if (ni->usecount == 0)
735 +               error_msg_and_die("Internal error: put_nod usecount zero");
736 +
737 +       ni->usecount--;
738 +       if (ni->usecount == 0)
739 +               /* Free happens in the cache code */
740 +               cache_item_set_unused(&ni->fs->inodes, &ni->link);
741 +}
742 +
743 +// Used to hold state information while walking a directory inode.
744 +typedef struct
745 +{
746 +       directory d;
747 +       filesystem *fs;
748 +       uint32 nod;
749 +       directory *last_d;
750 +       uint8 *b;
751 +       blk_info *bi;
752 +} dirwalker;
753 +
754 +// Start a directory walk on the given inode.  You must pass in a
755 +// dirwalker structure, then use that dirwalker for future operations.
756 +// Call put_dir when you are done walking the directory.
757 +static inline directory *
758 +get_dir(filesystem *fs, uint32 nod, dirwalker *dw)
759 +{
760 +       dw->fs = fs;
761 +       dw->b = get_blk(fs, nod, &dw->bi);
762 +       dw->nod = nod;
763 +       dw->last_d = (directory *) dw->b;
764 +
765 +       memcpy(&dw->d, dw->last_d, sizeof(directory));
766 +       if (fs->swapit)
767 +               swap_dir(&dw->d);
768 +       return &dw->d;
769 +}
770 +
771 +// Move to the next directory.
772 +static inline directory *
773 +next_dir(dirwalker *dw)
774 +{
775 +       directory *next_d = (directory *)((int8*)dw->last_d + dw->d.d_rec_len);
776 +
777 +       if (dw->fs->swapit)
778 +               swap_dir(&dw->d);
779 +       memcpy(dw->last_d, &dw->d, sizeof(directory));
780 +
781 +       if (((int8 *) next_d) >= ((int8 *) dw->b + BLOCKSIZE))
782 +               return NULL;
783 +
784 +       dw->last_d = next_d;
785 +       memcpy(&dw->d, next_d, sizeof(directory));
786 +       if (dw->fs->swapit)
787 +               swap_dir(&dw->d);
788 +       return &dw->d;
789 +}
790 +
791 +// Call then when you are done with the directory walk.
792 +static inline void
793 +put_dir(dirwalker *dw)
794 +{
795 +       if (dw->fs->swapit)
796 +               swap_dir(&dw->d);
797 +       memcpy(dw->last_d, &dw->d, sizeof(directory));
798 +
799 +       if (dw->nod == 0)
800 +               free_workblk(dw->b);
801 +       else
802 +               put_blk(dw->bi);
803 +}
804 +
805 +// Create a new directory block with the given inode as it's destination
806 +// and append it to the current dirwalker.
807 +static directory *
808 +new_dir(filesystem *fs, uint32 dnod, const char *name, int nlen, dirwalker *dw)
809 +{
810 +       directory *d;
811 +
812 +       dw->fs = fs;
813 +       dw->b = get_workblk();
814 +       dw->nod = 0;
815 +       dw->last_d = (directory *) dw->b;
816 +       d = &dw->d;
817 +       d->d_inode = dnod;
818 +       d->d_rec_len = BLOCKSIZE;
819 +       d->d_name_len = nlen;
820 +       strncpy(((char *) dw->last_d) + sizeof(directory), name, nlen);
821 +       return d;
822 +}
823 +
824 +// Shrink the current directory entry, make a new one with the free
825 +// space, and return the new directory entry (making it current).
826 +static inline directory *
827 +shrink_dir(dirwalker *dw, uint32 nod, const char *name, int nlen)
828 +{
829 +       int reclen, preclen;
830 +       directory *d = &dw->d;
831 +
832 +       reclen = d->d_rec_len;
833 +       d->d_rec_len = sizeof(directory) + rndup(d->d_name_len, 4);
834 +       preclen = d->d_rec_len;
835 +       reclen -= preclen;
836 +       if (dw->fs->swapit)
837 +               swap_dir(&dw->d);
838 +       memcpy(dw->last_d, &dw->d, sizeof(directory));
839 +
840 +       dw->last_d = (directory *) (((int8 *) dw->last_d) + preclen);
841 +       d->d_rec_len = reclen;
842 +       d->d_inode = nod;
843 +       d->d_name_len = nlen;
844 +       strncpy(((char *) dw->last_d) + sizeof(directory), name, nlen);
845 +
846 +       return d;
847 +}
848 +
849 +// Return the current block the directory is walking
850 +static inline uint8 *
851 +dir_data(dirwalker *dw)
852 +{
853 +       return dw->b;
854 +}
855 +
856 +// Return the pointer to the name for the current directory
857 +static inline char *
858 +dir_name(dirwalker *dw)
859 +{
860 +       return ((char *) dw->last_d) + sizeof(directory);
861 +}
862 +
863 +// Set the name for the current directory.  Note that this doesn't
864 +// verify that there is space for the directory name, you must do
865 +// that yourself.
866 +static void
867 +dir_set_name(dirwalker *dw, const char *name, int nlen)
868 +{
869 +       dw->d.d_name_len = nlen;
870 +       strncpy(((char *) dw->last_d) + sizeof(directory), name, nlen);
871  }
872  
873  // allocate a given block/inode in the bitmap
874 @@ -870,21 +1404,34 @@
875  {
876         uint32 bk=0;
877         uint32 grp,nbgroups;
878 +       blk_info *bi;
879 +       groupdescriptor *gd;
880 +       gd_info *gi;
881  
882         grp = GRP_GROUP_OF_INODE(fs,nod);
883         nbgroups = GRP_NBGROUPS(fs);
884 -       if(!(bk = allocate(get_blk(fs,fs->gd[grp].bg_block_bitmap), 0))) {
885 -               for(grp=0;grp<nbgroups && !bk;grp++)
886 -                       bk=allocate(get_blk(fs,fs->gd[grp].bg_block_bitmap),0);
887 +       gd = get_gd(fs, grp, &gi);
888 +       bk = allocate(GRP_GET_GROUP_BBM(fs, gd, &bi), 0);
889 +       GRP_PUT_GROUP_BBM(bi);
890 +       put_gd(gi);
891 +       if (!bk) {
892 +               for (grp=0; grp<nbgroups && !bk; grp++) {
893 +                       gd = get_gd(fs, grp, &gi);
894 +                       bk = allocate(GRP_GET_GROUP_BBM(fs, gd, &bi), 0);
895 +                       GRP_PUT_GROUP_BBM(bi);
896 +                       put_gd(gi);
897 +               }
898                 grp--;
899         }
900         if (!bk)
901                 error_msg_and_die("couldn't allocate a block (no free space)");
902 -       if(!(fs->gd[grp].bg_free_blocks_count--))
903 +       gd = get_gd(fs, grp, &gi);
904 +       if(!(gd->bg_free_blocks_count--))
905                 error_msg_and_die("group descr %d. free blocks count == 0 (corrupted fs?)",grp);
906 -       if(!(fs->sb.s_free_blocks_count--))
907 +       put_gd(gi);
908 +       if(!(fs->sb->s_free_blocks_count--))
909                 error_msg_and_die("superblock free blocks count == 0 (corrupted fs?)");
910 -       return fs->sb.s_blocks_per_group*grp + bk;
911 +       return fs->sb->s_first_data_block + fs->sb->s_blocks_per_group*grp + (bk-1);
912  }
913  
914  // free a block
915 @@ -892,12 +1439,18 @@
916  free_blk(filesystem *fs, uint32 bk)
917  {
918         uint32 grp;
919 -
920 -       grp = bk / fs->sb.s_blocks_per_group;
921 -       bk %= fs->sb.s_blocks_per_group;
922 -       deallocate(get_blk(fs,fs->gd[grp].bg_block_bitmap), bk);
923 -       fs->gd[grp].bg_free_blocks_count++;
924 -       fs->sb.s_free_blocks_count++;
925 +       blk_info *bi;
926 +       gd_info *gi;
927 +       groupdescriptor *gd;
928 +
929 +       grp = bk / fs->sb->s_blocks_per_group;
930 +       bk %= fs->sb->s_blocks_per_group;
931 +       gd = get_gd(fs, grp, &gi);
932 +       deallocate(GRP_GET_GROUP_BBM(fs, gd, &bi), bk);
933 +       GRP_PUT_GROUP_BBM(bi);
934 +       gd->bg_free_blocks_count++;
935 +       put_gd(gi);
936 +       fs->sb->s_free_blocks_count++;
937  }
938  
939  // allocate an inode
940 @@ -906,6 +1459,9 @@
941  {
942         uint32 nod,best_group=0;
943         uint32 grp,nbgroups,avefreei;
944 +       blk_info *bi;
945 +       gd_info *gi, *bestgi;
946 +       groupdescriptor *gd, *bestgd;
947  
948         nbgroups = GRP_NBGROUPS(fs);
949  
950 @@ -914,22 +1470,32 @@
951         /* find the one with the most free blocks and allocate node there     */
952         /* Idea from find_group_dir in fs/ext2/ialloc.c in 2.4.19 kernel      */
953         /* We do it for all inodes.                                           */
954 -       avefreei  =  fs->sb.s_free_inodes_count / nbgroups;
955 +       avefreei  =  fs->sb->s_free_inodes_count / nbgroups;
956 +       bestgd = get_gd(fs, best_group, &bestgi);
957         for(grp=0; grp<nbgroups; grp++) {
958 -               if (fs->gd[grp].bg_free_inodes_count < avefreei ||
959 -                   fs->gd[grp].bg_free_inodes_count == 0)
960 +               gd = get_gd(fs, grp, &gi);
961 +               if (gd->bg_free_inodes_count < avefreei ||
962 +                   gd->bg_free_inodes_count == 0) {
963 +                       put_gd(gi);
964                         continue;
965 -               if (!best_group || 
966 -                       fs->gd[grp].bg_free_blocks_count > fs->gd[best_group].bg_free_blocks_count)
967 +               }
968 +               if (!best_group || gd->bg_free_blocks_count > bestgd->bg_free_blocks_count) {
969 +                       put_gd(bestgi);
970                         best_group = grp;
971 +                       bestgd = gd;
972 +                       bestgi = gi;
973 +               } else
974 +                       put_gd(gi);
975         }
976 -       if (!(nod = allocate(get_blk(fs,fs->gd[best_group].bg_inode_bitmap),0)))
977 +       if (!(nod = allocate(GRP_GET_GROUP_IBM(fs, bestgd, &bi), 0)))
978                 error_msg_and_die("couldn't allocate an inode (no free inode)");
979 -       if(!(fs->gd[best_group].bg_free_inodes_count--))
980 +       GRP_PUT_GROUP_IBM(bi);
981 +       if(!(bestgd->bg_free_inodes_count--))
982                 error_msg_and_die("group descr. free blocks count == 0 (corrupted fs?)");
983 -       if(!(fs->sb.s_free_inodes_count--))
984 +       put_gd(bestgi);
985 +       if(!(fs->sb->s_free_inodes_count--))
986                 error_msg_and_die("superblock free blocks count == 0 (corrupted fs?)");
987 -       return fs->sb.s_inodes_per_group*best_group+nod;
988 +       return fs->sb->s_inodes_per_group*best_group+nod;
989  }
990  
991  // print a bitmap allocation
992 @@ -962,30 +1528,40 @@
993  //                               used after being freed, so once you start
994  //                               freeing blocks don't stop until the end of
995  //                               the file. moreover, i_blocks isn't updated.
996 -//                               in fact, don't do that, just use extend_blk
997  // if hole!=0, create a hole in the file
998  static uint32
999  walk_bw(filesystem *fs, uint32 nod, blockwalker *bw, int32 *create, uint32 hole)
1000  {
1001         uint32 *bkref = 0;
1002 +       uint32 bk = 0;
1003 +       blkmap_info *bmi1 = NULL, *bmi2 = NULL, *bmi3 = NULL;
1004         uint32 *b;
1005         int extend = 0, reduce = 0;
1006 +       inode *inod;
1007 +       nod_info *ni;
1008 +       uint32 *iblk;
1009 +
1010         if(create && (*create) < 0)
1011                 reduce = 1;
1012 -       if(bw->bnum >= get_nod(fs, nod)->i_blocks / INOBLK)
1013 +       inod = get_nod(fs, nod, &ni);
1014 +       if(bw->bnum >= inod->i_blocks / INOBLK)
1015         {
1016                 if(create && (*create) > 0)
1017                 {
1018                         (*create)--;
1019                         extend = 1;
1020                 }
1021 -               else    
1022 +               else
1023 +               {
1024 +                       put_nod(ni);
1025                         return WALK_END;
1026 +               }
1027         }
1028 +       iblk = inod->i_block;
1029         // first direct block
1030         if(bw->bpdir == EXT2_INIT_BLOCK)
1031         {
1032 -               bkref = &get_nod(fs, nod)->i_block[bw->bpdir = 0];
1033 +               bkref = &iblk[bw->bpdir = 0];
1034                 if(extend) // allocate first block
1035                         *bkref = hole ? 0 : alloc_blk(fs,nod);
1036                 if(reduce) // free first block
1037 @@ -994,7 +1570,7 @@
1038         // direct block
1039         else if(bw->bpdir < EXT2_NDIR_BLOCKS)
1040         {
1041 -               bkref = &get_nod(fs, nod)->i_block[++bw->bpdir];
1042 +               bkref = &iblk[++bw->bpdir];
1043                 if(extend) // allocate block
1044                         *bkref = hole ? 0 : alloc_blk(fs,nod);
1045                 if(reduce) // free block
1046 @@ -1007,10 +1583,10 @@
1047                 bw->bpdir = EXT2_IND_BLOCK;
1048                 bw->bpind = 0;
1049                 if(extend) // allocate indirect block
1050 -                       get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod);
1051 +                       iblk[bw->bpdir] = alloc_blk(fs,nod);
1052                 if(reduce) // free indirect block
1053 -                       free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1054 -               b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1055 +                       free_blk(fs, iblk[bw->bpdir]);
1056 +               b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
1057                 bkref = &b[bw->bpind];
1058                 if(extend) // allocate first block
1059                         *bkref = hole ? 0 : alloc_blk(fs,nod);
1060 @@ -1021,7 +1597,7 @@
1061         else if((bw->bpdir == EXT2_IND_BLOCK) && (bw->bpind < BLOCKSIZE/4 - 1))
1062         {
1063                 bw->bpind++;
1064 -               b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1065 +               b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
1066                 bkref = &b[bw->bpind];
1067                 if(extend) // allocate block
1068                         *bkref = hole ? 0 : alloc_blk(fs,nod);
1069 @@ -1036,15 +1612,15 @@
1070                 bw->bpind = 0;
1071                 bw->bpdind = 0;
1072                 if(extend) // allocate double indirect block
1073 -                       get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod);
1074 +                       iblk[bw->bpdir] = alloc_blk(fs,nod);
1075                 if(reduce) // free double indirect block
1076 -                       free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1077 -               b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1078 +                       free_blk(fs, iblk[bw->bpdir]);
1079 +               b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
1080                 if(extend) // allocate first indirect block
1081                         b[bw->bpind] = alloc_blk(fs,nod);
1082                 if(reduce) // free  firstindirect block
1083                         free_blk(fs, b[bw->bpind]);
1084 -               b = (uint32*)get_blk(fs, b[bw->bpind]);
1085 +               b = get_blkmap(fs, b[bw->bpind], &bmi2);
1086                 bkref = &b[bw->bpdind];
1087                 if(extend) // allocate first block
1088                         *bkref = hole ? 0 : alloc_blk(fs,nod);
1089 @@ -1055,8 +1631,8 @@
1090         else if((bw->bpdir == EXT2_DIND_BLOCK) && (bw->bpdind < BLOCKSIZE/4 - 1))
1091         {
1092                 bw->bpdind++;
1093 -               b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1094 -               b = (uint32*)get_blk(fs, b[bw->bpind]);
1095 +               b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
1096 +               b = get_blkmap(fs, b[bw->bpind], &bmi2);
1097                 bkref = &b[bw->bpdind];
1098                 if(extend) // allocate block
1099                         *bkref = hole ? 0 : alloc_blk(fs,nod);
1100 @@ -1069,12 +1645,12 @@
1101                 bw->bnum++;
1102                 bw->bpdind = 0;
1103                 bw->bpind++;
1104 -               b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1105 +               b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
1106                 if(extend) // allocate indirect block
1107                         b[bw->bpind] = alloc_blk(fs,nod);
1108                 if(reduce) // free indirect block
1109                         free_blk(fs, b[bw->bpind]);
1110 -               b = (uint32*)get_blk(fs, b[bw->bpind]);
1111 +               b = get_blkmap(fs, b[bw->bpind], &bmi2);
1112                 bkref = &b[bw->bpdind];
1113                 if(extend) // allocate first block
1114                         *bkref = hole ? 0 : alloc_blk(fs,nod);
1115 @@ -1094,20 +1670,20 @@
1116                 bw->bpdind = 0;
1117                 bw->bptind = 0;
1118                 if(extend) // allocate triple indirect block
1119 -                       get_nod(fs, nod)->i_block[bw->bpdir] = alloc_blk(fs,nod);
1120 +                       iblk[bw->bpdir] = alloc_blk(fs,nod);
1121                 if(reduce) // free triple indirect block
1122 -                       free_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1123 -               b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1124 +                       free_blk(fs, iblk[bw->bpdir]);
1125 +               b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
1126                 if(extend) // allocate first double indirect block
1127                         b[bw->bpind] = alloc_blk(fs,nod);
1128                 if(reduce) // free first double indirect block
1129                         free_blk(fs, b[bw->bpind]);
1130 -               b = (uint32*)get_blk(fs, b[bw->bpind]);
1131 +               b = get_blkmap(fs, b[bw->bpind], &bmi2);
1132                 if(extend) // allocate first indirect block
1133                         b[bw->bpdind] = alloc_blk(fs,nod);
1134                 if(reduce) // free first indirect block
1135                         free_blk(fs, b[bw->bpind]);
1136 -               b = (uint32*)get_blk(fs, b[bw->bpdind]);
1137 +               b = get_blkmap(fs, b[bw->bpdind], &bmi3);
1138                 bkref = &b[bw->bptind];
1139                 if(extend) // allocate first data block
1140                         *bkref = hole ? 0 : alloc_blk(fs,nod);
1141 @@ -1121,9 +1697,9 @@
1142                   (bw->bptind < BLOCKSIZE/4 -1) )
1143         {
1144                 bw->bptind++;
1145 -               b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1146 -               b = (uint32*)get_blk(fs, b[bw->bpind]);
1147 -               b = (uint32*)get_blk(fs, b[bw->bpdind]);
1148 +               b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
1149 +               b = get_blkmap(fs, b[bw->bpind], &bmi2);
1150 +               b = get_blkmap(fs, b[bw->bpdind], &bmi3);
1151                 bkref = &b[bw->bptind];
1152                 if(extend) // allocate data block
1153                         *bkref = hole ? 0 : alloc_blk(fs,nod);
1154 @@ -1140,13 +1716,13 @@
1155                 bw->bnum++;
1156                 bw->bptind = 0;
1157                 bw->bpdind++;
1158 -               b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1159 -               b = (uint32*)get_blk(fs, b[bw->bpind]);
1160 +               b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
1161 +               b = get_blkmap(fs, b[bw->bpind], &bmi2);
1162                 if(extend) // allocate single indirect block
1163                         b[bw->bpdind] = alloc_blk(fs,nod);
1164                 if(reduce) // free indirect block
1165                         free_blk(fs, b[bw->bpind]);
1166 -               b = (uint32*)get_blk(fs, b[bw->bpdind]);
1167 +               b = get_blkmap(fs, b[bw->bpdind], &bmi3);
1168                 bkref = &b[bw->bptind];
1169                 if(extend) // allocate first data block
1170                         *bkref = hole ? 0 : alloc_blk(fs,nod);
1171 @@ -1163,17 +1739,17 @@
1172                 bw->bpdind = 0;
1173                 bw->bptind = 0;
1174                 bw->bpind++;
1175 -               b = (uint32*)get_blk(fs, get_nod(fs, nod)->i_block[bw->bpdir]);
1176 +               b = get_blkmap(fs, iblk[bw->bpdir], &bmi1);
1177                 if(extend) // allocate double indirect block
1178                         b[bw->bpind] = alloc_blk(fs,nod);
1179                 if(reduce) // free double indirect block
1180                         free_blk(fs, b[bw->bpind]);
1181 -               b = (uint32*)get_blk(fs, b[bw->bpind]);
1182 +               b = get_blkmap(fs, b[bw->bpind], &bmi2);
1183                 if(extend) // allocate single indirect block
1184                         b[bw->bpdind] = alloc_blk(fs,nod);
1185                 if(reduce) // free indirect block
1186                         free_blk(fs, b[bw->bpind]);
1187 -               b = (uint32*)get_blk(fs, b[bw->bpdind]);
1188 +               b = get_blkmap(fs, b[bw->bpdind], &bmi3);
1189                 bkref = &b[bw->bptind];
1190                 if(extend) // allocate first block
1191                         *bkref = hole ? 0 : alloc_blk(fs,nod);
1192 @@ -1184,56 +1760,105 @@
1193                 error_msg_and_die("file too big !"); 
1194         /* End change for walking triple indirection */
1195  
1196 -       if(*bkref)
1197 -       {
1198 +       bk = *bkref;
1199 +       if (bmi3)
1200 +               put_blkmap(bmi3);
1201 +       if (bmi2)
1202 +               put_blkmap(bmi2);
1203 +       if (bmi1)
1204 +               put_blkmap(bmi1);
1205 +
1206 +       if(bk)
1207 +       {
1208 +               blk_info *bi;
1209 +               gd_info *gi;
1210 +               uint8 *block;
1211                 bw->bnum++;
1212 -               if(!reduce && !allocated(GRP_GET_BLOCK_BITMAP(fs,*bkref), GRP_BBM_OFFSET(fs,*bkref)))
1213 -                       error_msg_and_die("[block %d of inode %d is unallocated !]", *bkref, nod);
1214 +               block = GRP_GET_BLOCK_BITMAP(fs,bk,&bi,&gi);
1215 +               if(!reduce && !allocated(block, GRP_BBM_OFFSET(fs,bk)))
1216 +                       error_msg_and_die("[block %d of inode %d is unallocated !]", bk, nod);
1217 +               GRP_PUT_BLOCK_BITMAP(bi, gi);
1218         }
1219         if(extend)
1220 -               get_nod(fs, nod)->i_blocks = bw->bnum * INOBLK;
1221 -       return *bkref;
1222 +               inod->i_blocks = bw->bnum * INOBLK;
1223 +       put_nod(ni);
1224 +       return bk;
1225  }
1226  
1227 -// add blocks to an inode (file/dir/etc...)
1228 -static void
1229 -extend_blk(filesystem *fs, uint32 nod, block b, int amount)
1230 +typedef struct
1231  {
1232 -       int create = amount;
1233 -       blockwalker bw, lbw;
1234 -       uint32 bk;
1235 -       init_bw(&bw);
1236 -       if(amount < 0)
1237 -       {
1238 -               uint32 i;
1239 -               for(i = 0; i < get_nod(fs, nod)->i_blocks / INOBLK + amount; i++)
1240 -                       walk_bw(fs, nod, &bw, 0, 0);
1241 -               while(walk_bw(fs, nod, &bw, &create, 0) != WALK_END)
1242 +       blockwalker bw;
1243 +       uint32 nod;
1244 +       nod_info *ni;
1245 +       inode *inod;
1246 +} inode_pos;
1247 +#define INODE_POS_TRUNCATE 0
1248 +#define INODE_POS_EXTEND 1
1249 +
1250 +// Call this to set up an ipos structure for future use with
1251 +// extend_inode_blk to append blocks to the given inode.  If
1252 +// op is INODE_POS_TRUNCATE, the inode is truncated to zero size.
1253 +// If op is INODE_POS_EXTEND, the position is moved to the end
1254 +// of the inode's data blocks.
1255 +// Call inode_pos_finish when done with the inode_pos structure.
1256 +static void
1257 +inode_pos_init(filesystem *fs, inode_pos *ipos, uint32 nod, int op,
1258 +              blockwalker *endbw)
1259 +{
1260 +       blockwalker lbw;
1261 +
1262 +       init_bw(&ipos->bw);
1263 +       ipos->nod = nod;
1264 +       ipos->inod = get_nod(fs, nod, &ipos->ni);
1265 +       if (op == INODE_POS_TRUNCATE) {
1266 +               int32 create = -1;
1267 +               while(walk_bw(fs, nod, &ipos->bw, &create, 0) != WALK_END)
1268                         /*nop*/;
1269 -               get_nod(fs, nod)->i_blocks += amount * INOBLK;
1270 +               ipos->inod->i_blocks = 0;
1271         }
1272 -       else
1273 +
1274 +       if (endbw)
1275 +               ipos->bw = *endbw;
1276 +       else {
1277 +               /* Seek to the end */
1278 +               init_bw(&ipos->bw);
1279 +               lbw = ipos->bw;
1280 +               while(walk_bw(fs, nod, &ipos->bw, 0, 0) != WALK_END)
1281 +                       lbw = ipos->bw;
1282 +               ipos->bw = lbw;
1283 +       }
1284 +}
1285 +
1286 +// Clean up the inode_pos structure.
1287 +static void
1288 +inode_pos_finish(filesystem *fs, inode_pos *ipos)
1289 +{
1290 +       put_nod(ipos->ni);
1291 +}
1292 +
1293 +// add blocks to an inode (file/dir/etc...) at the given position.
1294 +// This will only work when appending to the end of an inode.
1295 +static void
1296 +extend_inode_blk(filesystem *fs, inode_pos *ipos, block b, int amount)
1297 +{
1298 +       uint32 bk;
1299 +       uint32 pos;
1300 +
1301 +       if (amount < 0)
1302 +               error_msg_and_die("extend_inode_blk: Got negative amount");
1303 +
1304 +       for (pos = 0; amount; pos += BLOCKSIZE)
1305         {
1306 -               lbw = bw;
1307 -               while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
1308 -                       lbw = bw;
1309 -               bw = lbw;
1310 -               while(create)
1311 -               {
1312 -                       int i, copyb = 0;
1313 -                       if(!(fs->sb.s_reserved[200] & OP_HOLES))
1314 -                               copyb = 1;
1315 -                       else
1316 -                               for(i = 0; i < BLOCKSIZE / 4; i++)
1317 -                                       if(((int32*)(b + BLOCKSIZE * (amount - create)))[i])
1318 -                                       {
1319 -                                               copyb = 1;
1320 -                                               break;
1321 -                                       }
1322 -                       if((bk = walk_bw(fs, nod, &bw, &create, !copyb)) == WALK_END)
1323 -                               break;
1324 -                       if(copyb)
1325 -                               memcpy(get_blk(fs, bk), b + BLOCKSIZE * (amount - create - 1), BLOCKSIZE);
1326 +               int hole = (fs->holes && is_blk_empty(b + pos));
1327 +
1328 +               bk = walk_bw(fs, ipos->nod, &ipos->bw, &amount, hole);
1329 +               if (bk == WALK_END)
1330 +                       error_msg_and_die("extend_inode_blk: extend failed");
1331 +               if (!hole) {
1332 +                       blk_info *bi;
1333 +                       uint8 *block = get_blk(fs, bk, &bi);
1334 +                       memcpy(block, b + pos, BLOCKSIZE);
1335 +                       put_blk(bi);
1336                 }
1337         }
1338  }
1339 @@ -1242,15 +1867,17 @@
1340  static void
1341  add2dir(filesystem *fs, uint32 dnod, uint32 nod, const char* name)
1342  {
1343 -       blockwalker bw;
1344 +       blockwalker bw, lbw;
1345         uint32 bk;
1346 -       uint8 *b;
1347         directory *d;
1348 +       dirwalker dw;
1349         int reclen, nlen;
1350         inode *node;
1351         inode *pnode;
1352 +       nod_info *dni, *ni;
1353 +       inode_pos ipos;
1354  
1355 -       pnode = get_nod(fs, dnod);
1356 +       pnode = get_nod(fs, dnod, &dni);
1357         if((pnode->i_mode & FM_IFMT) != FM_IFDIR)
1358                 error_msg_and_die("can't add '%s' to a non-directory", name);
1359         if(!*name)
1360 @@ -1262,52 +1889,52 @@
1361         if(reclen > BLOCKSIZE)
1362                 error_msg_and_die("bad name '%s' (too long)", name);
1363         init_bw(&bw);
1364 +       lbw = bw;
1365         while((bk = walk_bw(fs, dnod, &bw, 0, 0)) != WALK_END) // for all blocks in dir
1366         {
1367 -               b = get_blk(fs, bk);
1368                 // for all dir entries in block
1369 -               for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
1370 +               for(d = get_dir(fs, bk, &dw); d; d = next_dir(&dw))
1371                 {
1372                         // if empty dir entry, large enough, use it
1373                         if((!d->d_inode) && (d->d_rec_len >= reclen))
1374                         {
1375                                 d->d_inode = nod;
1376 -                               node = get_nod(fs, nod);
1377 +                               node = get_nod(fs, nod, &ni);
1378 +                               dir_set_name(&dw, name, nlen);
1379 +                               put_dir(&dw);
1380                                 node->i_links_count++;
1381 -                               d->d_name_len = nlen;
1382 -                               strncpy(d->d_name, name, nlen);
1383 -                               return;
1384 +                               put_nod(ni);
1385 +                               goto out;
1386                         }
1387                         // if entry with enough room (last one?), shrink it & use it
1388                         if(d->d_rec_len >= (sizeof(directory) + rndup(d->d_name_len, 4) + reclen))
1389                         {
1390 -                               reclen = d->d_rec_len;
1391 -                               d->d_rec_len = sizeof(directory) + rndup(d->d_name_len, 4);
1392 -                               reclen -= d->d_rec_len;
1393 -                               d = (directory*) (((int8*)d) + d->d_rec_len);
1394 -                               d->d_rec_len = reclen;
1395 -                               d->d_inode = nod;
1396 -                               node = get_nod(fs, nod);
1397 +                               d = shrink_dir(&dw, nod, name, nlen);
1398 +                               put_dir(&dw);
1399 +                               node = get_nod(fs, nod, &ni);
1400                                 node->i_links_count++;
1401 -                               d->d_name_len = nlen;
1402 -                               strncpy(d->d_name, name, nlen);
1403 -                               return;
1404 +                               put_nod(ni);
1405 +                               goto out;
1406                         }
1407                 }
1408 +               put_dir(&dw);
1409 +               lbw = bw;
1410         }
1411         // we found no free entry in the directory, so we add a block
1412 -       if(!(b = get_workblk()))
1413 -               error_msg_and_die("get_workblk() failed.");
1414 -       d = (directory*)b;
1415 -       d->d_inode = nod;
1416 -       node = get_nod(fs, nod);
1417 +       node = get_nod(fs, nod, &ni);
1418 +       d = new_dir(fs, nod, name, nlen, &dw);
1419         node->i_links_count++;
1420 -       d->d_rec_len = BLOCKSIZE;
1421 -       d->d_name_len = nlen;
1422 -       strncpy(d->d_name, name, nlen);
1423 -       extend_blk(fs, dnod, b, 1);
1424 -       get_nod(fs, dnod)->i_size += BLOCKSIZE;
1425 -       free_workblk(b);
1426 +       put_nod(ni);
1427 +       next_dir(&dw); // Force the data into the buffer
1428 +
1429 +       inode_pos_init(fs, &ipos, dnod, INODE_POS_EXTEND, &lbw);
1430 +       extend_inode_blk(fs, &ipos, dir_data(&dw), 1);
1431 +       inode_pos_finish(fs, &ipos);
1432 +
1433 +       put_dir(&dw);
1434 +       pnode->i_size += BLOCKSIZE;
1435 +out:
1436 +       put_nod(dni);
1437  }
1438  
1439  // find an entry in a directory
1440 @@ -1321,11 +1948,13 @@
1441         while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
1442         {
1443                 directory *d;
1444 -               uint8 *b;
1445 -               b = get_blk(fs, bk);
1446 -               for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
1447 -                       if(d->d_inode && (nlen == d->d_name_len) && !strncmp(d->d_name, name, nlen))
1448 +               dirwalker dw;
1449 +               for (d = get_dir(fs, bk, &dw); d; d=next_dir(&dw))
1450 +                       if(d->d_inode && (nlen == d->d_name_len) && !strncmp(dir_name(&dw), name, nlen)) {
1451 +                               put_dir(&dw);
1452                                 return d->d_inode;
1453 +                       }
1454 +               put_dir(&dw);
1455         }
1456         return 0;
1457  }
1458 @@ -1356,47 +1985,55 @@
1459         return nod;
1460  }
1461  
1462 +// chmod an inode
1463 +void
1464 +chmod_fs(filesystem *fs, uint32 nod, uint16 mode, uint16 uid, uint16 gid)
1465 +{
1466 +       inode *node;
1467 +       nod_info *ni;
1468 +       node = get_nod(fs, nod, &ni);
1469 +       node->i_mode = (node->i_mode & ~FM_IMASK) | (mode & FM_IMASK);
1470 +       node->i_uid = uid;
1471 +       node->i_gid = gid;
1472 +       put_nod(ni);
1473 +}
1474 +
1475  // create a simple inode
1476  static uint32
1477  mknod_fs(filesystem *fs, uint32 parent_nod, const char *name, uint16 mode, uint16 uid, uint16 gid, uint8 major, uint8 minor, uint32 ctime, uint32 mtime)
1478  {
1479         uint32 nod;
1480         inode *node;
1481 -       if((nod = find_dir(fs, parent_nod, name)))
1482 -       {
1483 -               node = get_nod(fs, nod);
1484 -               if((node->i_mode & FM_IFMT) != (mode & FM_IFMT))
1485 -                       error_msg_and_die("node '%s' already exists and isn't of the same type", name);
1486 -               node->i_mode = mode;
1487 -       }
1488 -       else
1489 +       nod_info *ni;
1490 +       gd_info *gi;
1491 +
1492 +       nod = alloc_nod(fs);
1493 +       node = get_nod(fs, nod, &ni);
1494 +       node->i_mode = mode;
1495 +       add2dir(fs, parent_nod, nod, name);
1496 +       switch(mode & FM_IFMT)
1497         {
1498 -               nod = alloc_nod(fs);
1499 -               node = get_nod(fs, nod);
1500 -               node->i_mode = mode;
1501 -               add2dir(fs, parent_nod, nod, name);
1502 -               switch(mode & FM_IFMT)
1503 -               {
1504 -                       case FM_IFLNK:
1505 -                               mode = FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO;
1506 -                               break;
1507 -                       case FM_IFBLK:
1508 -                       case FM_IFCHR:
1509 -                               ((uint8*)get_nod(fs, nod)->i_block)[0] = minor;
1510 -                               ((uint8*)get_nod(fs, nod)->i_block)[1] = major;
1511 -                               break;
1512 -                       case FM_IFDIR:
1513 -                               add2dir(fs, nod, nod, ".");
1514 -                               add2dir(fs, nod, parent_nod, "..");
1515 -                               fs->gd[GRP_GROUP_OF_INODE(fs,nod)].bg_used_dirs_count++;
1516 -                               break;
1517 -               }
1518 +       case FM_IFLNK:
1519 +               mode = FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO;
1520 +               break;
1521 +       case FM_IFBLK:
1522 +       case FM_IFCHR:
1523 +               ((uint8*)node->i_block)[0] = minor;
1524 +               ((uint8*)node->i_block)[1] = major;
1525 +               break;
1526 +       case FM_IFDIR:
1527 +               add2dir(fs, nod, nod, ".");
1528 +               add2dir(fs, nod, parent_nod, "..");
1529 +               get_gd(fs,GRP_GROUP_OF_INODE(fs,nod),&gi)->bg_used_dirs_count++;
1530 +               put_gd(gi);
1531 +               break;
1532         }
1533         node->i_uid = uid;
1534         node->i_gid = gid;
1535         node->i_atime = mtime;
1536         node->i_ctime = ctime;
1537         node->i_mtime = mtime;
1538 +       put_nod(ni);
1539         return nod;
1540  }
1541  
1542 @@ -1413,33 +2050,73 @@
1543  mklink_fs(filesystem *fs, uint32 parent_nod, const char *name, size_t size, uint8 *b, uid_t uid, gid_t gid, uint32 ctime, uint32 mtime)
1544  {
1545         uint32 nod = mknod_fs(fs, parent_nod, name, FM_IFLNK | FM_IRWXU | FM_IRWXG | FM_IRWXO, uid, gid, 0, 0, ctime, mtime);
1546 -       extend_blk(fs, nod, 0, - (int)get_nod(fs, nod)->i_blocks / INOBLK);
1547 -       get_nod(fs, nod)->i_size = size;
1548 -       if(size <= 4 * (EXT2_TIND_BLOCK+1))
1549 -       {
1550 -               strncpy((char*)get_nod(fs, nod)->i_block, (char*)b, size);
1551 +       nod_info *ni;
1552 +       inode *node = get_nod(fs, nod, &ni);
1553 +       inode_pos ipos;
1554 +
1555 +       inode_pos_init(fs, &ipos, nod, INODE_POS_TRUNCATE, NULL);
1556 +       node->i_size = size;
1557 +       if(size < 4 * (EXT2_TIND_BLOCK+1))
1558 +       {
1559 +               strncpy((char*)node->i_block, (char*)b, size);
1560 +               ((char*)node->i_block)[size+1] = '\0';
1561 +               inode_pos_finish(fs, &ipos);
1562 +               put_nod(ni);
1563                 return nod;
1564         }
1565 -       extend_blk(fs, nod, b, rndup(size, BLOCKSIZE) / BLOCKSIZE);
1566 +       extend_inode_blk(fs, &ipos, b, rndup(size, BLOCKSIZE) / BLOCKSIZE);
1567 +       inode_pos_finish(fs, &ipos);
1568 +       put_nod(ni);
1569         return nod;
1570  }
1571  
1572 +static void
1573 +fs_upgrade_rev1_largefile(filesystem *fs)
1574 +{
1575 +       fs->sb->s_rev_level = 1;
1576 +       fs->sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO;
1577 +       fs->sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
1578 +}
1579 +
1580 +#define COPY_BLOCKS 16
1581 +#define CB_SIZE (COPY_BLOCKS * BLOCKSIZE)
1582 +
1583  // make a file from a FILE*
1584  static uint32
1585 -mkfile_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode, size_t size, FILE *f, uid_t uid, gid_t gid, uint32 ctime, uint32 mtime)
1586 +mkfile_fs(filesystem *fs, uint32 parent_nod, const char *name, uint32 mode, FILE *f, uid_t uid, gid_t gid, uint32 ctime, uint32 mtime)
1587  {
1588         uint8 * b;
1589         uint32 nod = mknod_fs(fs, parent_nod, name, mode|FM_IFREG, uid, gid, 0, 0, ctime, mtime);
1590 -       extend_blk(fs, nod, 0, - (int)get_nod(fs, nod)->i_blocks / INOBLK);
1591 -       get_nod(fs, nod)->i_size = size;
1592 -       if (size) {
1593 -               if(!(b = (uint8*)calloc(rndup(size, BLOCKSIZE), 1)))
1594 -                       error_msg_and_die("not enough mem to read file '%s'", name);
1595 -               if(f)
1596 -                       fread(b, size, 1, f); // FIXME: ugly. use mmap() ...
1597 -               extend_blk(fs, nod, b, rndup(size, BLOCKSIZE) / BLOCKSIZE);
1598 -               free(b);
1599 -       }
1600 +       nod_info *ni;
1601 +       inode *node = get_nod(fs, nod, &ni);
1602 +       off_t size = 0;
1603 +       size_t readbytes;
1604 +       inode_pos ipos;
1605 +       int fullsize;
1606 +
1607 +       b = malloc(CB_SIZE);
1608 +       if (!b)
1609 +               error_msg_and_die("mkfile_fs: out of memory");
1610 +       inode_pos_init(fs, &ipos, nod, INODE_POS_TRUNCATE, NULL);
1611 +       readbytes = fread(b, 1, CB_SIZE, f);
1612 +       while (readbytes) {
1613 +               fullsize = rndup(readbytes, BLOCKSIZE);
1614 +               // Fill to end of block with zeros.
1615 +               memset(b + readbytes, 0, fullsize - readbytes);
1616 +               extend_inode_blk(fs, &ipos, b, fullsize / BLOCKSIZE);
1617 +               size += readbytes;
1618 +               readbytes = fread(b, 1, CB_SIZE, f);
1619 +       }
1620 +       if (size > 0x7fffffff) {
1621 +               if (fs->sb->s_rev_level < 1)
1622 +                       fs_upgrade_rev1_largefile(fs);
1623 +               fs->sb->s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
1624 +       }
1625 +       node->i_dir_acl = size >> 32;
1626 +       node->i_size = size;
1627 +       inode_pos_finish(fs, &ipos);
1628 +       put_nod(ni);
1629 +       free(b);
1630         return nod;
1631  }
1632  
1633 @@ -1591,13 +2268,24 @@
1634                                 dname = malloc(len + 1);
1635                                 for(i = start; i < count; i++)
1636                                 {
1637 +                                       uint32 oldnod;
1638                                         SNPRINTF(dname, len, "%s%lu", name, i);
1639 -                                       mknod_fs(fs, nod, dname, mode, uid, gid, major, minor + (i * increment - start), ctime, mtime);
1640 +                                       oldnod = find_dir(fs, nod, dname);
1641 +                                       if(oldnod)
1642 +                                               chmod_fs(fs, oldnod, mode, uid, gid);
1643 +                                       else
1644 +                                               mknod_fs(fs, nod, dname, mode, uid, gid, major, minor + (i * increment - start), ctime, mtime);
1645                                 }
1646                                 free(dname);
1647                         }
1648                         else
1649 -                               mknod_fs(fs, nod, name, mode, uid, gid, major, minor, ctime, mtime);
1650 +                       {
1651 +                               uint32 oldnod = find_dir(fs, nod, name);
1652 +                               if(oldnod)
1653 +                                       chmod_fs(fs, oldnod, mode, uid, gid);
1654 +                               else
1655 +                                       mknod_fs(fs, nod, name, mode, uid, gid, major, minor, ctime, mtime);
1656 +                       }
1657                 }
1658         }
1659         if (line)
1660 @@ -1643,6 +2331,10 @@
1661                         switch(st.st_mode & S_IFMT)
1662                         {
1663                                 case S_IFLNK:
1664 +                                       if((st.st_mode & S_IFMT) == S_IFREG || st.st_size >= 4 * (EXT2_TIND_BLOCK+1))
1665 +                                               stats->nblocks += (st.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
1666 +                                       stats->ninodes++;
1667 +                                       break;
1668                                 case S_IFREG:
1669                                         if((st.st_mode & S_IFMT) == S_IFREG || st.st_size > 4 * (EXT2_TIND_BLOCK+1))
1670                                                 stats->nblocks += (st.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
1671 @@ -1657,19 +2349,33 @@
1672                                         if(chdir(dent->d_name) < 0)
1673                                                 perror_msg_and_die(dent->d_name);
1674                                         add2fs_from_dir(fs, this_nod, squash_uids, squash_perms, fs_timestamp, stats);
1675 -                                       chdir("..");
1676 +                                       if (chdir("..") == -1)
1677 +                                               perror_msg_and_die("..");
1678 +
1679                                         break;
1680                                 default:
1681                                         break;
1682                         }
1683                 else
1684                 {
1685 +                       if((nod = find_dir(fs, this_nod, name)))
1686 +                       {
1687 +                               error_msg("ignoring duplicate entry %s", name);
1688 +                               if(S_ISDIR(st.st_mode)) {
1689 +                                       if(chdir(dent->d_name) < 0)
1690 +                                               perror_msg_and_die(name);
1691 +                                       add2fs_from_dir(fs, nod, squash_uids, squash_perms, fs_timestamp, stats);
1692 +                                       if (chdir("..") == -1)
1693 +                                               perror_msg_and_die("..");
1694 +                               }
1695 +                               continue;
1696 +                       }
1697                         save_nod = 0;
1698                         /* Check for hardlinks */
1699                         if (!S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) && st.st_nlink > 1) {
1700 -                               int32 hdlink = is_hardlink(st.st_ino);
1701 +                               int32 hdlink = is_hardlink(fs, st.st_ino);
1702                                 if (hdlink >= 0) {
1703 -                                       add2dir(fs, this_nod, hdlinks.hdl[hdlink].dst_nod, name);
1704 +                                       add2dir(fs, this_nod, fs->hdlinks.hdl[hdlink].dst_nod, name);
1705                                         continue;
1706                                 } else {
1707                                         save_nod = 1;
1708 @@ -1697,8 +2403,12 @@
1709                                         free(lnk);
1710                                         break;
1711                                 case S_IFREG:
1712 -                                       fh = xfopen(dent->d_name, "rb");
1713 -                                       nod = mkfile_fs(fs, this_nod, name, mode, st.st_size, fh, uid, gid, ctime, mtime);
1714 +                                       fh = fopen(dent->d_name, "rb");
1715 +                                       if (!fh) {
1716 +                                               error_msg("Unable to open file %s", dent->d_name);
1717 +                                               break;
1718 +                                       }
1719 +                                       nod = mkfile_fs(fs, this_nod, name, mode, fh, uid, gid, ctime, mtime);
1720                                         fclose(fh);
1721                                         break;
1722                                 case S_IFDIR:
1723 @@ -1706,199 +2416,128 @@
1724                                         if(chdir(dent->d_name) < 0)
1725                                                 perror_msg_and_die(name);
1726                                         add2fs_from_dir(fs, nod, squash_uids, squash_perms, fs_timestamp, stats);
1727 -                                       chdir("..");
1728 +                                       if (chdir("..") == -1)
1729 +                                               perror_msg_and_die("..");
1730                                         break;
1731                                 default:
1732                                         error_msg("ignoring entry %s", name);
1733                         }
1734                         if (save_nod) {
1735 -                               if (hdlinks.count == hdlink_cnt) {
1736 -                                       if ((hdlinks.hdl = 
1737 -                                                realloc (hdlinks.hdl, (hdlink_cnt + HDLINK_CNT) *
1738 +                               if (fs->hdlinks.count == fs->hdlink_cnt) {
1739 +                                       if ((fs->hdlinks.hdl =
1740 +                                                realloc (fs->hdlinks.hdl, (fs->hdlink_cnt + HDLINK_CNT) *
1741                                                                   sizeof (struct hdlink_s))) == NULL) {
1742                                                 error_msg_and_die("Not enough memory");
1743                                         }
1744 -                                       hdlink_cnt += HDLINK_CNT;
1745 +                                       fs->hdlink_cnt += HDLINK_CNT;
1746                                 }
1747 -                               hdlinks.hdl[hdlinks.count].src_inode = st.st_ino;
1748 -                               hdlinks.hdl[hdlinks.count].dst_nod = nod;
1749 -                               hdlinks.count++;
1750 +                               fs->hdlinks.hdl[fs->hdlinks.count].src_inode = st.st_ino;
1751 +                               fs->hdlinks.hdl[fs->hdlinks.count].dst_nod = nod;
1752 +                               fs->hdlinks.count++;
1753                         }
1754                 }
1755         }
1756         closedir(dh);
1757  }
1758  
1759 -// endianness swap of x-indirect blocks
1760 +// Copy size blocks from src to dst, putting holes in the output
1761 +// file (if possible) if the input block is all zeros.
1762 +// Copy size blocks from src to dst, putting holes in the output
1763 +// file (if possible) if the input block is all zeros.
1764  static void
1765 -swap_goodblocks(filesystem *fs, inode *nod)
1766 +copy_file(filesystem *fs, FILE *dst, FILE *src, size_t size)
1767  {
1768 -       uint32 i,j;
1769 -       int done=0;
1770 -       uint32 *b,*b2;
1771 +       uint8 *b;
1772  
1773 -       uint32 nblk = nod->i_blocks / INOBLK;
1774 -       if((nod->i_size && !nblk) || ((nod->i_mode & FM_IFBLK) == FM_IFBLK) || ((nod->i_mode & FM_IFCHR) == FM_IFCHR))
1775 -               for(i = 0; i <= EXT2_TIND_BLOCK; i++)
1776 -                       nod->i_block[i] = swab32(nod->i_block[i]);
1777 -       if(nblk <= EXT2_IND_BLOCK)
1778 -               return;
1779 -       swap_block(get_blk(fs, nod->i_block[EXT2_IND_BLOCK]));
1780 -       if(nblk <= EXT2_DIND_BLOCK + BLOCKSIZE/4)
1781 -               return;
1782 -       /* Currently this will fail b'cos the number of blocks as stored
1783 -          in i_blocks also includes the indirection blocks (see
1784 -          walk_bw). But this function assumes that i_blocks only
1785 -          stores the count of data blocks ( Actually according to
1786 -          "Understanding the Linux Kernel" (Table 17-3 p502 1st Ed)
1787 -          i_blocks IS supposed to store the count of data blocks). so
1788 -          with a file of size 268K nblk would be 269.The above check
1789 -          will be false even though double indirection hasn't been
1790 -          started.This is benign as 0 means block 0 which has been
1791 -          zeroed out and therefore points back to itself from any offset
1792 -        */
1793 -       // FIXME: I have fixed that, but I have the feeling the rest of
1794 -       // ths function needs to be fixed for the same reasons - Xav
1795 -       assert(nod->i_block[EXT2_DIND_BLOCK] != 0);
1796 -       for(i = 0; i < BLOCKSIZE/4; i++)
1797 -               if(nblk > EXT2_IND_BLOCK + BLOCKSIZE/4 + (BLOCKSIZE/4)*i )
1798 -                       swap_block(get_blk(fs, ((uint32*)get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]))[i]));
1799 -       swap_block(get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]));
1800 -       if(nblk <= EXT2_IND_BLOCK + BLOCKSIZE/4 + BLOCKSIZE/4 * BLOCKSIZE/4)
1801 -               return;
1802 -       /* Adding support for triple indirection */
1803 -       b = (uint32*)get_blk(fs,nod->i_block[EXT2_TIND_BLOCK]);
1804 -       for(i=0;i < BLOCKSIZE/4 && !done ; i++) {
1805 -               b2 = (uint32*)get_blk(fs,b[i]); 
1806 -               for(j=0; j<BLOCKSIZE/4;j++) {
1807 -                       if (nblk > ( EXT2_IND_BLOCK + BLOCKSIZE/4 + 
1808 -                                    (BLOCKSIZE/4)*(BLOCKSIZE/4) + 
1809 -                                    i*(BLOCKSIZE/4)*(BLOCKSIZE/4) + 
1810 -                                    j*(BLOCKSIZE/4)) ) 
1811 -                         swap_block(get_blk(fs,b2[j]));
1812 -                       else {
1813 -                         done = 1;
1814 -                         break;
1815 -                       }
1816 +       b = malloc(BLOCKSIZE);
1817 +       if (!b)
1818 +               error_msg_and_die("copy_file: out of memory");
1819 +       if (fseek(src, 0, SEEK_SET))
1820 +               perror_msg_and_die("fseek");
1821 +       if (ftruncate(fileno(dst), 0))
1822 +               perror_msg_and_die("copy_file: ftruncate");
1823 +       while (size > 0) {
1824 +               if (fread(b, BLOCKSIZE, 1, src) != 1)
1825 +                       perror_msg_and_die("copy failed on read");
1826 +               if ((dst != stdout) && fs->holes && is_blk_empty(b)) {
1827 +                       /* Empty block, just skip it */
1828 +                       if (fseek(dst, BLOCKSIZE, SEEK_CUR))
1829 +                               perror_msg_and_die("fseek");
1830 +               } else {
1831 +                       if (fwrite(b, BLOCKSIZE, 1, dst) != 1)
1832 +                               perror_msg_and_die("copy failed on write");
1833                 }
1834 -               swap_block((uint8 *)b2);
1835 +               size--;
1836         }
1837 -       swap_block((uint8 *)b);
1838 -       return;
1839 +       free(b);
1840  }
1841  
1842 -static void
1843 -swap_badblocks(filesystem *fs, inode *nod)
1844 +// Allocate a new filesystem structure, allocate internal memory,
1845 +// and initialize the contents.
1846 +static filesystem *
1847 +alloc_fs(int swapit, char *fname, uint32 nbblocks, FILE *srcfile)
1848  {
1849 -       uint32 i,j;
1850 -       int done=0;
1851 -       uint32 *b,*b2;
1852 +       filesystem *fs;
1853 +       struct stat srcstat, dststat;
1854  
1855 -       uint32 nblk = nod->i_blocks / INOBLK;
1856 -       if((nod->i_size && !nblk) || ((nod->i_mode & FM_IFBLK) == FM_IFBLK) || ((nod->i_mode & FM_IFCHR) == FM_IFCHR))
1857 -               for(i = 0; i <= EXT2_TIND_BLOCK; i++)
1858 -                       nod->i_block[i] = swab32(nod->i_block[i]);
1859 -       if(nblk <= EXT2_IND_BLOCK)
1860 -               return;
1861 -       swap_block(get_blk(fs, nod->i_block[EXT2_IND_BLOCK]));
1862 -       if(nblk <= EXT2_DIND_BLOCK + BLOCKSIZE/4)
1863 -               return;
1864 -       /* See comment in swap_goodblocks */
1865 -       assert(nod->i_block[EXT2_DIND_BLOCK] != 0);
1866 -       swap_block(get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]));
1867 -       for(i = 0; i < BLOCKSIZE/4; i++)
1868 -               if(nblk > EXT2_IND_BLOCK + BLOCKSIZE/4 + (BLOCKSIZE/4)*i )
1869 -                       swap_block(get_blk(fs, ((uint32*)get_blk(fs, nod->i_block[EXT2_DIND_BLOCK]))[i]));
1870 -       if(nblk <= EXT2_IND_BLOCK + BLOCKSIZE/4 + BLOCKSIZE/4 * BLOCKSIZE/4)
1871 -               return;
1872 -       /* Adding support for triple indirection */
1873 -       b = (uint32*)get_blk(fs,nod->i_block[EXT2_TIND_BLOCK]);
1874 -       swap_block((uint8 *)b);
1875 -       for(i=0;i < BLOCKSIZE/4 && !done ; i++) {
1876 -               b2 = (uint32*)get_blk(fs,b[i]); 
1877 -               swap_block((uint8 *)b2);
1878 -               for(j=0; j<BLOCKSIZE/4;j++) {
1879 -                       if (nblk > ( EXT2_IND_BLOCK + BLOCKSIZE/4 + 
1880 -                                    (BLOCKSIZE/4)*(BLOCKSIZE/4) + 
1881 -                                    i*(BLOCKSIZE/4)*(BLOCKSIZE/4) + 
1882 -                                    j*(BLOCKSIZE/4)) ) 
1883 -                         swap_block(get_blk(fs,b2[j]));
1884 -                       else {
1885 -                         done = 1;
1886 -                         break;
1887 -                       }
1888 -               }
1889 -       }
1890 -       return;
1891 -}
1892 +       fs = malloc(sizeof(*fs));
1893 +       if (!fs)
1894 +               error_msg_and_die("not enough memory for filesystem");
1895 +       memset(fs, 0, sizeof(*fs));
1896 +       fs->swapit = swapit;
1897 +       cache_init(&fs->blks, MAX_FREE_CACHE_BLOCKS, blk_elem_val, blk_freed);
1898 +       cache_init(&fs->gds, MAX_FREE_CACHE_GDS, gd_elem_val, gd_freed);
1899 +       cache_init(&fs->blkmaps, MAX_FREE_CACHE_BLOCKMAPS,
1900 +                  blkmap_elem_val, blkmap_freed);
1901 +       cache_init(&fs->inodes, MAX_FREE_CACHE_INODES,
1902 +                  inode_elem_val, inode_freed);
1903 +       fs->hdlink_cnt = HDLINK_CNT;
1904 +       fs->hdlinks.hdl = calloc(sizeof(struct hdlink_s), fs->hdlink_cnt);
1905 +       if (!fs->hdlinks.hdl)
1906 +               error_msg_and_die("Not enough memory");
1907 +       fs->hdlinks.count = 0 ;
1908  
1909 -// endianness swap of the whole filesystem
1910 -static void
1911 -swap_goodfs(filesystem *fs)
1912 -{
1913 -       uint32 i;
1914 -       for(i = 1; i < fs->sb.s_inodes_count; i++)
1915 -       {
1916 -               inode *nod = get_nod(fs, i);
1917 -               if(nod->i_mode & FM_IFDIR)
1918 -               {
1919 -                       blockwalker bw;
1920 -                       uint32 bk;
1921 -                       init_bw(&bw);
1922 -                       while((bk = walk_bw(fs, i, &bw, 0, 0)) != WALK_END)
1923 -                       {
1924 -                               directory *d;
1925 -                               uint8 *b;
1926 -                               b = get_blk(fs, bk);
1927 -                               for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + swab16(d->d_rec_len)))
1928 -                                       swap_dir(d);
1929 -                       }
1930 -               }
1931 -               swap_goodblocks(fs, nod);
1932 -               swap_nod(nod);
1933 -       }
1934 -       for(i=0;i<GRP_NBGROUPS(fs);i++)
1935 -               swap_gd(&(fs->gd[i]));
1936 -       swap_sb(&fs->sb);
1937 +       if (strcmp(fname, "-") == 0)
1938 +               fs->f = tmpfile();
1939 +       else if (srcfile) {
1940 +               if (fstat(fileno(srcfile), &srcstat))
1941 +                       perror_msg_and_die("fstat srcfile");
1942 +               if (stat(fname, &dststat) == 0
1943 +                   && srcstat.st_ino == dststat.st_ino
1944 +                   && srcstat.st_dev == dststat.st_dev)
1945 +                 {
1946 +                       // source and destination are the same file, don't
1947 +                       // truncate or copy, just use the file.
1948 +                       fs->f = fopen(fname, "r+b");
1949 +               } else {
1950 +                       fs->f = fopen(fname, "w+b");
1951 +                       if (fs->f)
1952 +                               copy_file(fs, fs->f, srcfile, nbblocks);
1953 +               }
1954 +       } else
1955 +               fs->f = fopen(fname, "w+b");
1956 +       if (!fs->f)
1957 +               perror_msg_and_die("opening %s", fname);
1958 +       return fs;
1959  }
1960  
1961 +/* Make sure the output file is the right size */
1962  static void
1963 -swap_badfs(filesystem *fs)
1964 +set_file_size(filesystem *fs)
1965  {
1966 -       uint32 i;
1967 -       swap_sb(&fs->sb);
1968 -       for(i=0;i<GRP_NBGROUPS(fs);i++)
1969 -               swap_gd(&(fs->gd[i]));
1970 -       for(i = 1; i < fs->sb.s_inodes_count; i++)
1971 -       {
1972 -               inode *nod = get_nod(fs, i);
1973 -               swap_nod(nod);
1974 -               swap_badblocks(fs, nod);
1975 -               if(nod->i_mode & FM_IFDIR)
1976 -               {
1977 -                       blockwalker bw;
1978 -                       uint32 bk;
1979 -                       init_bw(&bw);
1980 -                       while((bk = walk_bw(fs, i, &bw, 0, 0)) != WALK_END)
1981 -                       {
1982 -                               directory *d;
1983 -                               uint8 *b;
1984 -                               b = get_blk(fs, bk);
1985 -                               for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
1986 -                                       swap_dir(d);
1987 -                       }
1988 -               }
1989 -       }
1990 +       if (ftruncate(fileno(fs->f),
1991 +                     ((off_t) fs->sb->s_blocks_count) * BLOCKSIZE))
1992 +               perror_msg_and_die("set_file_size: ftruncate");
1993  }
1994  
1995  // initialize an empty filesystem
1996  static filesystem *
1997 -init_fs(int nbblocks, int nbinodes, int nbresrvd, int holes, uint32 fs_timestamp)
1998 +init_fs(int nbblocks, int nbinodes, int nbresrvd, int holes,
1999 +       uint32 fs_timestamp, uint32 creator_os, int swapit, char *fname)
2000  {
2001         uint32 i;
2002         filesystem *fs;
2003 -       directory *d;
2004 -       uint8 * b;
2005 +       dirwalker dw;
2006         uint32 nod, first_block;
2007         uint32 nbgroups,nbinodes_per_group,overhead_per_group,free_blocks,
2008                 free_blocks_per_group,nbblocks_per_group,min_nbgroups;
2009 @@ -1906,6 +2545,11 @@
2010         uint32 j;
2011         uint8 *bbm,*ibm;
2012         inode *itab0;
2013 +       blk_info *bi;
2014 +       nod_info *ni;
2015 +       groupdescriptor *gd;
2016 +       gd_info *gi;
2017 +       inode_pos ipos;
2018         
2019         if(nbresrvd < 0)
2020                 error_msg_and_die("reserved blocks value is invalid. Note: options have changed, see --help or the man page.");
2021 @@ -1919,10 +2563,14 @@
2022          */
2023         min_nbgroups = (nbinodes + INODES_PER_GROUP - 1) / INODES_PER_GROUP;
2024  
2025 +       /* On filesystems with 1k block size, the bootloader area uses a full
2026 +        * block. For 2048 and up, the superblock can be fitted into block 0.
2027 +        */
2028 +       first_block = (BLOCKSIZE == 1024);
2029 +
2030         /* nbblocks is the total number of blocks in the filesystem.
2031          * a block group can have no more than 8192 blocks.
2032          */
2033 -       first_block = (BLOCKSIZE == 1024);
2034         nbgroups = (nbblocks - first_block + BLOCKS_PER_GROUP - 1) / BLOCKS_PER_GROUP;
2035         if(nbgroups < min_nbgroups) nbgroups = min_nbgroups;
2036         nbblocks_per_group = rndup((nbblocks - first_block + nbgroups - 1)/nbgroups, 8);
2037 @@ -1934,51 +2582,59 @@
2038         gdsz = rndup(nbgroups*sizeof(groupdescriptor),BLOCKSIZE)/BLOCKSIZE;
2039         itblsz = nbinodes_per_group * sizeof(inode)/BLOCKSIZE;
2040         overhead_per_group = 3 /*sb,bbm,ibm*/ + gdsz + itblsz;
2041 -       if((uint32)nbblocks - 1 < overhead_per_group * nbgroups)
2042 -               error_msg_and_die("too much overhead, try fewer inodes or more blocks. Note: options have changed, see --help or the man page.");
2043 -       free_blocks = nbblocks - overhead_per_group*nbgroups - 1 /*boot block*/;
2044 +       free_blocks = nbblocks - overhead_per_group*nbgroups - first_block;
2045         free_blocks_per_group = nbblocks_per_group - overhead_per_group;
2046 +       if(free_blocks < 0)
2047 +               error_msg_and_die("too much overhead, try fewer inodes or more blocks. Note: options have changed, see --help or the man page.");
2048  
2049 -       if(!(fs = (filesystem*)calloc(nbblocks, BLOCKSIZE)))
2050 -               error_msg_and_die("not enough memory for filesystem");
2051 +       fs = alloc_fs(swapit, fname, nbblocks, NULL);
2052 +       fs->sb = calloc(1, SUPERBLOCK_SIZE);
2053 +       if (!fs->sb)
2054 +               error_msg_and_die("error allocating header memory");
2055  
2056         // create the superblock for an empty filesystem
2057 -       fs->sb.s_inodes_count = nbinodes_per_group * nbgroups;
2058 -       fs->sb.s_blocks_count = nbblocks;
2059 -       fs->sb.s_r_blocks_count = nbresrvd;
2060 -       fs->sb.s_free_blocks_count = free_blocks;
2061 -       fs->sb.s_free_inodes_count = fs->sb.s_inodes_count - EXT2_FIRST_INO + 1;
2062 -       fs->sb.s_first_data_block = first_block;
2063 -       fs->sb.s_log_block_size = BLOCKSIZE >> 11;
2064 -       fs->sb.s_log_frag_size = BLOCKSIZE >> 11;
2065 -       fs->sb.s_blocks_per_group = nbblocks_per_group;
2066 -       fs->sb.s_frags_per_group = nbblocks_per_group;
2067 -       fs->sb.s_inodes_per_group = nbinodes_per_group;
2068 -       fs->sb.s_wtime = fs_timestamp;
2069 -       fs->sb.s_magic = EXT2_MAGIC_NUMBER;
2070 -       fs->sb.s_lastcheck = fs_timestamp;
2071 +       fs->sb->s_inodes_count = nbinodes_per_group * nbgroups;
2072 +       fs->sb->s_blocks_count = nbblocks;
2073 +       fs->sb->s_r_blocks_count = nbresrvd;
2074 +       fs->sb->s_free_blocks_count = free_blocks;
2075 +       fs->sb->s_free_inodes_count = fs->sb->s_inodes_count - EXT2_FIRST_INO + 1;
2076 +       fs->sb->s_first_data_block = first_block;
2077 +       fs->sb->s_log_block_size = BLOCKSIZE >> 11;
2078 +       fs->sb->s_log_frag_size = BLOCKSIZE >> 11;
2079 +       fs->sb->s_blocks_per_group = nbblocks_per_group;
2080 +       fs->sb->s_frags_per_group = nbblocks_per_group;
2081 +       fs->sb->s_inodes_per_group = nbinodes_per_group;
2082 +       fs->sb->s_wtime = fs_timestamp;
2083 +       fs->sb->s_magic = EXT2_MAGIC_NUMBER;
2084 +       fs->sb->s_lastcheck = fs_timestamp;
2085 +       fs->sb->s_creator_os = creator_os;
2086 +
2087 +       set_file_size(fs);
2088  
2089         // set up groupdescriptors
2090 -       for(i=0, bbmpos=gdsz+2, ibmpos=bbmpos+1, itblpos=ibmpos+1;
2091 +       for(i=0, bbmpos=first_block+1+gdsz, ibmpos=bbmpos+1, itblpos=ibmpos+1;
2092                 i<nbgroups;
2093                 i++, bbmpos+=nbblocks_per_group, ibmpos+=nbblocks_per_group, itblpos+=nbblocks_per_group)
2094         {
2095 +               gd = get_gd(fs, i, &gi);
2096 +
2097                 if(free_blocks > free_blocks_per_group) {
2098 -                       fs->gd[i].bg_free_blocks_count = free_blocks_per_group;
2099 +                       gd->bg_free_blocks_count = free_blocks_per_group;
2100                         free_blocks -= free_blocks_per_group;
2101                 } else {
2102 -                       fs->gd[i].bg_free_blocks_count = free_blocks;
2103 +                       gd->bg_free_blocks_count = free_blocks;
2104                         free_blocks = 0; // this is the last block group
2105                 }
2106                 if(i)
2107 -                       fs->gd[i].bg_free_inodes_count = nbinodes_per_group;
2108 +                       gd->bg_free_inodes_count = nbinodes_per_group;
2109                 else
2110 -                       fs->gd[i].bg_free_inodes_count = nbinodes_per_group -
2111 +                       gd->bg_free_inodes_count = nbinodes_per_group -
2112                                                         EXT2_FIRST_INO + 2;
2113 -               fs->gd[i].bg_used_dirs_count = 0;
2114 -               fs->gd[i].bg_block_bitmap = bbmpos;
2115 -               fs->gd[i].bg_inode_bitmap = ibmpos;
2116 -               fs->gd[i].bg_inode_table = itblpos;
2117 +               gd->bg_used_dirs_count = 0;
2118 +               gd->bg_block_bitmap = bbmpos;
2119 +               gd->bg_inode_bitmap = ibmpos;
2120 +               gd->bg_inode_table = itblpos;
2121 +               put_gd(gi);
2122         }
2123  
2124         /* Mark non-filesystem blocks and inodes as allocated */
2125 @@ -1984,110 +2640,143 @@
2126         /* Mark non-filesystem blocks and inodes as allocated */
2127         /* Mark system blocks and inodes as allocated         */
2128         for(i = 0; i<nbgroups;i++) {
2129 -
2130                 /* Block bitmap */
2131 -               bbm = get_blk(fs,fs->gd[i].bg_block_bitmap);    
2132 +               gd = get_gd(fs, i, &gi);
2133 +               bbm = GRP_GET_GROUP_BBM(fs, gd, &bi);
2134                 //non-filesystem blocks
2135 -               for(j = fs->gd[i].bg_free_blocks_count
2136 +               for(j = gd->bg_free_blocks_count
2137                         + overhead_per_group + 1; j <= BLOCKSIZE * 8; j++)
2138                         allocate(bbm, j); 
2139                 //system blocks
2140                 for(j = 1; j <= overhead_per_group; j++)
2141                         allocate(bbm, j); 
2142 -               
2143 +               GRP_PUT_GROUP_BBM(bi);
2144 +
2145                 /* Inode bitmap */
2146 -               ibm = get_blk(fs,fs->gd[i].bg_inode_bitmap);    
2147 +               ibm = GRP_GET_GROUP_IBM(fs, gd, &bi);
2148                 //non-filesystem inodes
2149 -               for(j = fs->sb.s_inodes_per_group+1; j <= BLOCKSIZE * 8; j++)
2150 +               for(j = fs->sb->s_inodes_per_group+1; j <= BLOCKSIZE * 8; j++)
2151                         allocate(ibm, j);
2152  
2153                 //system inodes
2154                 if(i == 0)
2155                         for(j = 1; j < EXT2_FIRST_INO; j++)
2156                                 allocate(ibm, j);
2157 +               GRP_PUT_GROUP_IBM(bi);
2158 +               put_gd(gi);
2159         }
2160  
2161         // make root inode and directory
2162         /* We have groups now. Add the root filesystem in group 0 */
2163         /* Also increment the directory count for group 0 */
2164 -       fs->gd[0].bg_free_inodes_count--;
2165 -       fs->gd[0].bg_used_dirs_count = 1;
2166 -       itab0 = (inode *)get_blk(fs,fs->gd[0].bg_inode_table);
2167 -       itab0[EXT2_ROOT_INO-1].i_mode = FM_IFDIR | FM_IRWXU | FM_IRGRP | FM_IROTH | FM_IXGRP | FM_IXOTH; 
2168 -       itab0[EXT2_ROOT_INO-1].i_ctime = fs_timestamp;
2169 -       itab0[EXT2_ROOT_INO-1].i_mtime = fs_timestamp;
2170 -       itab0[EXT2_ROOT_INO-1].i_atime = fs_timestamp;
2171 -       itab0[EXT2_ROOT_INO-1].i_size = BLOCKSIZE;
2172 -       itab0[EXT2_ROOT_INO-1].i_links_count = 2;
2173 -
2174 -       if(!(b = get_workblk()))
2175 -               error_msg_and_die("get_workblk() failed.");
2176 -       d = (directory*)b;
2177 -       d->d_inode = EXT2_ROOT_INO;
2178 -       d->d_rec_len = sizeof(directory)+4;
2179 -       d->d_name_len = 1;
2180 -       strcpy(d->d_name, ".");
2181 -       d = (directory*)(b + d->d_rec_len);
2182 -       d->d_inode = EXT2_ROOT_INO;
2183 -       d->d_rec_len = BLOCKSIZE - (sizeof(directory)+4);
2184 -       d->d_name_len = 2;
2185 -       strcpy(d->d_name, "..");
2186 -       extend_blk(fs, EXT2_ROOT_INO, b, 1);
2187 +       gd = get_gd(fs, 0, &gi);
2188 +       gd->bg_free_inodes_count--;
2189 +       gd->bg_used_dirs_count = 1;
2190 +       put_gd(gi);
2191 +       itab0 = get_nod(fs, EXT2_ROOT_INO, &ni);
2192 +       itab0->i_mode = FM_IFDIR | FM_IRWXU | FM_IRGRP | FM_IROTH | FM_IXGRP | FM_IXOTH;
2193 +       itab0->i_ctime = fs_timestamp;
2194 +       itab0->i_mtime = fs_timestamp;
2195 +       itab0->i_atime = fs_timestamp;
2196 +       itab0->i_size = BLOCKSIZE;
2197 +       itab0->i_links_count = 2;
2198 +       put_nod(ni);
2199 +
2200 +       new_dir(fs, EXT2_ROOT_INO, ".", 1, &dw);
2201 +       shrink_dir(&dw, EXT2_ROOT_INO, "..", 2);
2202 +       next_dir(&dw); // Force the data into the buffer
2203 +       inode_pos_init(fs, &ipos, EXT2_ROOT_INO, INODE_POS_EXTEND, NULL);
2204 +       extend_inode_blk(fs, &ipos, dir_data(&dw), 1);
2205 +       inode_pos_finish(fs, &ipos);
2206 +       put_dir(&dw);
2207  
2208 -       // make lost+found directory and reserve blocks
2209 -       if(fs->sb.s_r_blocks_count)
2210 +       // make lost+found directory
2211 +       if(fs->sb->s_r_blocks_count)
2212         {
2213 -               nod = mkdir_fs(fs, EXT2_ROOT_INO, "lost+found", FM_IRWXU, 0, 0, fs_timestamp, fs_timestamp);
2214 +               inode *node;
2215 +               uint8 *b;
2216 +
2217 +               nod = mkdir_fs(fs, EXT2_ROOT_INO, "lost+found", FM_IRWXU,
2218 +                              0, 0, fs_timestamp, fs_timestamp);
2219 +               b = get_workblk();
2220                 memset(b, 0, BLOCKSIZE);
2221                 ((directory*)b)->d_rec_len = BLOCKSIZE;
2222 -               /* We run into problems with e2fsck if directory lost+found grows
2223 -                * bigger than this. Need to find out why this happens - sundar
2224 -                */
2225 -               if (fs->sb.s_r_blocks_count > fs->sb.s_blocks_count * MAX_RESERVED_BLOCKS ) 
2226 -                       fs->sb.s_r_blocks_count = fs->sb.s_blocks_count * MAX_RESERVED_BLOCKS;
2227 -               for(i = 1; i < fs->sb.s_r_blocks_count; i++)
2228 -                       extend_blk(fs, nod, b, 1);
2229 -               get_nod(fs, nod)->i_size = fs->sb.s_r_blocks_count * BLOCKSIZE;
2230 +               inode_pos_init(fs, &ipos, nod, INODE_POS_EXTEND, NULL);
2231 +               // It is always 16 blocks to start out with
2232 +               for(i = 1; i < 16; i++)
2233 +                       extend_inode_blk(fs, &ipos, b, 1);
2234 +               inode_pos_finish(fs, &ipos);
2235 +               free_workblk(b);
2236 +               node = get_nod(fs, nod, &ni);
2237 +               node->i_size = 16 * BLOCKSIZE;
2238 +               put_nod(ni);
2239         }
2240 -       free_workblk(b);
2241  
2242         // administrative info
2243 -       fs->sb.s_state = 1;
2244 -       fs->sb.s_max_mnt_count = 20;
2245 +       fs->sb->s_state = 1;
2246 +       fs->sb->s_max_mnt_count = 20;
2247  
2248         // options for me
2249 -       if(holes)
2250 -               fs->sb.s_reserved[200] |= OP_HOLES;
2251 +       fs->holes = holes;
2252         
2253         return fs;
2254  }
2255  
2256  // loads a filesystem from disk
2257  static filesystem *
2258 -load_fs(FILE * fh, int swapit)
2259 +load_fs(FILE *fh, int swapit, char *fname)
2260  {
2261 -       size_t fssize;
2262 +       off_t fssize;
2263         filesystem *fs;
2264 -       if((fseek(fh, 0, SEEK_END) < 0) || ((ssize_t)(fssize = ftell(fh)) == -1))
2265 +
2266 +       if((fseek(fh, 0, SEEK_END) < 0) || ((fssize = ftello(fh)) == -1))
2267                 perror_msg_and_die("input filesystem image");
2268         rewind(fh);
2269 -       fssize = (fssize + BLOCKSIZE - 1) / BLOCKSIZE;
2270 +       if ((fssize % BLOCKSIZE) != 0)
2271 +               error_msg_and_die("Input file not a multiple of block size");
2272 +       fssize /= BLOCKSIZE;
2273         if(fssize < 16) // totally arbitrary
2274                 error_msg_and_die("too small filesystem");
2275 -       if(!(fs = (filesystem*)calloc(fssize, BLOCKSIZE)))
2276 -               error_msg_and_die("not enough memory for filesystem");
2277 -       if(fread(fs, BLOCKSIZE, fssize, fh) != fssize)
2278 -               perror_msg_and_die("input filesystem image");
2279 +       fs = alloc_fs(swapit, fname, fssize, fh);
2280 +
2281 +       /* Read and check the superblock, then read the superblock
2282 +        * and all the group descriptors */
2283 +       fs->sb = malloc(SUPERBLOCK_SIZE);
2284 +       if (!fs->sb)
2285 +               error_msg_and_die("error allocating header memory");
2286 +       if (fseek(fs->f, SUPERBLOCK_OFFSET, SEEK_SET))
2287 +               perror_msg_and_die("fseek");
2288 +       if (fread(fs->sb, SUPERBLOCK_SIZE, 1, fs->f) != 1)
2289 +               perror_msg_and_die("fread filesystem image superblock");
2290         if(swapit)
2291 -               swap_badfs(fs);
2292 -       if(fs->sb.s_rev_level || (fs->sb.s_magic != EXT2_MAGIC_NUMBER))
2293 +               swap_sb(fs->sb);
2294 +
2295 +       if((fs->sb->s_rev_level > 1) || (fs->sb->s_magic != EXT2_MAGIC_NUMBER))
2296                 error_msg_and_die("not a suitable ext2 filesystem");
2297 +       if (fs->sb->s_rev_level > 0) {
2298 +               if (fs->sb->s_first_ino != EXT2_GOOD_OLD_FIRST_INO)
2299 +                       error_msg_and_die("First inode incompatible");
2300 +               if (fs->sb->s_inode_size != EXT2_GOOD_OLD_INODE_SIZE)
2301 +                       error_msg_and_die("inode size incompatible");
2302 +               if (fs->sb->s_feature_compat)
2303 +                       error_msg_and_die("Unsupported compat features");
2304 +               if (fs->sb->s_feature_incompat)
2305 +                       error_msg_and_die("Unsupported incompat features");
2306 +               if (fs->sb->s_feature_ro_compat
2307 +                   & ~EXT2_FEATURE_RO_COMPAT_LARGE_FILE)
2308 +                       error_msg_and_die("Unsupported ro compat features");
2309 +       }
2310 +
2311 +       set_file_size(fs);
2312         return fs;
2313  }
2314  
2315  static void
2316  free_fs(filesystem *fs)
2317  {
2318 +       free(fs->hdlinks.hdl);
2319 +       fclose(fs->f);
2320 +       free(fs->sb);
2321         free(fs);
2322  }
2323  
2324 @@ -2123,16 +2812,23 @@
2325  {
2326         blockwalker bw;
2327         uint32 bk;
2328 -       int32 fsize = get_nod(fs, nod)->i_size;
2329 +       nod_info *ni;
2330 +       inode *node = get_nod(fs, nod, &ni);
2331 +       int32 fsize = node->i_size;
2332 +       blk_info *bi;
2333 +
2334         init_bw(&bw);
2335         while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
2336         {
2337                 if(fsize <= 0)
2338                         error_msg_and_die("wrong size while saving inode %d", nod);
2339 -               if(fwrite(get_blk(fs, bk), (fsize > BLOCKSIZE) ? BLOCKSIZE : fsize, 1, f) != 1)
2340 +               if(fwrite(get_blk(fs, bk, &bi),
2341 +                         (fsize > BLOCKSIZE) ? BLOCKSIZE : fsize, 1, f) != 1)
2342                         error_msg_and_die("error while saving inode %d", nod);
2343 +               put_blk(bi);
2344                 fsize -= BLOCKSIZE;
2345         }
2346 +       put_nod(ni);
2347  }
2348  
2349  
2350 @@ -2141,8 +2837,11 @@
2351  print_dev(filesystem *fs, uint32 nod)
2352  {
2353         int minor, major;
2354 -       minor = ((uint8*)get_nod(fs, nod)->i_block)[0];
2355 -       major = ((uint8*)get_nod(fs, nod)->i_block)[1];
2356 +       nod_info *ni;
2357 +       inode *node = get_nod(fs, nod, &ni);
2358 +       minor = ((uint8*)node->i_block)[0];
2359 +       major = ((uint8*)node->i_block)[1];
2360 +       put_nod(ni);
2361         printf("major: %d, minor: %d\n", major, minor);
2362  }
2363  
2364 @@ -2157,17 +2856,15 @@
2365         while((bk = walk_bw(fs, nod, &bw, 0, 0)) != WALK_END)
2366         {
2367                 directory *d;
2368 -               uint8 *b;
2369 -               b = get_blk(fs, bk);
2370 -               for(d = (directory*)b; (int8*)d + sizeof(*d) < (int8*)b + BLOCKSIZE; d = (directory*)((int8*)d + d->d_rec_len))
2371 +               dirwalker dw;
2372 +               for (d = get_dir(fs, bk, &dw); d; d = next_dir(&dw))
2373                         if(d->d_inode)
2374                         {
2375 -                               int i;
2376                                 printf("entry '");
2377 -                               for(i = 0; i < d->d_name_len; i++)
2378 -                                       putchar(d->d_name[i]);
2379 +                               fwrite(dir_name(&dw), 1, d->d_name_len, stdout);
2380                                 printf("' (inode %d): rec_len: %d (name_len: %d)\n", d->d_inode, d->d_rec_len, d->d_name_len);
2381                         }
2382 +               put_dir(&dw);
2383         }
2384  }
2385  
2386 @@ -2175,14 +2872,18 @@
2387  static void
2388  print_link(filesystem *fs, uint32 nod)
2389  {
2390 -       if(!get_nod(fs, nod)->i_blocks)
2391 -               printf("links to '%s'\n", (char*)get_nod(fs, nod)->i_block);
2392 +       nod_info *ni;
2393 +       inode *node = get_nod(fs, nod, &ni);
2394 +
2395 +       if(!node->i_blocks)
2396 +               printf("links to '%s'\n", (char*)node->i_block);
2397         else
2398         {
2399                 printf("links to '");
2400                 write_blocks(fs, nod, stdout);
2401                 printf("'\n");
2402         }
2403 +       put_nod(ni);
2404  }
2405  
2406  // make a ls-like printout of permissions
2407 @@ -2251,8 +2952,13 @@
2408  {
2409         char *s;
2410         char perms[11];
2411 -       if(!get_nod(fs, nod)->i_mode)
2412 -               return;
2413 +       nod_info *ni;
2414 +       inode *node = get_nod(fs, nod, &ni);
2415 +       blk_info *bi;
2416 +       gd_info *gi;
2417 +
2418 +       if(!node->i_mode)
2419 +               goto out;
2420         switch(nod)
2421         {
2422                 case EXT2_BAD_INO:
2423 @@ -2274,15 +2980,18 @@
2424                 default:
2425                         s = (nod >= EXT2_FIRST_INO) ? "normal" : "unknown reserved"; 
2426         }
2427 -       printf("inode %d (%s, %d links): ", nod, s, get_nod(fs, nod)->i_links_count);
2428 -       if(!allocated(GRP_GET_INODE_BITMAP(fs,nod), GRP_IBM_OFFSET(fs,nod)))
2429 +       printf("inode %d (%s, %d links): ", nod, s, node->i_links_count);
2430 +       if(!allocated(GRP_GET_INODE_BITMAP(fs,nod,&bi,&gi), GRP_IBM_OFFSET(fs,nod)))
2431         {
2432 +               GRP_PUT_INODE_BITMAP(bi,gi);
2433                 printf("unallocated\n");
2434 -               return;
2435 +               goto out;
2436         }
2437 -       make_perms(get_nod(fs, nod)->i_mode, perms);
2438 -       printf("%s,  size: %d byte%s (%d block%s)\n", perms, plural(get_nod(fs, nod)->i_size), plural(get_nod(fs, nod)->i_blocks / INOBLK));
2439 -       switch(get_nod(fs, nod)->i_mode & FM_IFMT)
2440 +       GRP_PUT_INODE_BITMAP(bi,gi);
2441 +       make_perms(node->i_mode, perms);
2442 +       printf("%s,  size: %d byte%s (%d block%s)\n", perms,
2443 +              plural(node->i_size), plural(node->i_blocks / INOBLK));
2444 +       switch(node->i_mode & FM_IFMT)
2445         {
2446                 case FM_IFSOCK:
2447                         list_blocks(fs, nod);
2448 @@ -2310,6 +3019,8 @@
2449                         list_blocks(fs, nod);
2450         }
2451         printf("Done with inode %d\n",nod);
2452 +out:
2453 +       put_nod(ni);
2454  }
2455  
2456  // describes various fields in a filesystem
2457 @@ -2317,49 +3028,65 @@
2458  print_fs(filesystem *fs)
2459  {
2460         uint32 i;
2461 +       blk_info *bi;
2462 +       groupdescriptor *gd;
2463 +       gd_info *gi;
2464         uint8 *ibm;
2465  
2466         printf("%d blocks (%d free, %d reserved), first data block: %d\n",
2467 -              fs->sb.s_blocks_count, fs->sb.s_free_blocks_count,
2468 -              fs->sb.s_r_blocks_count, fs->sb.s_first_data_block);
2469 -       printf("%d inodes (%d free)\n", fs->sb.s_inodes_count,
2470 -              fs->sb.s_free_inodes_count);
2471 +              fs->sb->s_blocks_count, fs->sb->s_free_blocks_count,
2472 +              fs->sb->s_r_blocks_count, fs->sb->s_first_data_block);
2473 +       printf("%d inodes (%d free)\n", fs->sb->s_inodes_count,
2474 +              fs->sb->s_free_inodes_count);
2475         printf("block size = %d, frag size = %d\n",
2476 -              fs->sb.s_log_block_size ? (fs->sb.s_log_block_size << 11) : 1024,
2477 -              fs->sb.s_log_frag_size ? (fs->sb.s_log_frag_size << 11) : 1024);
2478 +              fs->sb->s_log_block_size ? (fs->sb->s_log_block_size << 11) : 1024,
2479 +              fs->sb->s_log_frag_size ? (fs->sb->s_log_frag_size << 11) : 1024);
2480         printf("number of groups: %d\n",GRP_NBGROUPS(fs));
2481         printf("%d blocks per group,%d frags per group,%d inodes per group\n",
2482 -            fs->sb.s_blocks_per_group, fs->sb.s_frags_per_group,
2483 -            fs->sb.s_inodes_per_group);
2484 +            fs->sb->s_blocks_per_group, fs->sb->s_frags_per_group,
2485 +            fs->sb->s_inodes_per_group);
2486         printf("Size of inode table: %d blocks\n",
2487 -               (int)(fs->sb.s_inodes_per_group * sizeof(inode) / BLOCKSIZE));
2488 +               (int)(fs->sb->s_inodes_per_group * sizeof(inode) / BLOCKSIZE));
2489         for (i = 0; i < GRP_NBGROUPS(fs); i++) {
2490                 printf("Group No: %d\n", i+1);
2491 +               gd = get_gd(fs, i, &gi);
2492                 printf("block bitmap: block %d,inode bitmap: block %d, inode table: block %d\n",
2493 -                    fs->gd[i].bg_block_bitmap, fs->gd[i].bg_inode_bitmap,
2494 -                    fs->gd[i].bg_inode_table);
2495 +                    gd->bg_block_bitmap,
2496 +                    gd->bg_inode_bitmap,
2497 +                    gd->bg_inode_table);
2498                 printf("block bitmap allocation:\n");
2499 -               print_bm(GRP_GET_GROUP_BBM(fs, i),fs->sb.s_blocks_per_group);
2500 +               print_bm(GRP_GET_GROUP_BBM(fs, gd, &bi),fs->sb->s_blocks_per_group);
2501 +               GRP_PUT_GROUP_BBM(bi);
2502                 printf("inode bitmap allocation:\n");
2503 -               ibm = GRP_GET_GROUP_IBM(fs, i);
2504 -               print_bm(ibm, fs->sb.s_inodes_per_group);
2505 -               for (i = 1; i <= fs->sb.s_inodes_per_group; i++)
2506 +               ibm = GRP_GET_GROUP_IBM(fs, gd, &bi);
2507 +               print_bm(ibm, fs->sb->s_inodes_per_group);
2508 +               for (i = 1; i <= fs->sb->s_inodes_per_group; i++)
2509                         if (allocated(ibm, i))
2510                                 print_inode(fs, i);
2511 +               GRP_PUT_GROUP_IBM(bi);
2512 +               put_gd(gi);
2513         }
2514  }
2515  
2516  static void
2517 -dump_fs(filesystem *fs, FILE * fh, int swapit)
2518 +finish_fs(filesystem *fs)
2519  {
2520 -       uint32 nbblocks = fs->sb.s_blocks_count;
2521 -       fs->sb.s_reserved[200] = 0;
2522 -       if(swapit)
2523 -               swap_goodfs(fs);
2524 -       if(fwrite(fs, BLOCKSIZE, nbblocks, fh) < nbblocks)
2525 -               perror_msg_and_die("output filesystem image");
2526 -       if(swapit)
2527 -               swap_badfs(fs);
2528 +       if (cache_flush(&fs->inodes))
2529 +               error_msg_and_die("entry mismatch on inode cache flush");
2530 +       if (cache_flush(&fs->blkmaps))
2531 +               error_msg_and_die("entry mismatch on blockmap cache flush");
2532 +       if (cache_flush(&fs->gds))
2533 +               error_msg_and_die("entry mismatch on gd cache flush");
2534 +       if (cache_flush(&fs->blks))
2535 +               error_msg_and_die("entry mismatch on block cache flush");
2536 +       if(fs->swapit)
2537 +               swap_sb(fs->sb);
2538 +       if (fseek(fs->f, SUPERBLOCK_OFFSET, SEEK_SET))
2539 +               perror_msg_and_die("fseek");
2540 +       if(fwrite(fs->sb, SUPERBLOCK_SIZE, 1, fs->f) != 1)
2541 +               perror_msg_and_die("output filesystem superblock");
2542 +       if(fs->swapit)
2543 +               swap_sb(fs->sb);
2544  }
2545  
2546  static void
2547 @@ -2419,10 +3146,12 @@
2548         "  -x, --starting-image <image>\n"
2549         "  -d, --root <directory>\n"
2550         "  -D, --devtable <file>\n"
2551 +       "  -B, --block-size <bytes>\n"
2552         "  -b, --size-in-blocks <blocks>\n"
2553         "  -i, --bytes-per-inode <bytes per inode>\n"
2554         "  -N, --number-of-inodes <number of inodes>\n"
2555         "  -m, --reserved-percentage <percentage of blocks to reserve>\n"
2556 +       "  -o, --creator-os <os>      'linux' (default), 'hurd', 'freebsd' or number.\n"
2557         "  -g, --block-map <path>     Generate a block map file for this path.\n"
2558         "  -e, --fill-value <value>   Fill unallocated blocks with value.\n"
2559         "  -z, --allow-holes          Allow files with holes.\n"
2560 @@ -2444,15 +3173,34 @@
2561  extern char* optarg;
2562  extern int optind, opterr, optopt;
2563  
2564 +// parse the value for -o <os>
2565 +int
2566 +lookup_creator_os(const char *name)
2567 +{
2568 +        if (isdigit (*name))
2569 +                return atoi(name);
2570 +        else if (strcasecmp(name, "linux") == 0)
2571 +                return EXT2_OS_LINUX;
2572 +        else if (strcasecmp(name, "GNU") == 0 || strcasecmp(name, "hurd") == 0)
2573 +                return EXT2_OS_HURD;
2574 +        else if (strcasecmp(name, "freebsd") == 0)
2575 +                return EXT2_OS_FREEBSD;
2576 +        else if (strcasecmp(name, "lites") == 0)
2577 +                return EXT2_OS_LITES;
2578 +        else
2579 +                return EXT2_OS_LINUX;
2580 +}
2581 +
2582  int
2583  main(int argc, char **argv)
2584  {
2585 -       int nbblocks = -1;
2586 +       long long nbblocks = -1;
2587         int nbinodes = -1;
2588         int nbresrvd = -1;
2589         float bytes_per_inode = -1;
2590         float reserved_frac = -1;
2591         int fs_timestamp = -1;
2592 +       int creator_os = CREATOR_OS;
2593         char * fsout = "-";
2594         char * fsin = 0;
2595         char * dopt[MAX_DOPT];
2596 @@ -2466,6 +3214,7 @@
2597         int squash_perms = 0;
2598         uint16 endian = 1;
2599         int bigendian = !*(char*)&endian;
2600 +       char *volumelabel = NULL;
2601         filesystem *fs;
2602         int i;
2603         int c;
2604 @@ -2476,13 +3225,16 @@
2605           { "starting-image",   required_argument,      NULL, 'x' },
2606           { "root",             required_argument,      NULL, 'd' },
2607           { "devtable",         required_argument,      NULL, 'D' },
2608 +         { "block-size",       required_argument,      NULL, 'B' },
2609           { "size-in-blocks",   required_argument,      NULL, 'b' },
2610           { "bytes-per-inode",  required_argument,      NULL, 'i' },
2611           { "number-of-inodes", required_argument,      NULL, 'N' },
2612 +         { "volume-label",     required_argument,      NULL, 'L' },
2613           { "reserved-percentage", required_argument,   NULL, 'm' },
2614 +         { "creator-os",       required_argument,      NULL, 'o' },
2615           { "block-map",        required_argument,      NULL, 'g' },
2616           { "fill-value",       required_argument,      NULL, 'e' },
2617 -         { "allow-holes",      no_argument,            NULL, 'z' },
2618 +         { "allow-holes",      no_argument,            NULL, 'z' },
2619           { "faketime",         no_argument,            NULL, 'f' },
2620           { "squash",           no_argument,            NULL, 'q' },
2621           { "squash-uids",      no_argument,            NULL, 'U' },
2622 @@ -2495,11 +3247,11 @@
2623  
2624         app_name = argv[0];
2625  
2626 -       while((c = getopt_long(argc, argv, "x:d:D:b:i:N:m:g:e:zfqUPhVv", longopts, NULL)) != EOF) {
2627 +       while((c = getopt_long(argc, argv, "x:d:D:B:b:i:N:L:m:o:g:e:zfqUPhVv", longopts, NULL)) != EOF) {
2628  #else
2629         app_name = argv[0];
2630  
2631 -       while((c = getopt(argc, argv,      "x:d:D:b:i:N:m:g:e:zfqUPhVv")) != EOF) {
2632 +       while((c = getopt(argc, argv,      "x:d:D:B:b:i:N:L:m:o:g:e:zfqUPhVv")) != EOF) {
2633  #endif /* HAVE_GETOPT_LONG */
2634                 switch(c)
2635                 {
2636 @@ -2510,6 +3262,9 @@
2637                         case 'D':
2638                                 dopt[didx++] = optarg;
2639                                 break;
2640 +                       case 'B':
2641 +                               blocksize = SI_atof(optarg);
2642 +                               break;
2643                         case 'b':
2644                                 nbblocks = SI_atof(optarg);
2645                                 break;
2646 @@ -2519,9 +3274,15 @@
2647                         case 'N':
2648                                 nbinodes = SI_atof(optarg);
2649                                 break;
2650 +                       case 'L':
2651 +                               volumelabel = optarg;
2652 +                               break;
2653                         case 'm':
2654                                 reserved_frac = SI_atof(optarg) / 100;
2655                                 break;
2656 +                       case 'o':
2657 +                               creator_os = lookup_creator_os(optarg);
2658 +                               break;
2659                         case 'g':
2660                                 gopt[gidx++] = optarg;
2661                                 break;
2662 @@ -2565,21 +3326,21 @@
2663                 error_msg_and_die("Not enough arguments. Try --help or else see the man page.");
2664         fsout = argv[optind];
2665  
2666 -       hdlinks.hdl = (struct hdlink_s *)malloc(hdlink_cnt * sizeof(struct hdlink_s));
2667 -       if (!hdlinks.hdl)
2668 -               error_msg_and_die("Not enough memory");
2669 -       hdlinks.count = 0 ;
2670 +       if(blocksize != 1024 && blocksize != 2048 && blocksize != 4096)
2671 +               error_msg_and_die("Valid block sizes: 1024, 2048 or 4096.");
2672 +       if(creator_os < 0)
2673 +               error_msg_and_die("Creator OS unknown.");
2674  
2675         if(fsin)
2676         {
2677                 if(strcmp(fsin, "-"))
2678                 {
2679                         FILE * fh = xfopen(fsin, "rb");
2680 -                       fs = load_fs(fh, bigendian);
2681 +                       fs = load_fs(fh, bigendian, fsout);
2682                         fclose(fh);
2683                 }
2684                 else
2685 -                       fs = load_fs(stdin, bigendian);
2686 +                       fs = load_fs(stdin, bigendian, fsout);
2687         }
2688         else
2689         {
2690 @@ -2609,16 +3370,29 @@
2691                 }
2692                 if(fs_timestamp == -1)
2693                         fs_timestamp = time(NULL);
2694 -               fs = init_fs(nbblocks, nbinodes, nbresrvd, holes, fs_timestamp);
2695 +               fs = init_fs(nbblocks, nbinodes, nbresrvd, holes,
2696 +                            fs_timestamp, creator_os, bigendian, fsout);
2697         }
2698 +       if (volumelabel != NULL)
2699 +               strncpy((char *)fs->sb->s_volume_name, volumelabel,
2700 +                       sizeof(fs->sb->s_volume_name));
2701         
2702         populate_fs(fs, dopt, didx, squash_uids, squash_perms, fs_timestamp, NULL);
2703  
2704         if(emptyval) {
2705                 uint32 b;
2706 -               for(b = 1; b < fs->sb.s_blocks_count; b++)
2707 -                       if(!allocated(GRP_GET_BLOCK_BITMAP(fs,b),GRP_BBM_OFFSET(fs,b)))
2708 -                               memset(get_blk(fs, b), emptyval, BLOCKSIZE);
2709 +               for(b = 1; b < fs->sb->s_blocks_count; b++) {
2710 +                       blk_info *bi;
2711 +                       gd_info *gi;
2712 +                       if(!allocated(GRP_GET_BLOCK_BITMAP(fs,b,&bi,&gi),
2713 +                                     GRP_BBM_OFFSET(fs,b))) {
2714 +                               blk_info *bi2;
2715 +                               memset(get_blk(fs, b, &bi2), emptyval,
2716 +                                      BLOCKSIZE);
2717 +                               put_blk(bi2);
2718 +                       }
2719 +                       GRP_PUT_BLOCK_BITMAP(bi,gi);
2720 +               }
2721         }
2722         if(verbose)
2723                 print_fs(fs);
2724 @@ -2628,24 +3402,22 @@
2725                 char fname[MAX_FILENAME];
2726                 char *p;
2727                 FILE *fh;
2728 +               nod_info *ni;
2729                 if(!(nod = find_path(fs, EXT2_ROOT_INO, gopt[i])))
2730                         error_msg_and_die("path %s not found in filesystem", gopt[i]);
2731                 while((p = strchr(gopt[i], '/')))
2732                         *p = '_';
2733                 SNPRINTF(fname, MAX_FILENAME-1, "%s.blk", gopt[i]);
2734                 fh = xfopen(fname, "wb");
2735 -               fprintf(fh, "%d:", get_nod(fs, nod)->i_size);
2736 +               fprintf(fh, "%d:", get_nod(fs, nod, &ni)->i_size);
2737 +               put_nod(ni);
2738                 flist_blocks(fs, nod, fh);
2739                 fclose(fh);
2740         }
2741 -       if(strcmp(fsout, "-"))
2742 -       {
2743 -               FILE * fh = xfopen(fsout, "wb");
2744 -               dump_fs(fs, fh, bigendian);
2745 -               fclose(fh);
2746 -       }
2747 -       else
2748 -               dump_fs(fs, stdout, bigendian);
2749 +       finish_fs(fs);
2750 +       if(strcmp(fsout, "-") == 0)
2751 +               copy_file(fs, stdout, fs->f, fs->sb->s_blocks_count);
2752 +
2753         free_fs(fs);
2754         return 0;
2755  }
2756 Index: genext2fs-1.4.1/cache.h
2757 ===================================================================
2758 --- /dev/null
2759 +++ genext2fs-1.4.1/cache.h
2760 @@ -0,0 +1,128 @@
2761 +#ifndef __CACHE_H__
2762 +#define __CACHE_H__
2763 +
2764 +#include "list.h"
2765 +
2766 +#define CACHE_LISTS 256
2767 +
2768 +typedef struct
2769 +{
2770 +    list_elem link;
2771 +    list_elem lru_link;
2772 +} cache_link;
2773 +
2774 +typedef struct
2775 +{
2776 +    /* LRU list holds unused items */
2777 +    unsigned int lru_entries;
2778 +    list_elem lru_list;
2779 +    unsigned int max_free_entries;
2780 +
2781 +    unsigned int entries;
2782 +    list_elem lists[CACHE_LISTS];
2783 +    unsigned int (*elem_val)(cache_link *elem);
2784 +    void (*freed)(cache_link *elem);
2785 +} listcache;
2786 +
2787 +static inline void
2788 +cache_add(listcache *c, cache_link *elem)
2789 +{
2790 +    unsigned int hash = c->elem_val(elem) % CACHE_LISTS;
2791 +    int delcount = c->lru_entries - c->max_free_entries;
2792 +
2793 +    if (delcount > 0) {
2794 +        /* Delete some unused items. */
2795 +        list_elem *lru, *next;
2796 +        cache_link *l;
2797 +        list_for_each_elem_safe(&c->lru_list, lru, next) {
2798 +            l = container_of(lru, cache_link, lru_link);
2799 +            list_del(lru);
2800 +            list_del(&l->link);
2801 +            c->entries--;
2802 +            c->lru_entries--;
2803 +            c->freed(l);
2804 +            delcount--;
2805 +            if (delcount <= 0)
2806 +                break;
2807 +        }
2808 +    }
2809 +
2810 +    c->entries++;
2811 +    list_item_init(&elem->lru_link); /* Mark it not in the LRU list */
2812 +    list_add_after(&c->lists[hash], &elem->link);
2813 +}
2814 +
2815 +static inline void
2816 +cache_item_set_unused(listcache *c, cache_link *elem)
2817 +{
2818 +    list_add_before(&c->lru_list, &elem->lru_link);
2819 +    c->lru_entries++;
2820 +}
2821 +
2822 +static inline cache_link *
2823 +cache_find(listcache *c, unsigned int val)
2824 +{
2825 +    unsigned int hash = val % CACHE_LISTS;
2826 +    list_elem *elem;
2827 +
2828 +    list_for_each_elem(&c->lists[hash], elem) {
2829 +        cache_link *l = container_of(elem, cache_link, link);
2830 +        if (c->elem_val(l) == val) {
2831 +            if (!list_empty(&l->lru_link)) {
2832 +                /* It's in the unused list, remove it. */
2833 +                list_del(&l->lru_link);
2834 +                list_item_init(&l->lru_link);
2835 +                c->lru_entries--;
2836 +            }
2837 +            return l;
2838 +        }
2839 +    }
2840 +    return NULL;
2841 +}
2842 +
2843 +static inline int
2844 +cache_flush(listcache *c)
2845 +{
2846 +    list_elem *elem, *next;
2847 +    cache_link *l;
2848 +    int i;
2849 +
2850 +    list_for_each_elem_safe(&c->lru_list, elem, next) {
2851 +        l = container_of(elem, cache_link, lru_link);
2852 +        list_del(elem);
2853 +        list_del(&l->link);
2854 +        c->entries--;
2855 +        c->lru_entries--;
2856 +        c->freed(l);
2857 +    }
2858 +
2859 +    for (i = 0; i < CACHE_LISTS; i++) {
2860 +        list_for_each_elem_safe(&c->lists[i], elem, next) {
2861 +            l = container_of(elem, cache_link, link);
2862 +            list_del(&l->link);
2863 +            c->entries--;
2864 +            c->freed(l);
2865 +        }
2866 +    }
2867 +
2868 +    return c->entries || c->lru_entries;
2869 +}
2870 +
2871 +static inline void
2872 +cache_init(listcache *c, unsigned int max_free_entries,
2873 +       unsigned int (*elem_val)(cache_link *elem),
2874 +       void (*freed)(cache_link *elem))
2875 +{
2876 +    int i;
2877 +
2878 +    c->entries = 0;
2879 +    c->lru_entries = 0;
2880 +    c->max_free_entries = max_free_entries;
2881 +    list_init(&c->lru_list);
2882 +    for (i = 0; i < CACHE_LISTS; i++)
2883 +        list_init(&c->lists[i]);
2884 +    c->elem_val = elem_val;
2885 +    c->freed = freed;
2886 +}
2887 +
2888 +#endif /* __CACHE_H__ */
2889 Index: genext2fs-1.4.1/list.h
2890 ===================================================================
2891 --- /dev/null
2892 +++ genext2fs-1.4.1/list.h
2893 @@ -0,0 +1,78 @@
2894 +#ifndef __LIST_H__
2895 +#define __LIST_H__
2896 +
2897 +#if STDC_HEADERS
2898 +# include <stdlib.h>
2899 +# include <stddef.h>
2900 +#else
2901 +# if HAVE_STDLIB_H
2902 +#  include <stdlib.h>
2903 +# endif
2904 +# if HAVE_STDDEF_H
2905 +#  include <stddef.h>
2906 +# endif
2907 +#endif
2908 +
2909 +#ifndef offsetof
2910 +#define offsetof(st, m) \
2911 +     ((size_t) ( (char *)&((st *)(0))->m - (char *)0 ))
2912 +#endif
2913 +
2914 +#define container_of(ptr, type, member) ({ \
2915 +                const typeof( ((type *)0)->member ) *__mptr = (ptr); \
2916 +                (type *)( (char *)__mptr - offsetof(type,member) );})
2917 +
2918 +typedef struct list_elem
2919 +{
2920 +    struct list_elem *next;
2921 +    struct list_elem *prev;
2922 +} list_elem;
2923 +
2924 +static inline void list_init(list_elem *list)
2925 +{
2926 +    list->next = list;
2927 +    list->prev = list;
2928 +}
2929 +
2930 +static inline void list_add_after(list_elem *pos, list_elem *elem)
2931 +{
2932 +    elem->next = pos->next;
2933 +    elem->prev = pos;
2934 +    pos->next->prev = elem;
2935 +    pos->next = elem;
2936 +}
2937 +
2938 +static inline void list_add_before(list_elem *pos, list_elem *elem)
2939 +{
2940 +    elem->prev = pos->prev;
2941 +    elem->next = pos;
2942 +    pos->prev->next = elem;
2943 +    pos->prev = elem;
2944 +}
2945 +
2946 +static inline void list_del(list_elem *elem)
2947 +{
2948 +    elem->next->prev = elem->prev;
2949 +    elem->prev->next = elem->next;
2950 +}
2951 +
2952 +static inline void list_item_init(list_elem *elem)
2953 +{
2954 +    elem->next = elem;
2955 +    elem->prev = elem;
2956 +}
2957 +
2958 +static inline int list_empty(list_elem *elem)
2959 +{
2960 +    return elem->next == elem;
2961 +}
2962 +
2963 +#define list_for_each_elem(list, curr)            \
2964 +    for ((curr) = (list)->next; (curr) != (list); (curr) = (curr)->next)
2965 +
2966 +#define list_for_each_elem_safe(list, curr, next)    \
2967 +    for ((curr) = (list)->next, (next) = (curr)->next;    \
2968 +         (curr) != (list);                    \
2969 +         (curr) = (next), (next) = (curr)->next)
2970 +
2971 +#endif /* __LIST_H__ */