Initial commit
authorRobert Pengelly <robertapengelly@hotmail.com>
Sat, 30 Aug 2025 21:46:47 +0000 (22:46 +0100)
committerRobert Pengelly <robertapengelly@hotmail.com>
Sat, 30 Aug 2025 21:46:47 +0000 (22:46 +0100)
17 files changed:
LICENSE [new file with mode: 0644]
Makefile.unix [new file with mode: 0644]
Makefile.w32 [new file with mode: 0644]
README.md [new file with mode: 0644]
cache.c [new file with mode: 0644]
cache.h [new file with mode: 0644]
common.c [new file with mode: 0644]
common.h [new file with mode: 0644]
e2cp.c [new file with mode: 0644]
e2md.c [new file with mode: 0644]
inode.h [new file with mode: 0644]
list.c [new file with mode: 0644]
list.h [new file with mode: 0644]
mke2fs.c [new file with mode: 0644]
report.c [new file with mode: 0644]
report.h [new file with mode: 0644]
super.h [new file with mode: 0644]

diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..fdddb29
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <https://unlicense.org>
diff --git a/Makefile.unix b/Makefile.unix
new file mode 100644 (file)
index 0000000..538b3a0
--- /dev/null
@@ -0,0 +1,49 @@
+#******************************************************************************
+# @file             Makefile.unix
+#******************************************************************************
+SRCDIR              ?=  $(CURDIR)
+VPATH               :=  $(SRCDIR)
+
+CC                  :=  gcc
+CFLAGS              :=  -D_FILE_OFFSET_BITS=64 -Wall -Werror -Wextra -std=c90
+
+ifeq ($(OS), Windows_NT)
+all: mke2fs.exe e2md.exe e2cp.exe
+
+mke2fs.exe: mke2fs.c cache.c common.c list.c report.c
+
+       $(CC) $(CFLAGS) -o $@ $^
+
+e2md.exe: e2md.c cache.c common.c list.c report.c
+
+       $(CC) $(CFLAGS) -o $@ $^
+
+e2cp.exe: e2cp.c cache.c common.c list.c report.c
+
+       $(CC) $(CFLAGS) -o $@ $^
+else
+all: mke2fs e2md e2cp
+
+mke2fs: mke2fs.c cache.c common.c list.c report.c
+
+       $(CC) $(CFLAGS) -o $@ $^
+
+e2md: e2md.c cache.c common.c list.c report.c
+
+       $(CC) $(CFLAGS) -o $@ $^
+
+e2cp: e2cp.c cache.c common.c list.c report.c
+
+       $(CC) $(CFLAGS) -o $@ $^
+endif
+
+clean:
+
+       if [ -f mke2fs.exe ]; then rm -rf mke2fs.exe; fi
+       if [ -f mke2fs ]; then rm -rf mke2fs; fi
+
+       if [ -f e2md.exe ]; then rm -rf e2md.exe; fi
+       if [ -f e2md ]; then rm -rf e2md; fi
+
+       if [ -f e2cp.exe ]; then rm -rf e2cp.exe; fi
+       if [ -f e2cp ]; then rm -rf e2cp; fi
diff --git a/Makefile.w32 b/Makefile.w32
new file mode 100644 (file)
index 0000000..7f09f4e
--- /dev/null
@@ -0,0 +1,33 @@
+#******************************************************************************
+# @file             Makefile.w32
+#******************************************************************************
+SRCDIR              ?=  $(CURDIR)
+VPATH               :=  $(SRCDIR)
+
+CC                  :=  gcc
+CFLAGS              :=  -D_FILE_OFFSET_BITS=64 -Wall -Werror -Wextra -std=c90
+
+all: mke2fs.exe e2md.exe e2cp.exe
+
+clean:
+
+       if exist mke2fs.exe ( del /q mke2fs.exe )
+       if exist mke2fs ( del /q mke2fs )
+
+       if exist e2md.exe ( del /q e2md.exe )
+       if exist e2md ( del /q e2md )
+
+       if exist e2cp.exe ( del /q e2cp.exe )
+       if exist e2cp ( del /q e2cp )
+
+mke2fs.exe: mke2fs.c cache.c common.c list.c report.c
+
+       $(CC) $(CFLAGS) -o $@ $^
+
+e2md.exe: e2md.c cache.c common.c list.c report.c
+
+       $(CC) $(CFLAGS) -o $@ $^
+
+e2cp.exe: e2cp.c cache.c common.c list.c report.c
+
+       $(CC) $(CFLAGS) -o $@ $^
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..e5dfd8c
--- /dev/null
+++ b/README.md
@@ -0,0 +1,23 @@
+All source code is Public Domain.
+
+## Obtain the source code
+
+    git clone https://git.candlhat.org/e2fsprogs.git
+
+## Building
+
+    BSD:
+    
+        Make sure you have gcc and gmake installed then run gmake -f Makefile.unix.
+    
+    Linux:
+    
+        Make sure you have gcc and make installed then run make -f Makefile.unix.
+    
+    macOS:
+    
+        Make sure you have xcode command line tools installed then run make -f Makefile.unix.
+    
+    Windows:
+    
+        Make sure you have mingw installed and the location within your PATH variable then run mingw32-make.exe -f Makefile.w32.
diff --git a/cache.c b/cache.c
new file mode 100644 (file)
index 0000000..40c3c8d
--- /dev/null
+++ b/cache.c
@@ -0,0 +1,140 @@
+/******************************************************************************
+ * @file            cache.h
+ *****************************************************************************/
+#include    "cache.h"
+#include    "list.h"
+
+struct cache_link *cache_find (struct listcache *c, unsigned long val) {
+
+    unsigned long hash = val % CACHE_LISTS;
+    struct list_elem *elem;
+    
+    list_for_each_elem (&c->lists[hash], elem) {
+    
+        struct cache_link *l = container_of (elem, struct cache_link, link);
+        
+        if (c->elem_val (l) == val) {
+        
+            if (!list_empty (&l->lru_link)) {
+            
+                /* It's in the unused list, remove it.*/
+                list_del (&l->lru_link);
+                
+                list_item_init (&l->lru_link);
+                c->lru_entries--;
+            
+            }
+            
+            return l;
+        
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+int cache_flush (struct listcache *c) {
+
+    struct list_elem *elem, *next;
+    struct cache_link *l;
+    
+    unsigned long i;
+    
+    list_for_each_elem_safe (&c->lru_list, elem, next) {
+    
+        l = container_of (elem, struct cache_link, lru_link);
+        
+        list_del (elem);
+        list_del (&l->link);
+        
+        c->entries--;
+        c->lru_entries--;
+        
+        c->freed (l);
+    
+    }
+    
+    for (i = 0; i < CACHE_LISTS; i++) {
+    
+        list_for_each_elem_safe (&c->lists[i], elem, next) {
+        
+            l = container_of (elem, struct cache_link, lru_link);
+            list_del (&l->link);
+            
+            c->entries--;
+            c->freed (l);
+        
+        }
+    
+    }
+    
+    return c->entries || c->lru_entries;
+
+}
+
+void cache_add (struct listcache *c, struct cache_link *elem) {
+
+    unsigned long hash = c->elem_val (elem) % CACHE_LISTS;
+    long delcount = c->lru_entries - c->max_free_entries;
+    
+    if (delcount > 0) {
+    
+        /* Delete some unused items. */
+        struct list_elem *lru, *next;
+        struct cache_link *l;
+        
+        list_for_each_elem_safe (&c->lru_list, lru, next) {
+        
+            l = container_of (lru, struct cache_link, lru_link);
+            
+            list_del (lru);
+            list_del (&l->link);
+            
+            c->entries--;
+            c->lru_entries--;
+            
+            c->freed (l);
+            delcount--;
+            
+            if (delcount <= 0) {
+                break;
+            }
+        
+        }
+    
+    }
+    
+    c->entries++;
+    
+    list_item_init (&elem->lru_link);                       /* Mark it not in the LRU list */
+    list_add_after (&c->lists[hash], &elem->link);
+
+}
+
+void cache_init (struct listcache *c, unsigned long max_free_entries, unsigned long (*elem_val) (struct cache_link *elem), void (*fread) (struct cache_link *elem)) {
+
+    unsigned long i;
+    
+    c->entries = 0;
+    c->lru_entries = 0;
+    c->max_free_entries = max_free_entries;
+    
+    list_init (&c->lru_list);
+    
+    for (i = 0; i < CACHE_LISTS; i++) {
+        list_init (&c->lists[i]);
+    }
+    
+    c->elem_val = elem_val;
+    c->freed = fread;
+
+}
+
+void cache_item_set_unused (struct listcache *c, struct cache_link *elem) {
+
+    list_add_before (&c->lru_list, &elem->lru_link);
+    c->lru_entries++;
+
+}
diff --git a/cache.h b/cache.h
new file mode 100644 (file)
index 0000000..e377ffa
--- /dev/null
+++ b/cache.h
@@ -0,0 +1,39 @@
+/******************************************************************************
+ * @file            cache.h
+ *****************************************************************************/
+#ifndef     _CACHE_H
+#define     _CACHE_H
+
+#include    "list.h"
+#define     CACHE_LISTS                 256
+
+struct cache_link {
+
+    struct list_elem link;
+    struct list_elem lru_link;
+
+};
+
+struct listcache {
+
+    unsigned long lru_entries;
+    struct list_elem lru_list;
+    
+    unsigned long max_free_entries;
+    unsigned long entries;
+    
+    struct list_elem lists[CACHE_LISTS];
+    
+    unsigned long (*elem_val) (struct cache_link *elem);
+    void (*freed) (struct cache_link *elem);
+
+};
+
+struct cache_link *cache_find (struct listcache *c, unsigned long val);
+int cache_flush (struct listcache *c);
+
+void cache_add (struct listcache *c, struct cache_link *elem);
+void cache_init (struct listcache *c, unsigned long max_free_entries, unsigned long (*elem_val) (struct cache_link *elem), void (*fread) (struct cache_link *elem));
+void cache_item_set_unused (struct listcache *c, struct cache_link *elem);
+
+#endif      /* _CACHE_H */
diff --git a/common.c b/common.c
new file mode 100644 (file)
index 0000000..aff66f1
--- /dev/null
+++ b/common.c
@@ -0,0 +1,1440 @@
+/******************************************************************************
+ * @file            common.c
+ *****************************************************************************/
+#include    <limits.h>
+#include    <stdio.h>
+#include    <stdlib.h>
+#include    <string.h>
+
+#include    "common.h"
+#include    "inode.h"
+#include    "report.h"
+
+unsigned long offset = 0;
+
+char *program_name = 0;
+char *outfile = 0;
+
+FILE *ofp = 0;
+
+int seekto (FILE *fp, unsigned long sector) {
+
+    unsigned long off = offset * SECTOR_SIZE;
+    off += sector * BLOCKSIZE;
+    
+    return fseek (fp, off, SEEK_SET);
+
+}
+
+
+int mode_con (char *p) {
+
+    unsigned long o1, o2, o3;
+    
+    char c1, c2, c3;
+    int mode;
+    
+    c1 = *p++;
+    c2 = *p++;
+    c3 = *p++;
+    
+    o1 = *p++ - '0';
+    o2 = *p++ - '0';
+    o3 = *p++ - '0';
+    
+    mode = (o1 << 6) | (o2 << 3) | o3;
+    
+    if (c1 == 'c') {
+        mode |= 0020000;
+    } else if (c1 == 'd') {
+        mode |= 0040000;
+    } else if (c1 == 'b') {
+        mode |= 0060000;
+    } else if (c1 == '-') {
+        mode |= 0100000;
+    }
+    
+    if (c2 == 'u') {
+        mode |= 0004000;
+    }
+    
+    if (c3 == 'g') {
+        mode |= 0002000;
+    }
+    
+    return mode;
+
+}
+
+
+unsigned long byte_array_to_integer (unsigned char *arr, int size) {
+
+    unsigned long val = 0;
+    int i;
+    
+    for (i = 0; i < size; i++) {
+        val |= arr[i] << (CHAR_BIT * i);
+    }
+    
+    return val;
+
+}
+
+void write_to_byte_array (unsigned char *arr, unsigned long value, int size) {
+
+    int i;
+    
+    for (i = 0; i < size; i++) {
+        arr[i] = (value >> (CHAR_BIT * i)) & UCHAR_MAX;
+    }
+
+}
+
+
+char *xstrdup (const char *str) {
+
+    char *ptr = xmalloc (strlen (str) + 1);
+    strcpy (ptr, str);
+    
+    return ptr;
+
+}
+
+void *xmalloc (unsigned long size) {
+
+    void *ptr = malloc (size);
+    
+    if (ptr == NULL && size) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "memory full (malloc)");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    memset (ptr, 0, size);
+    return ptr;
+
+}
+
+void *xrealloc (void *ptr, unsigned long size) {
+
+    void *new_ptr = realloc (ptr, size);
+    
+    if (new_ptr == NULL && size) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "memory full (realloc)");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    return new_ptr;
+
+}
+
+
+#define     MAX_FREE_CACHE_BLOCKS       100
+
+static unsigned long blk_elem_val (struct cache_link *elem) {
+    
+    struct blk_info *bi = container_of (elem, struct blk_info, link);
+    return bi->blk;
+    
+}
+
+static void blk_freed (struct cache_link *elem) {
+
+    struct blk_info *bi = container_of (elem, struct blk_info, link);
+    
+    if (seekto (ofp, bi->blk)) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "fseek");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    if (fwrite (bi->b, BLOCKSIZE, 1, ofp) != 1) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "blk_freed: write");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    free (bi->b);
+    free (bi);
+
+}
+
+#define     MAX_FREE_CACHE_GDS          100
+
+static unsigned long gd_elem_val (struct cache_link *elem) {
+
+    struct gd_info *gi = container_of (elem, struct gd_info, link);
+    return gi->gds;
+
+}
+
+static void gd_freed (struct cache_link *elem) {
+
+    struct gd_info *gi = container_of (elem, struct gd_info, link);
+    put_blk (gi->bi);
+    
+    free (gi);
+
+}
+
+struct blkmap_info {
+
+    struct cache_link link;
+    
+    struct filesystem *fs;
+    unsigned long blk;
+    
+    unsigned char *b;
+    struct blk_info *bi;
+    
+    unsigned long usecount;
+
+};
+
+#define     MAX_FREE_CACHE_BLOCKMAPS    100
+
+static unsigned long blkmap_elem_val (struct cache_link *elem) {
+
+    struct blkmap_info *bmi = container_of (elem, struct blkmap_info, link);
+    return bmi->blk;
+
+}
+
+static void blkmap_freed (struct cache_link *elem) {
+
+    struct blkmap_info *bmi = container_of (elem, struct blkmap_info, link);
+    put_blk (bmi->bi);
+    
+    free (bmi);
+
+}
+
+#define     MAX_FREE_CACHE_INODES       100
+
+static unsigned long inode_elem_val (struct cache_link *elem) {
+
+    struct nod_info *ni = container_of (elem, struct nod_info, link);
+    return ni->nod;
+
+}
+
+static void inode_freed (struct cache_link *elem) {
+
+    struct nod_info *ni = container_of (elem, struct nod_info, link);
+    put_blk (ni->bi);
+    
+    free (ni);
+
+}
+
+#define     GDS_START                   (EXT2_SUPERBLOCK_OFFSET + sizeof (struct superblock) + BLOCKSIZE - 1) / BLOCKSIZE
+#define     GDS_PER_BLOCK               (BLOCKSIZE / sizeof (struct group_descriptor))
+
+struct group_descriptor *get_gd (struct filesystem *fs, unsigned long no, struct gd_info **rgi) {
+
+    unsigned long gdblk, offset;
+    
+    struct cache_link *curr;
+    struct gd_info *gi;
+    
+    if ((curr = cache_find (&fs->gds, no))) {
+    
+        gi = container_of (curr, struct gd_info, link);
+        gi->usecount++;
+        
+        goto out;
+    
+    }
+    
+    gi = xmalloc (sizeof (*gi));
+    
+    gi->fs = fs;
+    gi->gds = no;
+    gi->usecount = 1;
+    
+    gdblk = GDS_START + (no / GDS_PER_BLOCK);
+    offset = no % GDS_PER_BLOCK;
+    
+    gi->gd = ((struct group_descriptor *) get_blk (fs, gdblk, &gi->bi)) + offset;
+    cache_add (&fs->gds, &gi->link);
+
+out:
+    
+    *rgi = gi;
+    return gi->gd;
+
+}
+
+void put_gd (struct gd_info *gi) {
+
+    if (gi->usecount == 0) {
+    
+        report_at (program_name, 0, REPORT_INTERNAL_ERROR, "put_gd usecount zero");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    gi->usecount--;
+    
+    if (gi->usecount == 0) {
+    
+        /* Free happens in the cache code. */
+        cache_item_set_unused (&gi->fs->gds, &gi->link);
+    
+    }
+
+}
+
+#define     HDLINK_CNT                  16
+
+struct filesystem *alloc_fs (void) {
+
+    struct filesystem *fs = xmalloc (sizeof (*fs));
+    
+    cache_init (&fs->blks, MAX_FREE_CACHE_BLOCKS, blk_elem_val, blk_freed);
+    cache_init (&fs->gds, MAX_FREE_CACHE_GDS, gd_elem_val, gd_freed);
+    cache_init (&fs->blkmaps, MAX_FREE_CACHE_BLOCKMAPS, blkmap_elem_val, blkmap_freed);
+    cache_init (&fs->indoes, MAX_FREE_CACHE_INODES, inode_elem_val, inode_freed);
+    
+    fs->hdlink_cnt = HDLINK_CNT;
+    
+    fs->hdlinks.hdl = xmalloc (sizeof (struct hdlink_s) * fs->hdlink_cnt);
+    fs->hdlinks.count = 0;
+    
+    return fs;
+
+}
+
+void finish_fs (struct filesystem *fs) {
+
+    if (cache_flush (&fs->indoes)) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "entry mismatch on inode cache flush");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    if (cache_flush (&fs->blkmaps)) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "entry mismatch on blockmap cache flush");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    if (cache_flush (&fs->gds)) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "entry mismatch on gd cache flush");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    if (cache_flush (&fs->blks)) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "entry mismatch on block cache flush");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    if (seekto (ofp, EXT2_SUPERBLOCK_OFFSET / BLOCKSIZE)) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "fseek");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    if (fwrite (fs->sb, sizeof (*fs->sb), 1, ofp) != 1) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "output filesystem superblock");
+        exit (EXIT_FAILURE);
+    
+    }
+
+}
+
+void put_nod (struct nod_info *ni) {
+
+    if (ni->usecount == 0) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "put_nod: usecount zero");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    ni->usecount--;
+    
+    if (ni->usecount == 0) {
+    
+        /* Free happens in the cache code. */
+        cache_item_set_unused (&ni->fs->indoes, &ni->link);
+    
+    }
+
+}
+
+#define     GRP_GROUP_OF_INODE(fs, nod)     \
+    (((nod) - 1) / byte_array_to_integer ((fs)->sb->s_inodes_per_group, 4))
+
+#define     GRP_IBM_OFFSET(fs, nod)     \
+    ((nod) - GRP_GROUP_OF_INODE ((fs), (nod)) * byte_array_to_integer ((fs)->sb->s_inodes_per_group, 4))
+
+#define     INODES_PER_BLOCK            (BLOCKSIZE / sizeof (struct inode))
+
+struct inode *get_nod (struct filesystem *fs, unsigned long nod, struct nod_info **rni) {
+
+    unsigned long grp, boffset, offset;
+    
+    struct group_descriptor *gd;
+    struct cache_link *curr;
+    
+    struct nod_info *ni;
+    struct gd_info *gi;
+    
+    if ((curr = cache_find (&fs->indoes, nod))) {
+    
+        ni = container_of (curr, struct nod_info, link);
+        ni->usecount++;
+        
+        goto out;
+    
+    }
+    
+    ni = xmalloc (sizeof (*ni));
+    
+    ni->fs = fs;
+    ni->nod = nod;
+    ni->usecount = 1;
+    
+    cache_add (&fs->indoes, &ni->link);
+    offset = GRP_IBM_OFFSET (fs, nod) - 1;
+    
+    boffset = offset / INODES_PER_BLOCK;
+    offset %= INODES_PER_BLOCK;
+    
+    grp = GRP_GROUP_OF_INODE (fs, nod);
+    gd = get_gd (fs, grp, &gi);
+    
+    ni->b = get_blk (fs, byte_array_to_integer (gd->bg_inode_table, 4) + boffset, &ni->bi);
+    ni->itab = ((struct inode *) ni->b) + offset;
+    
+    put_gd (gi);
+    
+out:
+
+    *rni = ni;
+    return ni->itab;
+
+}
+
+#define     GRP_NBGROUPS(fs)                                                    \
+    ((byte_array_to_integer ((fs)->sb->s_blocks_count, 4) -                     \
+      byte_array_to_integer ((fs)->sb->s_first_data_block, 4) +                 \
+      byte_array_to_integer ((fs)->sb->s_blocks_per_group, 4) - 1) /            \
+     byte_array_to_integer ((fs)->sb->s_blocks_per_group, 4)                       \
+    )
+
+static unsigned long alloc_nod (struct filesystem *fs) {
+
+    unsigned long best_group = 0, nod;
+    unsigned long grp, nbgroups, avefreei;
+    
+    struct group_descriptor *gd, *bestgd;
+    struct gd_info *gi, *bestgi;
+    
+    unsigned long inodes_count = 0;
+    struct blk_info *bi;
+    
+    nbgroups = GRP_NBGROUPS (fs);
+    
+    avefreei = byte_array_to_integer (fs->sb->s_free_inodes_count, 4) / nbgroups;
+    bestgd = get_gd (fs, best_group, &bestgi);
+    
+    for (grp = 0; grp < nbgroups; grp++) {
+    
+        gd = get_gd (fs, grp, &gi);
+        
+        if (byte_array_to_integer (gd->bg_free_inodes_count, 2) < avefreei || byte_array_to_integer (gd->bg_free_inodes_count, 2) == 0) {
+        
+            put_gd (gi);
+            continue;
+        
+        }
+        
+        if (!best_group || byte_array_to_integer (gd->bg_free_blocks_count, 2) > byte_array_to_integer (bestgd->bg_free_blocks_count, 2)) {
+        
+            put_gd (bestgi);
+            
+            best_group = grp;
+            bestgd = gd;
+            bestgi = gi;
+        
+        } else {
+            put_gd (gi);
+        }
+    
+    }
+    
+    if (!(nod = allocate (GRP_GET_GROUP_IBM (fs, bestgd, &bi), 0))) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "couldn't allocate an inode (no free inode)");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    GRP_PUT_GROUP_IBM (bi);
+    
+    if ((inodes_count = byte_array_to_integer (bestgd->bg_free_inodes_count, 2)) == 0) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "group descr. free blocks count == 0 (corrupted fs?)");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    write_to_byte_array (bestgd->bg_free_inodes_count, inodes_count - 1, 2);
+    put_gd (bestgi);
+    
+    if ((inodes_count = byte_array_to_integer (fs->sb->s_free_inodes_count, 4)) == 0) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "superblock free blocks count == 0 (corrupted fs?)");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    write_to_byte_array (fs->sb->s_free_inodes_count, inodes_count - 1, 4);
+    return byte_array_to_integer (fs->sb->s_inodes_per_group, 4) * best_group + nod;
+
+}
+
+static void dir_set_name (struct dirwalker *dw, const char *name, unsigned long nlen) {
+
+    write_to_byte_array (dw->d.d_name_len, nlen, 2);
+    strncpy (((char *) dw->last_d) + sizeof (struct directory), name, nlen);
+
+}
+
+static void add2dir (struct filesystem *fs, unsigned long dnod, unsigned long nod, const char *name) {
+
+    struct blockwalker bw, lbw;
+    struct directory *d;
+    struct dirwalker dw;
+    
+    struct inode *node, *pnode;
+    struct inode_pos ipos;
+    
+    struct nod_info *dni, *ni;
+    unsigned long bk, reclen, nlen, count, size;
+    
+    pnode = get_nod (fs, dnod, &dni);
+    
+    if ((byte_array_to_integer (pnode->i_mode, 2) & FM_IFMT) != FM_IFDIR) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "can't add '%s' to a non-directory", name);
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    if (!*name) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "can't create an inode with an empty name");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    if (strchr (name, '/')) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "bad name '%s' (contains a slash)", name);
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    nlen = strlen (name);
+    
+    if ((reclen = sizeof (struct directory) + rndup (nlen, 4)) > BLOCKSIZE) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "bad name '%s' (too long)", name);
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    init_bw (&bw);
+    lbw = bw;
+    
+    while ((bk = walk_bw (fs, dnod, &bw, 0, 0)) != WALK_END) {
+    
+        for (d = get_dir (fs, bk, &dw); d; d = next_dir (&dw)) {
+        
+            if (!byte_array_to_integer (d->d_inode, 4) && byte_array_to_integer (d->d_rec_len, 2) >= reclen) {
+            
+                write_to_byte_array (d->d_inode, nod, 4);
+                
+                node = get_nod (fs, nod, &ni);
+                dir_set_name (&dw, name, nlen);
+                
+                put_dir (&dw);
+                
+                count = byte_array_to_integer (node->i_nlinks, 2);
+                write_to_byte_array (node->i_nlinks, count + 1, 2);
+                
+                put_nod (ni);
+                goto out;
+            
+            }
+            
+            if (byte_array_to_integer (d->d_rec_len, 2) >= (sizeof (struct directory) + rndup (byte_array_to_integer (d->d_name_len, 2), 4) + reclen)) {
+            
+                d = shrink_dir (&dw, nod, name, nlen);
+                put_dir (&dw);
+                
+                node = get_nod (fs, nod, &ni);
+                
+                count = byte_array_to_integer (node->i_nlinks, 2);
+                write_to_byte_array (node->i_nlinks, count + 1, 2);
+                
+                put_nod (ni);
+                goto out;
+            
+            }
+        
+        }
+        
+        put_dir (&dw);
+        lbw = bw;
+    
+    }
+    
+    node = get_nod (fs, nod, &ni);
+    d = new_dir (fs, nod, name, nlen, &dw);
+    
+    count = byte_array_to_integer (node->i_nlinks, 2);
+    write_to_byte_array (node->i_nlinks, count + 1, 2);
+    
+    put_nod (ni);
+    next_dir (&dw);
+    
+    inode_pos_init (fs, &ipos, dnod, INODE_POS_EXTEND, &lbw);
+    extend_inode_blk (fs, &ipos, dw.b, 1);
+    inode_pos_finish (&ipos);
+    
+    put_dir (&dw);
+    
+    size = byte_array_to_integer (pnode->i_size, 4);
+    write_to_byte_array (pnode->i_size, size + BLOCKSIZE, 4);
+    
+out:
+
+    put_nod (dni);
+
+}
+
+#define     GRP_GROUP_OF_INODE(fs, nod)                     \
+    (((nod) - 1) / byte_array_to_integer ((fs)->sb->s_inodes_per_group, 4))
+
+unsigned long mknod_fs (struct filesystem *fs, unsigned long parent_nod, const char *name, unsigned long mode, unsigned short uid, unsigned short gid, unsigned long ctime, unsigned long mtime) {
+
+    unsigned long nod, used_dirs_count;
+    
+    struct inode *node;
+    struct nod_info *ni;
+    
+    struct gd_info *gi;
+    nod = alloc_nod (fs);
+    
+    node = get_nod (fs, nod, &ni);
+    write_to_byte_array (node->i_mode, mode, 2);
+    
+    add2dir (fs, parent_nod, nod, name);
+    
+    switch (mode & FM_IFMT) {
+    
+        case FM_IFDIR:
+        
+            add2dir (fs, nod, nod, ".");
+            add2dir (fs, nod, parent_nod, "..");
+            
+            get_gd (fs, GRP_GROUP_OF_INODE (fs, nod), &gi);
+            
+            used_dirs_count = byte_array_to_integer (gi->gd->bg_used_dirs_count, 2);
+            write_to_byte_array (gi->gd->bg_used_dirs_count, used_dirs_count + 1, 2);
+            
+            put_gd (gi);
+            break;
+        
+        default:
+        
+            break;
+    
+    }
+    
+    write_to_byte_array (node->i_uid, uid, 2);
+    write_to_byte_array (node->i_gid, gid, 2);
+    
+    write_to_byte_array (node->i_atime, mtime, 4);
+    write_to_byte_array (node->i_ctime, ctime, 4);
+    write_to_byte_array (node->i_mtime, mtime, 4);
+    
+    put_nod (ni);
+    return nod;
+
+}
+
+unsigned long mkdir_fs (struct filesystem *fs, unsigned long parent_nod, const char *name, unsigned long mode, unsigned short uid, unsigned short gid, unsigned long ctime, unsigned long mtime) {
+    return mknod_fs (fs, parent_nod, name, mode | FM_IFDIR, uid, gid, ctime, mtime);
+}
+
+unsigned long rndup (unsigned long qty, unsigned long siz) {
+    return (qty + (siz - 1)) & ~(siz - 1);
+}
+
+#define     GRP_GROUP_OF_BLOCK(fs, blk)                     \
+    (((blk) - 1) / byte_array_to_integer ((fs)->sb->s_blocks_per_group, 4))
+
+#define     GRP_GET_BLOCK_BITMAP(fs, blk, bi, gi)           \
+    (GRP_GET_GROUP_BBM ((fs), get_gd (fs, GRP_GROUP_OF_BLOCK ((fs), (blk)), (gi)), (bi)))
+
+#define     GRP_PUT_BLOCK_BITMAP(bi, gi)                    \
+    (GRP_PUT_GROUP_BBM ((bi)), put_gd ((gi)))
+
+#define     GRP_BBM_OFFSET(fs, blk)                         \
+    ((blk) - GRP_GROUP_OF_BLOCK ((fs), (blk)) * byte_array_to_integer ((fs)->sb->s_blocks_per_group, 4))
+
+static unsigned long allocated (unsigned char *b, unsigned long item) {
+    return b[(item - 1) / 8] & (1 << ((item - 1) % 8));
+}
+
+static void deallocate (unsigned char *block, unsigned long item) {
+    block[(item - 1) / 8] &= ~(1 << ((item - 1) % 8));
+}
+
+#define     INOBLK              (BLOCKSIZE / INODE_BLOCKSIZE)
+
+static void free_blk (struct filesystem *fs, unsigned long bk) {
+
+    struct group_descriptor *gd;
+    unsigned long grp, free_blocks;
+    
+    struct blk_info *bi;
+    struct gd_info *gi;
+    
+    grp = bk / byte_array_to_integer (fs->sb->s_blocks_per_group, 4);
+    bk %= byte_array_to_integer (fs->sb->s_blocks_per_group, 4);
+    
+    gd = get_gd (fs, grp, &gi);
+    
+    deallocate (GRP_GET_GROUP_BBM (fs, gd, &bi), bk);
+    GRP_PUT_GROUP_BBM (bi);
+    
+    free_blocks = byte_array_to_integer (gd->bg_free_blocks_count, 2);
+    write_to_byte_array (gd->bg_free_blocks_count, free_blocks + 1, 2);
+    
+    put_gd (gi);
+    
+    free_blocks = byte_array_to_integer (fs->sb->s_free_blocks_count, 4);
+    write_to_byte_array (fs->sb->s_free_blocks_count, free_blocks + 1, 4);
+
+}
+
+#if     INT_MAX == 32767
+static unsigned long *get_blkmap (struct filesystem *fs, unsigned long blk, struct blkmap_info **rbmi) {
+#else
+static unsigned int *get_blkmap (struct filesystem *fs, unsigned long blk, struct blkmap_info **rbmi) {
+#endif
+
+    struct blkmap_info *bmi;
+    struct cache_link *curr;
+    
+    if ((curr = cache_find (&fs->blkmaps, blk))) {
+    
+        bmi = container_of (curr, struct blkmap_info, link);
+        bmi->usecount++;
+        
+        goto out;
+    
+    }
+    
+    bmi = xmalloc (sizeof (*bmi));
+    bmi->usecount = 1;
+    
+    bmi->fs = fs;
+    bmi->blk = blk;
+    
+    bmi->b = get_blk (fs, blk, &bmi->bi);
+    cache_add (&fs->blkmaps, &bmi->link);
+    
+out:
+
+    *rbmi = bmi;
+    
+#if     INT_MAX == 32767
+    return (unsigned long *) bmi->b;
+#else
+    return (unsigned int *) bmi->b;
+#endif
+
+}
+
+static void put_blkmap (struct blkmap_info *bmi) {
+
+    if (bmi->usecount == 0) {
+    
+        report_at (program_name, 0, REPORT_INTERNAL_ERROR, "put_blkmap: usecount zero");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    bmi->usecount--;
+    
+    if (bmi->usecount == 0) {
+    
+        /* Free happens in the cache code. */
+        cache_item_set_unused (&bmi->fs->blkmaps, &bmi->link);
+    
+    }
+
+}
+
+static unsigned long alloc_blk (struct filesystem *fs, unsigned long nod) {
+
+    unsigned long bk = 0, grp, nbgroups;
+    unsigned long free_blocks_count;
+    
+    struct blk_info *bi;
+    struct gd_info *gi;
+    
+    struct group_descriptor *gd;
+    
+    grp = GRP_GROUP_OF_INODE (fs, nod);
+    nbgroups = GRP_NBGROUPS (fs);
+    
+    gd = get_gd (fs, grp, &gi);
+    
+    bk = allocate (GRP_GET_GROUP_BBM (fs, gd, &bi), 0);
+    GRP_PUT_GROUP_BBM (bi);
+    
+    put_gd (gi);
+    
+    if (!bk) {
+    
+        for (grp = 0; grp < nbgroups && !bk; grp++) {
+        
+            gd = get_gd (fs, grp, &gi);
+            
+            bk = allocate (GRP_GET_GROUP_BBM (fs, gd, &bi), 0);
+            GRP_PUT_GROUP_BBM (bi);
+            
+            put_gd (gi);
+        
+        }
+        
+        grp--;
+    
+    }
+    
+    if (!bk) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "couldn't allocate a block (no free space)");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    gd = get_gd (fs, grp, &gi);
+    
+    if (!(free_blocks_count = byte_array_to_integer (gd->bg_free_blocks_count, 2))) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "group descr %ld. free blocks count == 0 (corrupted fs?)", grp);
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    write_to_byte_array (gd->bg_free_blocks_count, free_blocks_count - 1, 2);
+    put_gd (gi);
+    
+    if (!(free_blocks_count = byte_array_to_integer (fs->sb->s_free_blocks_count, 4))) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "superblock free blocks count == 0 (corrupted fs?)");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    write_to_byte_array (fs->sb->s_free_blocks_count, free_blocks_count - 1, 4);
+    return byte_array_to_integer (fs->sb->s_first_data_block, 4) + byte_array_to_integer (fs->sb->s_blocks_per_group, 4) * grp + (bk - 1);
+
+}
+
+unsigned long walk_bw (struct filesystem *fs, unsigned long nod, struct blockwalker *bw, signed long *create, unsigned int hole) {
+
+    int extend = 0, reduce = 0;
+    
+    struct blkmap_info *bmi1 = 0, *bmi2 = 0, *bmi3 = 0;
+    struct inode *inod;
+    struct nod_info *ni;
+    
+#if     INT_MAX == 32767
+    unsigned long *bkref = 0, bk = 0, *iblk, *b;
+#else
+    unsigned int *bkref = 0, bk = 0, *iblk, *b;
+#endif
+    
+    if (create && (*create) < 0) {
+        reduce = 1;
+    }
+    
+    inod = get_nod (fs, nod, &ni);
+    
+    if (bw->bnum >= byte_array_to_integer (inod->i_blocks, 4) / INOBLK) {
+    
+        if (create && (*create) > 0) {
+        
+            (*create)--;
+            extend = 1;
+        
+        } else {
+        
+            put_nod (ni);
+            return WALK_END;
+        
+        }
+    
+    }
+    
+#if     INT_MAX == 32767
+    iblk = (unsigned long *) inod->i_block;
+#else
+    iblk = (unsigned int *) inod->i_block;
+#endif
+    
+    if (bw->bpdir == EXT2_INIT_BLOCK) {
+    
+        bkref = &iblk[bw->bpdir = 0];
+        
+        if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, *bkref); }
+    
+    } else if (bw->bpdir < EXT2_NDIR_BLOCKS) {
+    
+        bkref = &iblk[++bw->bpdir];
+        
+        if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, *bkref); }
+    
+    } else if (bw->bpdir == EXT2_NDIR_BLOCKS) {
+    
+        bw->bnum++;
+        
+        bw->bpdir = EXT2_IND_BLOCK;
+        bw->bpind = 0;
+        
+        if (extend) { iblk[bw->bpdir] = alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, iblk[bw->bpdir]); }
+        
+        b = get_blkmap (fs, iblk[bw->bpdir], &bmi1);
+        bkref = &b[bw->bpind];
+        
+        if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, *bkref); }
+    
+    } else if ((bw->bpdir == EXT2_IND_BLOCK) && (bw->bpind < BLOCKSIZE / 4 - 1)) {
+    
+        bw->bpind++;
+        
+        b = get_blkmap (fs, iblk[bw->bpdir], &bmi1);
+        bkref = &b[bw->bpind];
+        
+        if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, *bkref); }
+    
+    } else if (bw->bpdir == EXT2_IND_BLOCK) {
+    
+        bw->bpdir = EXT2_DIND_BLOCK;
+        bw->bnum += 2;
+        
+        bw->bpind = 0;
+        bw->bpdind = 0;
+        
+        if (extend) { iblk[bw->bpdir] = alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, iblk[bw->bpdir]); }
+        
+        b = get_blkmap (fs, iblk[bw->bpdir], &bmi1);
+        
+        if (extend) { b[bw->bpind] = alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, b[bw->bpind]); }
+        
+        b = get_blkmap (fs, b[bw->bpind], &bmi2);
+        bkref = &b[bw->bpdind];
+        
+        if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, *bkref); }
+    
+    } else if ((bw->bpdir == EXT2_DIND_BLOCK) && (bw->bpdind < BLOCKSIZE / 4 - 1)) {
+    
+        bw->bpdind++;
+        
+        b = get_blkmap (fs, iblk[bw->bpdir], &bmi1);
+        b = get_blkmap (fs, b[bw->bpind], &bmi2);
+        
+        bkref = &b[bw->bpdind];
+        
+        if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, *bkref); }
+    
+    } else if ((bw->bpdir == EXT2_DIND_BLOCK) && (bw->bpind < BLOCKSIZE / 4 - 1)) {
+    
+        bw->bnum++;
+        bw->bpind++;
+        
+        bw->bpdind = 0;
+        b = get_blkmap (fs, iblk[bw->bpdir], &bmi1);
+        
+        if (extend) { b[bw->bpind] = alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, b[bw->bpind]); }
+        
+        b = get_blkmap (fs, b[bw->bpind], &bmi2);
+        bkref = &b[bw->bpdind];
+        
+        if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, *bkref); }
+    
+    } else if (bw->bpdir == EXT2_DIND_BLOCK) {
+    
+        bw->bpdir = EXT2_TIND_BLOCK;
+        bw->bnum += 3;
+        
+        bw->bpind = 0;
+        bw->bpdind = 0;
+        bw->bptind = 0;
+        
+        if (extend) { iblk[bw->bpdir] = alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, iblk[bw->bpdir]); }
+        
+        b = get_blkmap (fs, iblk[bw->bpdir], &bmi1);
+        
+        if (extend) { b[bw->bpind] = alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, b[bw->bpind]); }
+        
+        b = get_blkmap (fs, b[bw->bpind], &bmi2);
+        
+        if (extend) { b[bw->bpdind] = alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, b[bw->bpind]); }
+        
+        b = get_blkmap (fs, b[bw->bpdind], &bmi3);
+        bkref = &b[bw->bptind];
+        
+        if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, *bkref); }
+    
+    } else if ((bw->bpdir == EXT2_TIND_BLOCK) && (bw->bptind < BLOCKSIZE / 4 - 1)) {
+    
+        bw->bptind++;
+        
+        b = get_blkmap (fs, iblk[bw->bpdir], &bmi1);
+        b = get_blkmap (fs, b[bw->bpind],  &bmi2);
+        b = get_blkmap (fs, b[bw->bpdind], &bmi3);
+        
+        bkref = &b[bw->bptind];
+        
+        if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, *bkref); }
+    
+    } else if ((bw->bpdir == EXT2_TIND_BLOCK) && (bw->bpdind < BLOCKSIZE / 4 - 1)) {
+    
+        bw->bnum++;
+        
+        bw->bptind = 0;
+        bw->bpdind++;
+        
+        b = get_blkmap (fs, iblk[bw->bpdir], &bmi1);
+        b = get_blkmap (fs, b[bw->bpind], &bmi2);
+        
+        if (extend) { b[bw->bpdind] = alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, b[bw->bpind]); }
+        
+        b = get_blkmap (fs, b[bw->bpdind], &bmi3);
+        bkref = &b[bw->bptind];
+        
+        if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, *bkref); }
+    
+    } else if ((bw->bpdir == EXT2_TIND_BLOCK) && (bw->bpind < BLOCKSIZE / 4 - 1)) {
+    
+        bw->bnum += 2;
+        bw->bpind++;
+        
+        bw->bpdind = 0;
+        bw->bptind = 0;
+        
+        b = get_blkmap (fs, iblk[bw->bpdir], &bmi1);
+        
+        if (extend) { b[bw->bpind] = alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, b[bw->bpind]); }
+        
+        b = get_blkmap (fs, b[bw->bpind], &bmi2);
+        
+        if (extend) { b[bw->bpdind] = alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, b[bw->bpind]); }
+        
+        b = get_blkmap (fs, b[bw->bpdind], &bmi3);
+        bkref = &b[bw->bptind];
+        
+        if (extend) { *bkref = hole ? 0 : alloc_blk (fs, nod); }
+        if (reduce) { free_blk (fs, *bkref); }
+    
+    } else {
+    
+        report_at (program_name, 0, REPORT_ERROR, "file too big");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    bk = *bkref;
+    
+    if (bmi3) { put_blkmap (bmi3); }
+    if (bmi2) { put_blkmap (bmi2); }
+    if (bmi1) { put_blkmap (bmi1); }
+    
+    if (bk) {
+    
+        unsigned char *block;
+        
+        struct blk_info *bi;
+        struct gd_info *gi;
+        
+        block = GRP_GET_BLOCK_BITMAP (fs, bk, &bi, &gi);
+        bw->bnum++;
+        
+        if (!reduce && !allocated (block, GRP_BBM_OFFSET (fs, bk))) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "[block %lu of inode %lu is unallocated !]", bk, nod);
+            exit (EXIT_FAILURE);
+        
+        }
+        
+        GRP_PUT_BLOCK_BITMAP (bi, gi);
+    
+    }
+    
+    if (extend) {
+        write_to_byte_array (inod->i_blocks, bw->bnum * INOBLK, 4);
+    }
+    
+    put_nod (ni);
+    return bk;
+
+}
+
+void inode_pos_init (struct filesystem *fs, struct inode_pos *ipos, unsigned long nod, int op, struct blockwalker *endbw) {
+
+    struct blockwalker lbw;
+    
+    init_bw (&ipos->bw);
+    ipos->nod = nod;
+    
+    ipos->inod = get_nod (fs, nod, &ipos->ni);
+    
+    if (op == INODE_POS_TRUNCATE) {
+    
+        long create = -1;
+        
+        while (walk_bw (fs, nod, &ipos->bw, &create, 0) != WALK_END) {
+            /* nop */
+        }
+        
+        write_to_byte_array (ipos->inod->i_blocks, 0, 4);
+    
+    }
+    
+    if (endbw) {
+        ipos->bw = *endbw;
+    } else {
+    
+        init_bw (&ipos->bw);
+        lbw = ipos->bw;
+        
+        while (walk_bw (fs, nod, &ipos->bw, 0, 0) != WALK_END) {
+            lbw = ipos->bw;
+        }
+        
+        ipos->bw = lbw;
+    
+    }
+
+}
+
+void inode_pos_finish (struct inode_pos *ipos) {
+    put_nod (ipos->ni);
+}
+
+
+unsigned char *get_blk (struct filesystem *fs, unsigned long blk, struct blk_info **rbi) {
+
+    struct cache_link *curr;
+    struct blk_info *bi;
+    
+    if (blk >= byte_array_to_integer (fs->sb->s_blocks_count, 4)) {
+    
+        report_at (program_name, 0, REPORT_INTERNAL_ERROR, "block out of range: %lu, %lu",
+            blk, byte_array_to_integer (fs->sb->s_blocks_count, 4));
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    if ((curr = cache_find (&fs->blks, blk))) {
+    
+        bi = container_of (curr, struct blk_info, link);
+        bi->usecount++;
+        
+        goto out;
+    
+    }
+    
+    bi = xmalloc (sizeof (*bi));
+    
+    bi->fs = fs;
+    bi->blk = blk;
+    bi->usecount = 1;
+    
+    bi->b = xmalloc (BLOCKSIZE);
+    cache_add (&fs->blks, &bi->link);
+    
+    if (seekto (ofp, blk)) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "fseek");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    if (fread (bi->b, BLOCKSIZE, 1, ofp) != 1) {
+    
+        if (ferror (ofp)) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "fread");
+            exit (EXIT_FAILURE);
+        
+        }
+        
+        memset (bi->b, 0, BLOCKSIZE);
+    
+    }
+    
+out:
+    
+    *rbi = bi;
+    return bi->b;
+
+}
+
+void put_blk (struct blk_info *bi) {
+
+    if (bi->usecount == 0) {
+    
+        report_at (program_name, 0, REPORT_INTERNAL_ERROR, "put_blk: usecount zero");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    bi->usecount--;
+    
+    if (bi->usecount == 0) {
+    
+        /* Free happens in the cache code. */
+        cache_item_set_unused (&bi->fs->blks, &bi->link);
+    
+    }
+
+}
+
+unsigned long allocate (unsigned char *b, unsigned long item) {
+
+    if (!item) {
+    
+        unsigned char bits;
+        unsigned long i, j;
+        
+        for (i = 0; i < BLOCKSIZE; i++) {
+        
+            if ((bits = b[i]) != (unsigned char) -1) {
+            
+                for (j = 0; j < 8; j++) {
+                
+                    if (!(bits & (1 << j))) {
+                        break;
+                    }
+                
+                }
+                
+                item = i * 8 + j + 1;
+                break;
+            
+            }
+        
+        }
+        
+        if (i == BLOCKSIZE) {
+            return 0;
+        }
+    
+    }
+    
+    b[(item - 1) / 8] |= (1 << ((item - 1) % 8));
+    return item;
+
+}
+
+char *dir_name (struct dirwalker *dw) {
+    return ((char *) dw->last_d) + sizeof (struct directory);
+}
+
+void put_dir (struct dirwalker *dw) {
+
+    if (dw->need_flush) {
+    
+        /* Walk ended before reaching the end of block b. */
+        memcpy (dw->last_d, &dw->d, sizeof (dw->d));
+    
+    }
+    
+    if (dw->nod == 0) {
+        free (dw->b);
+    } else {
+        put_blk (dw->bi);
+    }
+
+}
+
+struct directory *get_dir (struct filesystem *fs, unsigned long nod, struct dirwalker *dw) {
+
+    dw->fs = fs;
+    dw->nod = nod;
+    
+    dw->last_d = dw->b = get_blk (fs, nod, &dw->bi);
+    dw->need_flush = 1;
+    
+    memcpy (&dw->d, dw->last_d, sizeof (struct directory));
+    return &dw->d;
+
+}
+
+struct directory *next_dir (struct dirwalker *dw) {
+
+    unsigned char *next_d = dw->last_d + byte_array_to_integer (dw->d.d_rec_len, 2);
+    
+    if (dw->need_flush) {
+        memcpy (dw->last_d, &dw->d, sizeof (struct directory));
+    }
+    
+    if (next_d - dw->b >= BLOCKSIZE) {
+    
+        dw->need_flush = 0;
+        return 0;
+    
+    }
+    
+    dw->last_d = next_d;
+    memcpy (&dw->d, next_d, sizeof (struct directory));
+    
+    return &dw->d;
+
+}
+
+void init_bw (struct blockwalker *bw) {
+
+    bw->bnum = 0;
+    bw->bpdir = EXT2_INIT_BLOCK;
+
+}
+
+#define     EXT2_GOOD_OLD_FIRST_INO                         EXT2_FIRST_INO
+#define     EXT2_GOOD_OLD_INODE_SIZE                        sizeof (struct inode)
+
+void fs_upgrade_rev1_largefile (struct filesystem *fs) {
+
+    write_to_byte_array (fs->sb->s_rev_level, 1, 4);
+    
+    write_to_byte_array (fs->sb->s_first_ino, EXT2_GOOD_OLD_FIRST_INO, 4);
+    write_to_byte_array (fs->sb->s_inode_size, EXT2_GOOD_OLD_INODE_SIZE, 2);
+
+}
+
+unsigned char *get_workblk (void) {
+    return xmalloc (BLOCKSIZE);
+}
+
+struct directory *new_dir (struct filesystem *fs, unsigned long dnod, const char *name, unsigned short nlen, struct dirwalker *dw) {
+
+    struct directory *d;
+    
+    dw->fs = fs;
+    dw->nod = 0;
+    
+    dw->b = get_workblk ();
+    dw->last_d = dw->b;
+    
+    dw->need_flush = 1;
+    d = &dw->d;
+    
+    write_to_byte_array (d->d_inode, dnod, 4);
+    write_to_byte_array (d->d_rec_len, BLOCKSIZE, 2);
+    write_to_byte_array (d->d_name_len, nlen, 2);
+    
+    strncpy (((char *) dw->last_d) + sizeof (struct directory), name, nlen);
+    return d;
+
+}
+
+struct directory *shrink_dir (struct dirwalker *dw, unsigned long nod, const char *name, unsigned short nlen) {
+
+    struct directory *d = &dw->d;
+    unsigned long reclen, preclen;
+    
+    reclen = byte_array_to_integer (d->d_rec_len, 2);
+    write_to_byte_array (d->d_rec_len, sizeof (*d) + rndup (byte_array_to_integer (d->d_name_len, 2), 4), 2);
+    
+    preclen = byte_array_to_integer (d->d_rec_len, 2);
+    reclen -= preclen;
+    
+    memcpy (dw->last_d, &dw->d, sizeof (dw->d));
+    
+    dw->last_d = dw->last_d + preclen;
+    write_to_byte_array (d->d_rec_len, reclen, 2);
+    
+    write_to_byte_array (d->d_inode, nod, 4);
+    write_to_byte_array (d->d_name_len, nlen, 2);
+    
+    strncpy (((char *) dw->last_d) + sizeof (*d), name, nlen);
+    return d;
+
+}
+
+void extend_inode_blk (struct filesystem *fs, struct inode_pos *ipos, unsigned char *b, signed long amount) {
+
+    unsigned long pos, bk;
+    
+    struct blk_info *bi;
+    unsigned char *block;
+    
+    if (amount < 0) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "got negative amount");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    for (pos = 0; amount; pos += BLOCKSIZE) {
+    
+        if ((bk = walk_bw (fs, ipos->nod, &ipos->bw, &amount, 0)) == WALK_END) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "extend_inode_blk: extend failed");
+            exit (EXIT_FAILURE);
+        
+        }
+        
+        block = get_blk (fs, bk, &bi);
+        memcpy (block, b + pos, BLOCKSIZE);
+        
+        put_blk (bi);
+    
+    }
+
+}
diff --git a/common.h b/common.h
new file mode 100644 (file)
index 0000000..e07fbca
--- /dev/null
+++ b/common.h
@@ -0,0 +1,227 @@
+/******************************************************************************
+ * @file            common.h
+ *****************************************************************************/
+#ifndef     _COMMON_H
+#define     _COMMON_H
+
+#include    <stdio.h>
+
+#include    "cache.h"
+#include    "inode.h"
+#include    "super.h"
+
+extern unsigned long offset;
+
+extern char *outfile;
+extern char *program_name;
+
+extern FILE *ofp;
+
+int seekto (FILE *fp, unsigned long sector);
+int mode_con (char *p);
+
+char *xstrdup (const char *str);
+void *xmalloc (unsigned long size);
+void *xrealloc (void *ptr, unsigned long size);
+
+unsigned long byte_array_to_integer (unsigned char *arr, int size);
+void write_to_byte_array (unsigned char *arr, unsigned long value, int size);
+
+#define     SECTOR_SIZE                 512
+#define     BLOCKSIZE                   1024
+
+struct blockwalker {
+
+    unsigned long bnum;
+    unsigned long bpdir;
+    unsigned long bpind;
+    unsigned long bpdind;
+    unsigned long bptind;
+
+};
+
+struct inode_pos {
+
+    struct blockwalker bw;
+    unsigned long nod;
+    
+    struct nod_info *ni;
+    struct inode *inod;
+
+};
+
+#define     EXT2_FIRST_INO              11
+#define     EXT2_ROOT_INO               2
+
+struct hdlink_s {
+
+    unsigned long src_index;
+    unsigned long dst_nod;
+
+};
+
+struct hdlinks_s {
+
+    unsigned long count;
+    struct hdlink_s *hdl;
+
+};
+
+struct filesystem {
+
+    struct superblock *sb;
+    int swapit;
+    
+    unsigned long hdlink_cnt;
+    struct hdlinks_s hdlinks;
+    
+    unsigned long holes;
+    
+    struct listcache blks;
+    struct listcache gds;
+    struct listcache indoes;
+    struct listcache blkmaps;
+
+};
+
+#define     MIN(a, b)                   ((a) > (b) ? (b) : (a))
+unsigned long rndup (unsigned long qty, unsigned long siz);
+
+#define     FM_IFMT                     0170000
+
+#define     FM_IFREG                    0100000
+#define     FM_IFDIR                    0040000
+#define     FM_IRWXU                    0000700
+#define     FM_IRGRP                    0000040
+#define     FM_IXGRP                    0000010
+#define     FM_IROTH                    0000004
+#define     FM_IXOTH                    0000001
+
+struct group_descriptor {
+
+    unsigned char bg_block_bitmap[4]; 
+    unsigned char bg_inode_bitmap[4];
+    unsigned char bg_inode_table[4];
+    
+    unsigned char bg_free_blocks_count[2];
+    unsigned char bg_free_inodes_count[2];
+    unsigned char bg_used_dirs_count[2];
+    
+    unsigned char bg_pad[2];
+    unsigned char bg_reserved2[12];
+
+};
+
+struct blk_info {
+
+    struct cache_link link;
+    
+    struct filesystem *fs;
+    unsigned long blk;
+    
+    unsigned char *b;
+    unsigned long usecount;
+
+};
+
+struct gd_info {
+
+    struct cache_link link;
+    
+    struct filesystem *fs;
+    int gds;
+    
+    struct blk_info *bi;
+    struct group_descriptor *gd;
+    
+    unsigned long usecount;
+
+};
+
+struct nod_info {
+
+    struct cache_link link;
+    
+    struct filesystem *fs;
+    unsigned long nod;
+    
+    unsigned char *b;
+    struct blk_info *bi;
+    
+    struct inode *itab;
+    unsigned long usecount;
+
+};
+
+struct filesystem *alloc_fs (void);
+unsigned long mknod_fs (struct filesystem *fs, unsigned long parent_nod, const char *name, unsigned long mode, unsigned short uid, unsigned short gid, unsigned long ctime, unsigned long mtime);
+
+struct group_descriptor *get_gd (struct filesystem *fs, unsigned long no, struct gd_info **rgi);
+void put_gd (struct gd_info *gi);
+
+unsigned char *get_blk (struct filesystem *fs, unsigned long blk, struct blk_info **rbi);
+void put_blk (struct blk_info *bi);
+
+#define     GRP_GET_GROUP_BBM(fs, grp, bi)                  (get_blk ((fs), byte_array_to_integer ((grp)->bg_block_bitmap, 4), (bi)))
+#define     GRP_PUT_GROUP_BBM(bi)                           (put_blk ((bi)))
+
+#define     GRP_GET_GROUP_IBM(fs, grp, bi)                  (get_blk ((fs), byte_array_to_integer ((grp)->bg_inode_bitmap, 4), (bi)))
+#define     GRP_PUT_GROUP_IBM(bi)                           (put_blk ((bi)))
+
+unsigned long allocate (unsigned char *b, unsigned long item);
+
+struct directory {
+
+    unsigned char d_inode[4];
+    
+    unsigned char d_rec_len[2];
+    unsigned char d_name_len[2];
+
+};
+
+struct dirwalker {
+
+    struct directory d;
+    struct filesystem *fs;
+    
+    unsigned char *last_d, *b;
+    unsigned long nod;
+    
+    struct blk_info *bi;
+    int need_flush;
+
+};
+
+#define     COPY_BLOCKS                 16
+
+unsigned long mkdir_fs (struct filesystem *fs, unsigned long parent_nod, const char *name, unsigned long mode, unsigned short uid, unsigned short gid, unsigned long ctime, unsigned long mtime);
+unsigned long walk_bw (struct filesystem *fs, unsigned long nod, struct blockwalker *bw, signed long *create, unsigned int hole);
+
+#define     INODE_POS_TRUNCATE          0
+#define     INODE_POS_EXTEND            1
+
+#define     WALK_END                    0xFFFFFFFE
+
+struct directory *next_dir (struct dirwalker *dw);
+void fs_upgrade_rev1_largefile (struct filesystem *fs);
+
+void inode_pos_init (struct filesystem *fs, struct inode_pos *ipos, unsigned long nod, int op, struct blockwalker *endbw);
+void inode_pos_finish (struct inode_pos *ipos);
+
+struct inode *get_nod (struct filesystem *fs, unsigned long nod, struct nod_info **rni);
+void put_nod (struct nod_info *ni);
+
+unsigned char *get_workblk (void);
+void put_dir (struct dirwalker *dw);
+
+struct directory *get_dir (struct filesystem *fs, unsigned long nod, struct dirwalker *dw);
+struct directory *new_dir (struct filesystem *fs, unsigned long dnod, const char *name, unsigned short nlen, struct dirwalker *dw);
+struct directory *shrink_dir (struct dirwalker *dw, unsigned long nod, const char *name, unsigned short nlen);
+
+char *dir_name (struct dirwalker *dw);
+void init_bw (struct blockwalker *bw);
+
+void extend_inode_blk (struct filesystem *fs, struct inode_pos *ipos, unsigned char *b, signed long amount);
+void finish_fs (struct filesystem *fs);
+
+#endif      /* _COMMON_H */
diff --git a/e2cp.c b/e2cp.c
new file mode 100644 (file)
index 0000000..635aadc
--- /dev/null
+++ b/e2cp.c
@@ -0,0 +1,580 @@
+/******************************************************************************
+ * @file            e2cp.c
+ *****************************************************************************/
+#include    <ctype.h>
+#include    <errno.h>
+#include    <limits.h>
+#include    <stdio.h>
+#include    <stdlib.h>
+#include    <string.h>
+#include    <time.h>
+
+#include    "common.h"
+#include    "inode.h"
+#include    "report.h"
+#include    "super.h"
+
+static char *mode = 0;
+static char *root = 0;
+
+#define     OPTION_INPUT                0x0001
+#define     OPTION_HELP                 0x0002
+#define     OPTION_MODE                 0x0003
+#define     OPTION_OFFSET               0x0004
+#define     OPTION_ROOT                 0x0005
+
+struct option {
+
+    const char *name;
+    int index, flags;
+
+};
+
+#define     OPTION_NO_ARG               0x0001
+#define     OPTION_HAS_ARG              0x0002
+
+static struct option opts[] = {
+
+    {   "-i",               OPTION_INPUT,           OPTION_HAS_ARG      },
+    
+    {   "--help",           OPTION_HELP,            OPTION_NO_ARG       },
+    {   "--mode",           OPTION_MODE,            OPTION_HAS_ARG      },
+    {   "--offset",         OPTION_OFFSET,          OPTION_HAS_ARG      },
+    {   "--root",           OPTION_ROOT,            OPTION_HAS_ARG      },
+    
+    {   0,                  0,                      0                   }
+
+};
+
+static char **files = 0;
+static long nb_files = 0;
+
+static int strstart (const char *val, const char **str) {
+
+    const char *p = val;
+    const char *q = *str;
+    
+    while (*p != '\0') {
+    
+        if (*p != *q) {
+            return 0;
+        }
+        
+        ++p;
+        ++q;
+    
+    }
+    
+    *str = q;
+    return 1;
+
+}
+
+static void print_help (void) {
+
+    if (program_name) {
+    
+        fprintf (stderr, "Usage: %s [options] dirname\n\n", program_name);
+        fprintf (stderr, "Options:\n\n");
+        
+        fprintf (stderr, "    Short options:\n\n");
+        fprintf (stderr, "        -i                Specify the input target.\n");
+        
+        fprintf (stderr, "\n");
+        
+        fprintf (stderr, "    Long options:\n\n");
+        fprintf (stderr, "        --help            Show this help information then exit.\n");
+        fprintf (stderr, "        --offset SECTOR   Write the filesystem starting at SECTOR.\n");
+    
+    }
+    
+    exit (EXIT_SUCCESS);
+
+}
+
+static void dynarray_add (void *ptab, long *nb_ptr, void *data) {
+
+    int nb, nb_alloc;
+    void **pp;
+    
+    nb = *nb_ptr;
+    pp = *(void ***) ptab;
+    
+    if ((nb & (nb - 1)) == 0) {
+    
+        if (!nb) {
+            nb_alloc = 1;
+        } else {
+            nb_alloc = nb * 2;
+        }
+        
+        pp = xrealloc (pp, nb_alloc * sizeof (void *));
+        *(void ***) ptab = pp;
+    
+    }
+    
+    pp[nb++] = data;
+    *nb_ptr = nb;
+
+}
+
+static void parse_args (int argc, char **argv, int optind) {
+
+    struct option *popt;
+    const char *optarg, *r;
+    
+    if (argc <= optind) {
+        print_help ();
+    }
+    
+    while (optind < argc) {
+    
+        r = argv[optind++];
+        
+        if (r[0] != '-' || r[1] == '\0') {
+        
+            dynarray_add (&files, &nb_files, xstrdup (r));
+            continue;
+        
+        }
+        
+        for (popt = opts; popt; popt++) {
+        
+            const char *p1 = popt->name;
+            const char *r1 = r;
+            
+            if (!p1) {
+            
+                report_at (program_name, 0, REPORT_ERROR, "invalid option -- '%s'", r);
+                exit (EXIT_FAILURE);
+            
+            }
+            
+            if (!strstart (p1, &r1)) {
+                continue;
+            }
+            
+            optarg = r1;
+            
+            if (popt->flags & OPTION_HAS_ARG) {
+            
+                if (*optarg == '\0') {
+                
+                    if (optind >= argc) {
+                    
+                        report_at (program_name, 0, REPORT_ERROR, "argument to '%s' is missing", r);
+                        exit (EXIT_FAILURE);
+                    
+                    }
+                    
+                    optarg = argv[optind++];
+                
+                }
+            
+            } else if (*optarg != '\0') {
+                continue;
+            }
+            
+            break;
+        
+        }
+        
+        switch (popt->index) {
+        
+            case OPTION_HELP: {
+            
+                print_help ();
+                break;
+            
+            }
+            
+            case OPTION_INPUT: {
+            
+                if (outfile) {
+                
+                    report_at (program_name, 0, REPORT_ERROR, "multiple output files provided");
+                    exit (EXIT_FAILURE);
+                
+                }
+                
+                outfile = xstrdup (optarg);
+                break;
+            
+            }
+            
+            case OPTION_MODE: {
+            
+                if (strlen (optarg) != 3) {
+                
+                    report_at (program_name, 0, REPORT_ERROR, "invalid mode provided");
+                    exit (EXIT_FAILURE);
+                
+                }
+                
+                memcpy (mode + 3, optarg, 3);
+                break;
+            
+            }
+            
+            case OPTION_OFFSET: {
+            
+                long conversion;
+                char *temp;
+                
+                errno = 0;
+                conversion = strtol (optarg, &temp, 0);
+                
+                if (!*optarg || isspace ((int) *optarg) || errno || *temp) {
+                
+                    report_at (program_name, 0, REPORT_ERROR, "bad number for offset (%s)", optarg);
+                    exit (EXIT_FAILURE);
+                
+                }
+                
+                if (conversion < 0 || conversion > INT_MAX) {
+                
+                    report_at (program_name, 0, REPORT_ERROR, "offset must be between 0 and %u", INT_MAX);
+                    exit (EXIT_FAILURE);
+                
+                }
+                
+                offset = conversion;
+                break;
+            
+            }
+            
+            case OPTION_ROOT: {
+            
+                if (root) {
+                    free (root);
+                }
+                
+                root = xstrdup (optarg);
+                break;
+            
+            }
+            
+            default: {
+            
+                report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", r);
+                exit (EXIT_FAILURE);
+            
+            }
+        
+        }
+    
+    }
+
+}
+
+static void cleanup (void) {
+
+    if (ofp != NULL) {
+        fclose (ofp);
+    }
+
+}
+
+static unsigned long find_entry (struct filesystem *fs, unsigned long nod, const char *name) {
+
+    struct blockwalker bw;
+    unsigned long nlen, bk;
+    
+    nlen = strlen (name);
+    init_bw (&bw);
+    
+    while ((bk = walk_bw (fs, nod, &bw, 0, 0)) != WALK_END) {
+    
+        struct directory *d;
+        struct dirwalker dw;
+        
+        for (d = get_dir (fs, bk, &dw); d; d = next_dir (&dw)) {
+        
+            if (byte_array_to_integer (d->d_inode, 4) && (nlen == byte_array_to_integer (d->d_name_len, 2)) && !strncmp (dir_name (&dw), name, nlen)) {
+            
+                unsigned long result = byte_array_to_integer (d->d_inode, 4);
+                put_dir (&dw);
+                
+                return result;
+            
+            }
+        
+        }
+        
+        put_dir (&dw);
+    
+    }
+    
+    return 0;
+
+}
+
+#define     CB_SIZE                     (COPY_BLOCKS * BLOCKSIZE)
+
+signed long read_cb (struct filesystem *fs, struct inode_pos *ipos, signed long size, void *data) {
+
+    signed long remaining_size = size;
+    
+    unsigned long readbytes;
+    int fullsize;
+    
+    unsigned char *b = xmalloc (CB_SIZE);
+    
+    while ((readbytes = fread (b, 1, MIN (remaining_size, CB_SIZE), (FILE *) data))) {
+    
+        remaining_size -= readbytes;
+        
+        fullsize = rndup (readbytes, BLOCKSIZE);
+        memset (b + readbytes, 0, fullsize - readbytes);
+        
+        extend_inode_blk (fs, ipos, b, fullsize / BLOCKSIZE);
+    
+    }
+    
+    free (b);
+    return size;
+
+}
+
+#define     EXT2_FEATURE_RO_COMPAT_LARGE_FILE               0x0002
+
+unsigned long mkfile_fs (struct filesystem *fs, unsigned long parent_nod, const char *name, void *data, unsigned long size, unsigned short uid, unsigned short gid, unsigned long ctime, unsigned long mtime) {
+
+    unsigned long nod;
+    
+    struct nod_info *ni;
+    struct inode *node;
+    
+    signed long actual_size;
+    struct inode_pos ipos;
+    
+    nod = mknod_fs (fs, parent_nod, name, mode_con (mode), uid, gid, ctime, mtime);
+    node = get_nod (fs, nod, &ni);
+    
+    inode_pos_init (fs, &ipos, nod, INODE_POS_TRUNCATE, 0);
+    
+    if ((actual_size = read_cb (fs, &ipos, size, data)) > 0x7FFFFFFF) {
+    
+        unsigned long feature_ro_compat = byte_array_to_integer (fs->sb->s_feature_ro_compat, 4);
+        
+        if (byte_array_to_integer (fs->sb->s_rev_level, 4) < 1) {
+            fs_upgrade_rev1_largefile (fs);
+        }
+        
+        write_to_byte_array (fs->sb->s_feature_ro_compat, feature_ro_compat | EXT2_FEATURE_RO_COMPAT_LARGE_FILE, 4);
+    
+    }
+    
+    write_to_byte_array (node->i_dir_acl, actual_size >> 32, 4);
+    write_to_byte_array (node->i_size, actual_size, 4);
+    
+    inode_pos_finish (&ipos);
+    
+    put_nod (ni);
+    return nod;
+
+}
+
+static int walk_path (struct filesystem *fs, char *dirname) {
+
+    unsigned long nod = EXT2_ROOT_INO;
+    char *p1 = dirname, *p2;
+    
+    while (*p1 && *p1 == '/') {
+        p1++;
+    }
+    
+    if (*p1) {
+    
+        for (; (p2 = strchr (p1, '/')); p1 = p2 + 1) {
+        
+            *p2 = '\0';
+            
+            if (strlen (p1) >= 14) {
+                p1[13] = '\0';
+            }
+            
+            if (!(nod = find_entry (fs, nod, p1))) {
+            
+                report_at (program_name, 0, REPORT_ERROR, "cannot access '%s'", dirname);
+                
+                *p2 = '/';
+                return 0;
+            
+            }
+            
+            *p2 = '/';
+        
+        }
+        
+        if (!(nod = find_entry (fs, nod, p1))) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "cannot access '%s'", dirname);
+            return 0;
+        
+        }
+    
+    }
+    
+    return nod;
+
+}
+
+static void copy_file (struct filesystem *fs, FILE *fp, unsigned long parent, char *fn) {
+
+    unsigned long bytes = 0, timestamp = time (0);
+    char *basename = fn, *p;
+    
+    if ((p = strrchr (fn, '/'))) {
+        basename = (p + 1);
+    }
+    
+    fseek (fp, 0, SEEK_END);
+    
+    bytes = ftell (fp);
+    fseek (fp, 0, SEEK_SET);
+    
+    mkfile_fs (fs, parent, basename, fp, bytes, 0, 0, timestamp, timestamp);
+
+}
+
+int main (int argc, char **argv) {
+
+    struct superblock sup = { 0 };
+    
+    unsigned long parent_ino = EXT2_ROOT_INO;
+    struct filesystem *fs;
+    
+    FILE *fp = NULL;
+    long i;
+    
+    if (argc && *argv) {
+    
+        char *p;
+        program_name = *argv;
+        
+        if ((p = strrchr (program_name, '/')) || (p = strrchr (program_name, '\\'))) {
+            program_name = (p + 1);
+        }
+    
+    }
+    
+    atexit (cleanup);
+    
+    mode = xstrdup ("---666");
+    parse_args (argc, argv, 1);
+    
+    if (!outfile) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "no input file was provided");
+        return EXIT_FAILURE;
+    
+    }
+    
+    if ((ofp = fopen (outfile, "r+b")) == NULL) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "faild to open '%s' for writing", outfile);
+        return EXIT_FAILURE;
+    
+    }
+    
+    seekto (ofp, EXT2_SUPERBLOCK_OFFSET / BLOCKSIZE);
+    
+    if (fread (&sup, sizeof (sup), 1, ofp) != 1) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "failed to whilst reading '%s'", outfile);
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    if (sup.s_magic[0] != (EXT2_SUPER_MAGIC & 0xff) || sup.s_magic[1] != ((EXT2_SUPER_MAGIC >> 8) & 0xff)) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "'%s' has a unsupported file system", outfile);
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    fs = alloc_fs ();
+    fs->swapit = 0;
+    
+    fs->sb = &sup;
+    
+    if (nb_files >= 2) {
+    
+        char *path = files[nb_files - 1];
+        char *p;
+        
+        if (path[0] == ':' && path[1] == ':') {
+        
+            path += 2;
+            
+            if (nb_files > 2) {
+            
+                report_at (program_name, 0, REPORT_ERROR, "cannot copy multiple files to %s", path);
+                return EXIT_FAILURE;
+            
+            }
+            
+            if (root) {
+            
+                report_at (program_name, 0, REPORT_WARNING, "root '%s' is ignored, using output path instead", root);
+                free (root);
+                
+                root = 0;
+            
+            }
+            
+            if ((p = strrchr (path, '/'))) {
+            
+                *p = '\0';
+                
+                if (!(parent_ino = walk_path (fs, path))) {
+                    return EXIT_FAILURE;
+                }
+                
+                path = (p + 1);
+            
+            }
+            
+            if ((fp = fopen (files[0], "rb")) == NULL) {
+            
+                report_at (program_name, 0, REPORT_ERROR, "failed to open '%s' for reading", files[0]);
+                return EXIT_FAILURE;
+            
+            }
+            
+            copy_file (fs, fp, parent_ino, path);
+            
+            fclose (fp);
+            return (get_error_count () > 0) ? EXIT_FAILURE : EXIT_SUCCESS;
+        
+        }
+    
+    }
+    
+    if (root) {
+    
+        if (!(parent_ino = walk_path (fs, root))) {
+            return EXIT_FAILURE;
+        }
+    
+    }
+    
+    for (i = 0; i < nb_files; i++) {
+    
+        if ((fp = fopen (files[i], "rb")) == NULL) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "failed to open '%s' for reading", files[i]);
+            continue;
+        
+        }
+        
+        copy_file (fs, fp, parent_ino, files[i]);
+        fclose (fp);
+    
+    }
+    
+    finish_fs (fs);
+    return (get_error_count () > 0) ? EXIT_FAILURE : EXIT_SUCCESS;
+
+}
diff --git a/e2md.c b/e2md.c
new file mode 100644 (file)
index 0000000..c2347b1
--- /dev/null
+++ b/e2md.c
@@ -0,0 +1,410 @@
+/******************************************************************************
+ * @file            e2md.c
+ *****************************************************************************/
+#include    <ctype.h>
+#include    <errno.h>
+#include    <limits.h>
+#include    <stdio.h>
+#include    <stdlib.h>
+#include    <string.h>
+#include    <time.h>
+
+#include    "common.h"
+#include    "inode.h"
+#include    "report.h"
+#include    "super.h"
+
+static char *mode = 0;
+
+#define     OPTION_INPUT                0x0001
+#define     OPTION_HELP                 0x0002
+#define     OPTION_MODE                 0x0003
+#define     OPTION_OFFSET               0x0004
+
+struct option {
+
+    const char *name;
+    int index, flags;
+
+};
+
+#define     OPTION_NO_ARG               0x0001
+#define     OPTION_HAS_ARG              0x0002
+
+static struct option opts[] = {
+
+    {   "-i",               OPTION_INPUT,           OPTION_HAS_ARG      },
+    
+    {   "--help",           OPTION_HELP,            OPTION_NO_ARG       },
+    {   "--mode",           OPTION_MODE,            OPTION_HAS_ARG      },
+    {   "--offset",         OPTION_OFFSET,          OPTION_HAS_ARG      },
+    
+    {   0,                  0,                      0                   }
+
+};
+
+static char **dirs = 0;
+static long nb_dirs = 0;
+
+static int strstart (const char *val, const char **str) {
+
+    const char *p = val;
+    const char *q = *str;
+    
+    while (*p != '\0') {
+    
+        if (*p != *q) {
+            return 0;
+        }
+        
+        ++p;
+        ++q;
+    
+    }
+    
+    *str = q;
+    return 1;
+
+}
+
+static void print_help (void) {
+
+    if (program_name) {
+    
+        fprintf (stderr, "Usage: %s [options] dirname\n\n", program_name);
+        fprintf (stderr, "Options:\n\n");
+        
+        fprintf (stderr, "    Short options:\n\n");
+        fprintf (stderr, "        -i                Specify the input target.\n");
+        
+        fprintf (stderr, "\n");
+        
+        fprintf (stderr, "    Long options:\n\n");
+        fprintf (stderr, "        --help            Show this help information then exit.\n");
+        fprintf (stderr, "        --offset SECTOR   Write the filesystem starting at SECTOR.\n");
+    
+    }
+    
+    exit (EXIT_SUCCESS);
+
+}
+
+static void dynarray_add (void *ptab, long *nb_ptr, void *data) {
+
+    int nb, nb_alloc;
+    void **pp;
+    
+    nb = *nb_ptr;
+    pp = *(void ***) ptab;
+    
+    if ((nb & (nb - 1)) == 0) {
+    
+        if (!nb) {
+            nb_alloc = 1;
+        } else {
+            nb_alloc = nb * 2;
+        }
+        
+        pp = xrealloc (pp, nb_alloc * sizeof (void *));
+        *(void ***) ptab = pp;
+    
+    }
+    
+    pp[nb++] = data;
+    *nb_ptr = nb;
+
+}
+
+static void parse_args (int argc, char **argv, int optind) {
+
+    struct option *popt;
+    const char *optarg, *r;
+    
+    if (argc <= optind) {
+        print_help ();
+    }
+    
+    while (optind < argc) {
+    
+        r = argv[optind++];
+        
+        if (r[0] != '-' || r[1] == '\0') {
+        
+            dynarray_add (&dirs, &nb_dirs, xstrdup (r));
+            continue;
+        
+        }
+        
+        for (popt = opts; popt; popt++) {
+        
+            const char *p1 = popt->name;
+            const char *r1 = r;
+            
+            if (!p1) {
+            
+                report_at (program_name, 0, REPORT_ERROR, "invalid option -- '%s'", r);
+                exit (EXIT_FAILURE);
+            
+            }
+            
+            if (!strstart (p1, &r1)) {
+                continue;
+            }
+            
+            optarg = r1;
+            
+            if (popt->flags & OPTION_HAS_ARG) {
+            
+                if (*optarg == '\0') {
+                
+                    if (optind >= argc) {
+                    
+                        report_at (program_name, 0, REPORT_ERROR, "argument to '%s' is missing", r);
+                        exit (EXIT_FAILURE);
+                    
+                    }
+                    
+                    optarg = argv[optind++];
+                
+                }
+            
+            } else if (*optarg != '\0') {
+                continue;
+            }
+            
+            break;
+        
+        }
+        
+        switch (popt->index) {
+        
+            case OPTION_HELP: {
+            
+                print_help ();
+                break;
+            
+            }
+            
+            case OPTION_INPUT: {
+            
+                if (outfile) {
+                
+                    report_at (program_name, 0, REPORT_ERROR, "multiple output files provided");
+                    exit (EXIT_FAILURE);
+                
+                }
+                
+                outfile = xstrdup (optarg);
+                break;
+            
+            }
+            
+            case OPTION_MODE: {
+            
+                if (strlen (optarg) != 3) {
+                
+                    report_at (program_name, 0, REPORT_ERROR, "invalid mode provided");
+                    exit (EXIT_FAILURE);
+                
+                }
+                
+                memcpy (mode + 3, optarg, 3);
+                break;
+            
+            }
+            
+            case OPTION_OFFSET: {
+            
+                long conversion;
+                char *temp;
+                
+                errno = 0;
+                conversion = strtol (optarg, &temp, 0);
+                
+                if (!*optarg || isspace ((int) *optarg) || errno || *temp) {
+                
+                    report_at (program_name, 0, REPORT_ERROR, "bad number for offset (%s)", optarg);
+                    exit (EXIT_FAILURE);
+                
+                }
+                
+                if (conversion < 0 || conversion > INT_MAX) {
+                
+                    report_at (program_name, 0, REPORT_ERROR, "offset must be between 0 and %u", INT_MAX);
+                    exit (EXIT_FAILURE);
+                
+                }
+                
+                offset = conversion;
+                break;
+            
+            }
+            
+            default: {
+            
+                report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", r);
+                exit (EXIT_FAILURE);
+            
+            }
+        
+        }
+    
+    }
+
+}
+
+static void cleanup (void) {
+
+    if (ofp != NULL) {
+        fclose (ofp);
+    }
+
+}
+
+static unsigned long find_entry (struct filesystem *fs, unsigned long nod, const char *name) {
+
+    struct blockwalker bw;
+    unsigned long nlen, bk;
+    
+    nlen = strlen (name);
+    init_bw (&bw);
+    
+    while ((bk = walk_bw (fs, nod, &bw, 0, 0)) != WALK_END) {
+    
+        struct directory *d;
+        struct dirwalker dw;
+        
+        for (d = get_dir (fs, bk, &dw); d; d = next_dir (&dw)) {
+        
+            if (byte_array_to_integer (d->d_inode, 4) && (nlen == byte_array_to_integer (d->d_name_len, 2)) && !strncmp (dir_name (&dw), name, nlen)) {
+            
+                unsigned long result = byte_array_to_integer (d->d_inode, 4);
+                put_dir (&dw);
+                
+                return result;
+            
+            }
+        
+        }
+        
+        put_dir (&dw);
+    
+    }
+    
+    return 0;
+
+}
+
+static void walk_dir (struct filesystem *fs, char *dirname) {
+
+    unsigned long parent_nod = EXT2_ROOT_INO;
+    unsigned long timestamp = time (0);
+    
+    char *p1 = dirname, *p2;
+    
+    while (*p1 && *p1 == '/') {
+        p1++;
+    }
+    
+    if (*p1) {
+    
+        for (; (p2 = strchr (p1, '/')); p1 = p2 + 1) {
+        
+            *p2 = '\0';
+            
+            if (strlen (p1) >= 14) {
+                p1[13] = '\0';
+            }
+            
+            if (!(parent_nod = find_entry (fs, parent_nod, p1))) {
+            
+                report_at (program_name, 0, REPORT_ERROR, "cannot access '%s'", dirname);
+                
+                *p2 = '/';
+                return;
+            
+            }
+            
+            *p2 = '/';
+        
+        }
+        
+        if (find_entry (fs, parent_nod, p1)) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "'%s' already exists", dirname);
+            return;
+        
+        }
+    
+    }
+    
+    mknod_fs (fs, parent_nod, p1, mode_con (mode), 0, 0, timestamp, timestamp);
+
+}
+
+int main (int argc, char **argv) {
+
+    struct superblock sup = { 0 };
+    
+    struct filesystem *fs;
+    long i;
+    
+    if (argc && *argv) {
+    
+        char *p;
+        program_name = *argv;
+        
+        if ((p = strrchr (program_name, '/')) || (p = strrchr (program_name, '\\'))) {
+            program_name = (p + 1);
+        }
+    
+    }
+    
+    atexit (cleanup);
+    
+    mode = xstrdup ("d--644");
+    parse_args (argc, argv, 1);
+    
+    if (!outfile) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "no input file was provided");
+        return EXIT_FAILURE;
+    
+    }
+    
+    if ((ofp = fopen (outfile, "r+b")) == NULL) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "faild to open '%s' for writing", outfile);
+        return EXIT_FAILURE;
+    
+    }
+    
+    seekto (ofp, EXT2_SUPERBLOCK_OFFSET / BLOCKSIZE);
+    
+    if (fread (&sup, sizeof (sup), 1, ofp) != 1) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "failed to whilst reading '%s'", outfile);
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    if (sup.s_magic[0] != (EXT2_SUPER_MAGIC & 0xff) || sup.s_magic[1] != ((EXT2_SUPER_MAGIC >> 8) & 0xff)) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "'%s' has a unsupported file system", outfile);
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    fs = alloc_fs ();
+    fs->swapit = 0;
+    
+    fs->sb = &sup;
+    
+    for (i = 0; i < nb_dirs; i++) {
+        walk_dir (fs, dirs[i]);
+    }
+    
+    finish_fs (fs);
+    return (get_error_count () > 0) ? EXIT_FAILURE : EXIT_SUCCESS;
+
+}
diff --git a/inode.h b/inode.h
new file mode 100644 (file)
index 0000000..9599b24
--- /dev/null
+++ b/inode.h
@@ -0,0 +1,53 @@
+/******************************************************************************
+ * @file            inode.h
+ *****************************************************************************/
+#ifndef     _INODE_H
+#define     _INODE_H
+
+#define     EXT2_INIT_BLOCK             0xFFFFFFFF
+#define     EXT2_NDIR_BLOCKS            11
+
+#define     EXT2_IND_BLOCK              (EXT2_NDIR_BLOCKS + 1)
+
+#define     EXT2_DIND_BLOCK             (EXT2_IND_BLOCK + 1)
+#define     EXT2_TIND_BLOCK             (EXT2_DIND_BLOCK + 1)
+
+#define     EXT2_NBLOCKS                (EXT2_TIND_BLOCK + 1)
+
+struct inode {
+
+    unsigned char i_mode[2];
+    unsigned char i_uid[2];
+    unsigned char i_size[4];
+    
+    unsigned char i_atime[4];
+    unsigned char i_ctime[4];
+    unsigned char i_mtime[4];
+    unsigned char i_dtime[4];
+    
+    unsigned char i_gid[2];
+    unsigned char i_nlinks[2];
+    
+    unsigned char i_blocks[4];
+    unsigned char i_flags[4];
+    
+    unsigned char i_reserved1[4];
+    unsigned char i_block[EXT2_NBLOCKS * 4];
+    
+    unsigned char i_version[4];
+    
+    unsigned char i_file_acl[4];
+    unsigned char i_dir_acl[4];
+    
+    unsigned char i_faddr[4];
+    unsigned char i_frag;
+    unsigned char i_fsize;
+    unsigned char i_pad1[2];
+    
+    unsigned char i_reserved2[8];
+
+};
+
+#define     INODE_BLOCKSIZE     512
+
+#endif      /* _INODE_H */
diff --git a/list.c b/list.c
new file mode 100644 (file)
index 0000000..4f146b1
--- /dev/null
+++ b/list.c
@@ -0,0 +1,49 @@
+/******************************************************************************
+ * @file            list.c
+ *****************************************************************************/
+#include    "list.h"
+
+void list_init (struct list_elem *list) {
+
+    list->next = list;
+    list->prev = list;
+
+}
+
+void list_add_after (struct list_elem *pos, struct list_elem *elem) {
+
+    elem->next = pos->next;
+    elem->prev = pos;
+    
+    pos->next->prev = elem;
+    pos->next = elem;
+
+}
+
+void list_add_before (struct list_elem *pos, struct list_elem *elem) {
+
+    elem->prev = pos->prev;
+    elem->next = pos;
+    
+    pos->prev->next = elem;
+    pos->prev = elem;
+
+}
+
+void list_del (struct list_elem *elem) {
+
+    elem->next->prev = elem->prev;
+    elem->prev->next = elem->next;
+
+}
+
+void list_item_init (struct list_elem *elem) {
+
+    elem->next = elem;
+    elem->prev = elem;
+
+}
+
+int list_empty (struct list_elem *elem) {
+    return elem->next == elem;
+}
diff --git a/list.h b/list.h
new file mode 100644 (file)
index 0000000..452daa9
--- /dev/null
+++ b/list.h
@@ -0,0 +1,42 @@
+/******************************************************************************
+ * @file            list.h
+ *****************************************************************************/
+#ifndef     _LIST_H
+#define     _LIST_H
+
+#include    <stddef.h>
+
+#ifndef     offsetof
+# define        offsetof(st, m)         ((size_t) ((char *) &((st *)(0))->m - (char *) 0))
+#endif
+
+#define     container_of(ptr, type, member)                 \
+({                                                          \
+    const __typeof__ (((type *) 0)->member) *__mptr = (ptr);    \
+    (type *) ((char *) __mptr - offsetof (type, member));   \
+})
+
+struct list_elem {
+
+    struct list_elem *next;
+    struct list_elem *prev;
+
+};
+
+void list_init (struct list_elem *list);
+
+void list_add_after (struct list_elem *pos, struct list_elem *elem);
+void list_add_before (struct list_elem *pos, struct list_elem *elem);
+
+void list_del (struct list_elem *elem);
+void list_item_init (struct list_elem *elem);
+
+int list_empty (struct list_elem *elem);
+
+#define     list_for_each_elem(list, curr)                  \
+    for ((curr) = (list)->next; (curr) != (list); (curr) = (curr)->next)
+
+#define     list_for_each_elem_safe(list, curr, next)       \
+    for ((curr) = (list)->next, (next) = (curr)->next; (curr) != (list); (curr) = (next), (next) = (curr)->next)
+
+#endif      /* _LIST_H */
diff --git a/mke2fs.c b/mke2fs.c
new file mode 100644 (file)
index 0000000..435904e
--- /dev/null
+++ b/mke2fs.c
@@ -0,0 +1,775 @@
+/******************************************************************************
+ * @file            mke2fs.c
+ *****************************************************************************/
+#include    <ctype.h>
+#include    <errno.h>
+#include    <limits.h>
+#include    <stdio.h>
+#include    <stdlib.h>
+#include    <string.h>
+#include    <time.h>
+
+#include    "common.h"
+#include    "inode.h"
+#include    "list.h"
+#include    "report.h"
+
+static unsigned long sectors = 0;
+static char *boot_sector = 0;
+
+#define     OPTION_BOOT                 0x0001
+#define     OPTION_HELP                 0x0002
+#define     OPTION_OFFSET               0x0003
+#define     OPTION_SECTORS              0x0004
+#define     OPTION_VERBOSE              0x0005
+
+struct option {
+
+    const char *name;
+    int index, flags;
+
+};
+
+#define     OPTION_NO_ARG               0x0001
+#define     OPTION_HAS_ARG              0x0002
+
+static struct option opts[] = {
+
+    {   "--boot",           OPTION_BOOT,            OPTION_HAS_ARG      },
+    {   "--help",           OPTION_HELP,            OPTION_NO_ARG       },
+    {   "--offset",         OPTION_OFFSET,          OPTION_HAS_ARG      },
+    {   "--sectors",        OPTION_SECTORS,         OPTION_HAS_ARG      },
+    
+    {   0,                  0,                      0                   }
+
+};
+
+static int strstart (const char *val, const char **str) {
+
+    const char *p = val;
+    const char *q = *str;
+    
+    while (*p != '\0') {
+    
+        if (*p != *q) {
+            return 0;
+        }
+        
+        ++p;
+        ++q;
+    
+    }
+    
+    *str = q;
+    return 1;
+
+}
+
+static void print_help (void) {
+
+    if (program_name) {
+    
+        fprintf (stderr, "Usage: %s [options] outfile\n\n", program_name);
+        fprintf (stderr, "Options:\n\n");
+        
+        fprintf (stderr, "    --boot FILE           Use FILE as the boot sector.\n");
+        fprintf (stderr, "    --help                Show this help information then exit.\n");
+        fprintf (stderr, "    --offset SECTOR       Write the filesystem starting at SECTOR.\n");
+        fprintf (stderr, "    --sectors COUNT       Make the filesystem the size of COUNT * 512.\n");
+    
+    }
+    
+    exit (EXIT_SUCCESS);
+
+}
+
+static void parse_args (int argc, char **argv, int optind) {
+
+    struct option *popt;
+    const char *optarg, *r;
+    
+    if (argc <= optind) {
+        print_help ();
+    }
+    
+    while (optind < argc) {
+    
+        r = argv[optind++];
+        
+        if (r[0] != '-' || r[1] == '\0') {
+        
+            if (outfile) {
+            
+                report_at (program_name, 0, REPORT_ERROR, "multiple output files provided");
+                exit (EXIT_FAILURE);
+            
+            }
+            
+            outfile = xstrdup (r);
+            continue;
+        
+        }
+        
+        for (popt = opts; popt; popt++) {
+        
+            const char *p1 = popt->name;
+            const char *r1 = r;
+            
+            if (!p1) {
+            
+                report_at (program_name, 0, REPORT_ERROR, "invalid option -- '%s'", r);
+                exit (EXIT_FAILURE);
+            
+            }
+            
+            if (!strstart (p1, &r1)) {
+                continue;
+            }
+            
+            optarg = r1;
+            
+            if (popt->flags & OPTION_HAS_ARG) {
+            
+                if (*optarg == '\0') {
+                
+                    if (optind >= argc) {
+                    
+                        report_at (program_name, 0, REPORT_ERROR, "argument to '%s' is missing", r);
+                        exit (EXIT_FAILURE);
+                    
+                    }
+                    
+                    optarg = argv[optind++];
+                
+                }
+            
+            } else if (*optarg != '\0') {
+                continue;
+            }
+            
+            break;
+        
+        }
+        
+        switch (popt->index) {
+        
+            case OPTION_BOOT: {
+            
+                if (boot_sector) { free (boot_sector); }
+                
+                boot_sector = xstrdup (optarg);
+                break;
+            
+            }
+            
+            case OPTION_HELP: {
+            
+                print_help ();
+                break;
+            
+            }
+            
+            case OPTION_OFFSET: {
+            
+                char *temp;
+                long conversion;
+                
+                errno = 0;
+                conversion = strtol (optarg, &temp, 0);
+                
+                 if (!*optarg || isspace ((int) *optarg) || *temp || errno) {
+                
+                    report_at (program_name, 0, REPORT_ERROR, "bad number for offset");
+                    exit (EXIT_FAILURE);
+                
+                }
+                
+                if (conversion < 0 || conversion > INT_MAX) {
+                
+                    report_at (program_name, 0, REPORT_ERROR, "offset must be between 0 and %u", INT_MAX);
+                    exit (EXIT_FAILURE);
+                
+                }
+                
+                offset = conversion;
+                break;
+            
+            }
+            
+            case OPTION_SECTORS: {
+            
+                long conversion;
+                char *temp;
+                
+                errno = 0;
+                conversion = strtol (optarg, &temp, 0);
+                
+                if (!*optarg || isspace ((int) *optarg) || errno || *temp) {
+                
+                    report_at (program_name, 0, REPORT_ERROR, "bad number for sectors count");
+                    exit (EXIT_FAILURE);
+                
+                }
+                
+                if (conversion < 0) {
+                
+                    report_at (program_name, 0, REPORT_ERROR, "sectors count must be greater than zero");
+                    exit (EXIT_FAILURE);
+                
+                }
+                
+                sectors = conversion;
+                break;
+            
+            }
+            
+            default: {
+            
+                report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", r);
+                exit (EXIT_FAILURE);
+            
+            }
+        
+        }
+    
+    }
+
+}
+
+
+static unsigned long reserved_sectors = 4;
+
+static void wipe_sectors (unsigned long start, unsigned long end) {
+
+    void *zero = xmalloc (BLOCKSIZE);
+    unsigned long i;
+    
+    seekto (ofp, start);
+    
+    for (i = start; i < end; i++) {
+    
+        if (fwrite (zero, SECTOR_SIZE, 1, ofp) != 1) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "failed whilst writing blank sector");
+            free (zero);
+            
+            exit (EXIT_FAILURE);
+        
+        }
+    
+    }
+    
+    seekto (ofp, start);
+    free (zero);
+
+}
+
+static void cleanup (void) {
+
+    if (ofp != NULL) { fclose (ofp); }
+    
+    if (get_error_count () > 0) {
+        remove (outfile);
+    }
+
+}
+
+#define     INODES_PER_GROUP            8192
+#define     BLOCKS_PER_GROUP            8192
+
+struct uuid {
+
+    unsigned long time_low;
+    unsigned short time_mid;
+    
+    unsigned short time_hi_and_version;
+    unsigned short clock_seq;
+    
+    unsigned char node[6];
+
+};
+
+static void uuid_pack (struct uuid *uu, void *ptr) {
+
+    unsigned char *out = ptr;
+    unsigned long tmp;
+    
+    tmp = uu->time_low;
+    out[3] = (unsigned char) tmp;
+    
+    tmp >>= 8;
+    out[2] = (unsigned char) tmp;
+    
+    tmp >>= 8;
+    out[1] = (unsigned char) tmp;
+    
+    tmp >>= 8;
+    out[0] = (unsigned char) tmp;
+    
+    tmp = uu->time_mid;
+    out[5] = (unsigned char) tmp;
+    
+    tmp >>= 8;
+    out[4] = (unsigned char) tmp;
+    
+    tmp = uu->time_hi_and_version;
+    out[7] = (unsigned char) tmp;
+    
+    tmp >>= 8;
+    out[6] = (unsigned char) tmp;
+    
+    tmp = uu->clock_seq;
+    out[9] = (unsigned char) tmp;
+    
+    tmp >>= 8;
+    out[8] = (unsigned char) tmp;
+    
+    memcpy (out + 10, uu->node, 6);
+
+}
+
+static void uuid_unpack (void *buf, struct uuid *uu) {
+
+    unsigned char *ptr = buf;
+    unsigned long tmp;
+    
+    tmp = byte_array_to_integer (ptr, 4);
+    uu->time_low = tmp;
+    
+    ptr += 4;
+    
+    tmp = byte_array_to_integer (ptr, 2);
+    uu->time_mid = tmp;
+    
+    ptr += 2;
+    
+    tmp = byte_array_to_integer (ptr, 2);
+    uu->time_hi_and_version = tmp;
+    
+    ptr += 2;
+    
+    tmp = byte_array_to_integer (ptr, 2);
+    uu->clock_seq = tmp;
+    
+    memcpy (uu->node, ptr + 2, 6);
+
+}
+
+static void get_random_bytes (void *buf, int nbytes) {
+
+    unsigned char *cp;
+    int i;
+    
+    srand (time (0));
+    
+    for (cp = buf, i = 0; i < nbytes; i++) {
+        *cp++ ^= (rand () >> 7) & UCHAR_MAX;
+    }
+
+}
+
+static void uuid_generate (unsigned char out[16]) {
+
+    struct uuid uu;
+    unsigned char buf[16];
+    
+    get_random_bytes (buf, sizeof (buf));
+    uuid_unpack (buf, &uu);
+    
+    uu.clock_seq = (uu.clock_seq & 0x3FFF) | 0x8000;
+    uu.time_hi_and_version = (uu.time_hi_and_version & 0x0FFF) | 0x4000;
+    
+    uuid_pack (&uu, out);
+
+}
+
+static struct filesystem *init_fs (long nbblocks, long nbinodes, long nbresrvd, int holes, unsigned long fs_timestamp, unsigned long creator_os, int swapit) {
+
+    struct group_descriptor *gd;
+    struct gd_info *gi;
+    struct blk_info *bi;
+    
+    struct nod_info *ni;
+    struct inode *itab0;
+    
+    unsigned char *bbm, *ibm;
+    struct filesystem *fs;
+    
+    struct inode_pos ipos;
+    struct dirwalker dw;
+    
+    unsigned long i, j, bbmpos, ibmpos, itblpos;
+    unsigned long nod, first_block, gdsz, itblsz;
+    unsigned long nbgroups, nbinodes_per_group, overhead_per_group;
+    unsigned long free_blocks, free_blocks_per_group, free_inodes_count;
+    unsigned long nbblocks_per_group, min_nbgroups;
+    
+    first_block = (BLOCKSIZE == 1024);
+    
+    if (nbresrvd < 0) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "reserved blocks value is invalid");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    if (nbinodes < EXT2_FIRST_INO - 1 + (nbresrvd ? 1 : 0)) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "too few inodes");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    if (nbblocks < 0) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "too few blocks");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    min_nbgroups = (nbinodes + INODES_PER_GROUP - 1) / INODES_PER_GROUP;
+    
+    if ((nbgroups = (nbblocks - first_block + BLOCKS_PER_GROUP - 1) / BLOCKS_PER_GROUP) < min_nbgroups) {
+        nbgroups = min_nbgroups;
+    }
+    
+    nbblocks_per_group = rndup ((nbblocks - first_block + nbgroups - 1) / nbgroups, 8);
+    nbinodes_per_group = rndup ((nbinodes + nbgroups - 1) / nbgroups, BLOCKSIZE / sizeof (struct inode));
+    
+    if (nbinodes_per_group < 16) {
+        nbinodes_per_group = 16;
+    }
+    
+    itblsz = nbinodes_per_group * sizeof (struct inode) / BLOCKSIZE;
+    gdsz = rndup (nbgroups * sizeof (struct group_descriptor), BLOCKSIZE) / BLOCKSIZE;
+    
+    overhead_per_group = 3 + gdsz + itblsz;
+    
+    free_blocks = nbblocks - overhead_per_group * nbgroups - first_block;
+    free_blocks_per_group = nbblocks_per_group - overhead_per_group;
+    
+    fs = alloc_fs ();
+    fs->swapit = swapit;
+    
+    fs->sb = xmalloc (sizeof (*fs->sb));
+    
+    write_to_byte_array (fs->sb->s_inodes_count, nbinodes_per_group * nbgroups, 4);
+    write_to_byte_array (fs->sb->s_blocks_count, nbblocks, 4);
+    
+    write_to_byte_array (fs->sb->s_r_blocks_count, nbresrvd, 4);
+    
+    write_to_byte_array (fs->sb->s_free_blocks_count, free_blocks, 4);
+    write_to_byte_array (fs->sb->s_free_inodes_count, byte_array_to_integer (fs->sb->s_inodes_count, 4) - EXT2_FIRST_INO + 1, 4);
+    
+    write_to_byte_array (fs->sb->s_first_data_block, first_block, 4);
+    
+    write_to_byte_array (fs->sb->s_log_block_size, BLOCKSIZE >> 11, 4);
+    write_to_byte_array (fs->sb->s_log_frag_size, BLOCKSIZE >> 11, 4);
+    
+    write_to_byte_array (fs->sb->s_blocks_per_group, nbblocks_per_group, 4);
+    write_to_byte_array (fs->sb->s_frags_per_group, nbblocks_per_group, 4);
+    write_to_byte_array (fs->sb->s_inodes_per_group, nbinodes_per_group, 4);
+    
+    write_to_byte_array (fs->sb->s_wtime, fs_timestamp, 4);
+    write_to_byte_array (fs->sb->s_magic, EXT2_SUPER_MAGIC, 2);
+    
+    write_to_byte_array (fs->sb->s_lastcheck, fs_timestamp, 4);
+    write_to_byte_array (fs->sb->s_creator_os, creator_os, 4);
+    
+    uuid_generate (fs->sb->s_uuid);
+    
+    for (i = 0, bbmpos = first_block + 1 + gdsz, ibmpos = bbmpos + 1, itblpos = ibmpos + 1; i < nbgroups; i++, bbmpos += nbblocks_per_group, ibmpos += nbblocks_per_group, itblpos += nbblocks_per_group) {
+    
+        gd = get_gd (fs, i, &gi);
+        
+        if (free_blocks > free_blocks_per_group) {
+        
+            write_to_byte_array (gd->bg_free_blocks_count, free_blocks_per_group, 2);
+            free_blocks -= free_blocks_per_group;
+        
+        } else {
+        
+            write_to_byte_array (gd->bg_free_blocks_count, free_blocks, 2);
+            free_blocks = 0;
+        
+        }
+        
+        if (i) {
+            write_to_byte_array (gd->bg_free_inodes_count, nbinodes_per_group, 2);
+        } else {
+            write_to_byte_array (gd->bg_free_inodes_count, nbinodes_per_group - EXT2_FIRST_INO + 2, 2);
+        }
+        
+        write_to_byte_array (gd->bg_used_dirs_count, 0, 2);
+        
+        write_to_byte_array (gd->bg_block_bitmap, bbmpos, 4);
+        write_to_byte_array (gd->bg_inode_bitmap, ibmpos, 4);
+        write_to_byte_array (gd->bg_inode_table, itblpos, 4);
+        
+        put_gd (gi);
+    
+    }
+    
+    for (i = 0; i < nbgroups; i++) {
+    
+        gd = get_gd (fs, i, &gi);
+        
+        /* Block bitmap. */
+        bbm = GRP_GET_GROUP_BBM (fs, gd, &bi);
+        
+        for (j = byte_array_to_integer (gd->bg_free_blocks_count, 2) + overhead_per_group + 1; j <= BLOCKSIZE * 8; j++) {
+            allocate (bbm, j);
+        }
+        
+        /* System blocks. */
+        for (j = 1; j <= overhead_per_group; j++) {
+            allocate (bbm, j);
+        }
+        
+        GRP_PUT_GROUP_BBM (bi);
+        
+        /* Indoe bitmap. */
+        ibm = GRP_GET_GROUP_IBM (fs, gd, &bi);
+        
+        for (j = byte_array_to_integer (fs->sb->s_inodes_per_group, 4) + 1; j <= BLOCKSIZE * 8; j++) {
+            allocate (ibm, j);
+        }
+        
+        /* System inodes. */
+        if (i == 0) {
+        
+            for (j = 1; j < EXT2_FIRST_INO; j++) {
+                allocate (ibm, j);
+            }
+        
+        }
+        
+        GRP_PUT_GROUP_IBM (bi);
+        put_gd (gi);
+    
+    }
+    
+    gd = get_gd (fs, 0, &gi);
+    
+    free_inodes_count = byte_array_to_integer (gd->bg_free_inodes_count, 2);
+    write_to_byte_array (gd->bg_free_inodes_count, free_inodes_count - 1, 2);
+    
+    write_to_byte_array (gd->bg_used_dirs_count, 1, 2);
+    put_gd (gi);
+    
+    itab0 = get_nod (fs, EXT2_ROOT_INO, &ni);
+    
+    write_to_byte_array (itab0->i_mode, FM_IFDIR | FM_IRWXU | FM_IRGRP | FM_IROTH | FM_IXGRP | FM_IXOTH, 2);
+    write_to_byte_array (itab0->i_ctime, fs_timestamp, 4);
+    write_to_byte_array (itab0->i_mtime, fs_timestamp, 4);
+    write_to_byte_array (itab0->i_atime, fs_timestamp, 4);
+    write_to_byte_array (itab0->i_size, BLOCKSIZE, 4);
+    write_to_byte_array (itab0->i_nlinks, 2, 2);
+    
+    put_nod (ni);
+    
+    new_dir (fs, EXT2_ROOT_INO, ".", 1, &dw);
+    shrink_dir (&dw, EXT2_ROOT_INO, "..", 2);
+    
+    next_dir (&dw);                      /* force the data into the buffer. */
+    
+    inode_pos_init (fs, &ipos, EXT2_ROOT_INO, INODE_POS_EXTEND, 0);
+    extend_inode_blk (fs, &ipos, dw.b, 1);
+    inode_pos_finish (&ipos);
+    
+    put_dir (&dw);
+    
+    if (byte_array_to_integer (fs->sb->s_r_blocks_count, 4)) {
+    
+        struct inode *node;
+        unsigned char *b;
+        
+        nod = mkdir_fs (fs, EXT2_ROOT_INO, "lost+found", FM_IRWXU, 0, 0, fs_timestamp, fs_timestamp);
+        
+        b = get_workblk ();
+        write_to_byte_array (((struct directory *) b)->d_rec_len, BLOCKSIZE, 2);
+        
+        inode_pos_init (fs, &ipos, nod, INODE_POS_EXTEND, 0);
+        
+        for (i = 1; i < 16; i++) {
+            extend_inode_blk (fs, &ipos, b, 1);
+        }
+        
+        inode_pos_finish (&ipos);
+        free (b);
+        
+        node = get_nod (fs, nod, &ni);
+        write_to_byte_array (node->i_size, 16 * BLOCKSIZE, 4);
+        
+        put_nod (ni);
+    
+    }
+    
+    write_to_byte_array (fs->sb->s_state, 1, 2);
+    write_to_byte_array (fs->sb->s_max_mount_count, 20, 2);
+    
+    fs->holes = holes;
+    return fs;
+
+}
+
+int main (int argc, char **argv) {
+
+    unsigned long nbblocks = 0, nbresrvd = 0, nbinodes = 0;
+    unsigned long image_size = 0, holes = 0;
+    
+    struct filesystem *fs;
+    
+    if (argc && *argv) {
+    
+        char *p;
+        program_name = *argv;
+        
+        if ((p = strrchr (program_name, '/')) || (p = strrchr (program_name, '\\'))) {
+            program_name = (p + 1);
+        }
+    
+    }
+    
+    atexit (cleanup);
+    parse_args (argc, argv, 1);
+    
+    if (!outfile) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "no output file provided");
+        return EXIT_FAILURE;
+    
+    }
+    
+    image_size = sectors * SECTOR_SIZE;
+    
+    if ((nbblocks = image_size / BLOCKSIZE) < 5) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "block count too small");
+        return EXIT_FAILURE;
+    
+    }
+    
+    nbresrvd = (nbblocks * 5) / 100;
+    nbinodes = EXT2_FIRST_INO - 1 + (nbresrvd ? 1 : 0);
+    
+    image_size += offset * SECTOR_SIZE;
+    
+    if ((ofp = fopen (outfile, "r+b")) == NULL) {
+    
+        unsigned long len;
+        void *zero;
+        
+        if ((ofp = fopen (outfile, "w+b")) == NULL) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "failed to open '%s' for writing", outfile);
+            return EXIT_FAILURE;
+        
+        }
+        
+        zero = xmalloc (BLOCKSIZE);
+        len = image_size;
+        
+        while (len > 0) {
+        
+            if (fwrite (zero, SECTOR_SIZE, 1, ofp) != 1) {
+            
+                report_at (program_name, 0, REPORT_ERROR, "failed whilst writing '%s'", outfile);
+                free (zero);
+                
+                return EXIT_FAILURE;
+            
+            }
+            
+            len -= SECTOR_SIZE;
+        
+        }
+        
+        free (zero);
+    
+    }
+    
+    seekto (ofp, 0);
+    
+    if (offset * SECTOR_SIZE > image_size) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "size (%lu) of %s is less than the requested offset (%lu)", image_size, outfile, offset * SECTOR_SIZE);
+        return EXIT_FAILURE;
+    
+    }
+    
+    image_size -= offset * SECTOR_SIZE;
+    
+    if (sectors) {
+    
+        if (sectors * SECTOR_SIZE > image_size) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "size (%lu) of %s is less than the requested size (%lu)", image_size, outfile, sectors * SECTOR_SIZE);
+            return EXIT_FAILURE;
+        
+        }
+    
+    }
+    
+    wipe_sectors (0, reserved_sectors);
+    
+    if (boot_sector) {
+    
+        unsigned long bytes;
+        void *boot_block;
+        
+        FILE *ifp;
+        
+        if ((ifp = fopen (boot_sector, "rb")) == NULL) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "unable to open %s", boot_sector);
+            exit (EXIT_FAILURE);
+        
+        }
+        
+        fseek (ifp, 0, SEEK_END);
+        
+        if ((bytes = ftell (ifp)) > BLOCKSIZE) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "boot sector must not exceed 1024 bytes in size");
+            fclose (ifp);
+            
+            exit (EXIT_FAILURE);
+        
+        }
+        
+        boot_block = xmalloc (BLOCKSIZE);
+        fseek (ifp, 0, SEEK_SET);
+        
+        if (fread (boot_block, bytes, 1, ifp) != 1) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "failed to read %s", boot_sector);
+            free (boot_block);
+            
+            fclose (ifp);
+            return EXIT_FAILURE;
+        
+        }
+        
+        fclose (ifp);
+        
+        if (fwrite (boot_block, BLOCKSIZE, 1, ofp) != 1) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "to write boot sector to %s", outfile);
+            free (boot_block);
+            
+            return EXIT_FAILURE;
+        
+        }
+        
+        free (boot_block);
+    
+    }
+    
+    fs = init_fs (nbblocks, nbinodes, nbresrvd, holes, time (0), 0, 0);
+    fs_upgrade_rev1_largefile (fs);
+    
+    finish_fs (fs);
+    return EXIT_SUCCESS;
+
+}
diff --git a/report.c b/report.c
new file mode 100644 (file)
index 0000000..8e128ef
--- /dev/null
+++ b/report.c
@@ -0,0 +1,150 @@
+/******************************************************************************
+ * @file            report.c
+ *****************************************************************************/
+#include    <stdarg.h>
+#include    <stdio.h>
+#include    <string.h>
+
+#include    "report.h"
+
+unsigned long errors = 0;
+
+#ifndef     __PDOS__
+#if     defined (_WIN32)
+# include   <windows.h>
+static int OriginalConsoleColor = -1;
+#endif
+
+static void reset_console_color (void) {
+
+#if     defined (_WIN32)
+
+    HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE);
+    
+    if (OriginalConsoleColor == -1) { return; }
+    
+    SetConsoleTextAttribute (hStdError, OriginalConsoleColor);
+    OriginalConsoleColor = -1;
+
+#else
+
+    fprintf (stderr, "\033[0m");
+
+#endif
+
+}
+
+static void set_console_color (int color) {
+
+#if     defined (_WIN32)
+
+    HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE);
+    WORD wColor;
+    
+    if (OriginalConsoleColor == -1) {
+    
+        CONSOLE_SCREEN_BUFFER_INFO csbi;
+        
+        if (!GetConsoleScreenBufferInfo (hStdError, &csbi)) {
+            return;
+        }
+        
+        OriginalConsoleColor = csbi.wAttributes;
+    
+    }
+    
+    wColor = (OriginalConsoleColor & 0xF0) + (color & 0xF);
+    SetConsoleTextAttribute (hStdError, wColor);
+
+#else
+
+    fprintf (stderr, "\033[%dm", color);
+
+#endif
+
+}
+#endif
+
+static void output_message (const char *filename, unsigned long lineno, unsigned long idx, enum report_type type, const char *fmt, va_list ap) {
+
+    if (filename) {
+    
+        if (lineno == 0 && idx == 0) {
+            fprintf (stderr, "%s: ", filename);
+        } else {
+            fprintf (stderr, "%s:", filename);
+        }
+    
+    }
+    
+    if (lineno > 0) {
+    
+        if (idx == 0) {
+            fprintf (stderr, "%lu: ", lineno);
+        } else {
+            fprintf (stderr, "%lu:", lineno);
+        }
+    
+    }
+    
+    if (idx > 0) {
+        fprintf (stderr, "%lu: ", idx);
+    }
+    
+    if (type == REPORT_ERROR || type == REPORT_FATAL_ERROR) {
+    
+#ifndef     __PDOS__
+        set_console_color (COLOR_ERROR);
+#endif
+        
+        if (type == REPORT_ERROR) {
+            fprintf (stderr, "error:");
+        } else {
+            fprintf (stderr, "fatal error:");
+        }
+    
+    } else if (type == REPORT_INTERNAL_ERROR) {
+    
+#ifndef     __PDOS__
+        set_console_color (COLOR_INTERNAL_ERROR);
+#endif
+        
+        fprintf (stderr, "internal error:");
+    
+    } else if (type == REPORT_WARNING) {
+    
+#ifndef     __PDOS__
+        set_console_color (COLOR_WARNING);
+#endif
+        
+        fprintf (stderr, "warning:");
+    
+    }
+    
+#ifndef     __PDOS__
+    reset_console_color ();
+#endif
+    
+    fprintf (stderr, " ");
+    vfprintf (stderr, fmt, ap);
+    fprintf (stderr, "\n");
+    
+    if (type != REPORT_WARNING) {
+        ++errors;
+    }
+
+}
+
+unsigned long get_error_count (void) {
+    return errors;
+}
+
+void report_at (const char *filename, unsigned long lineno, enum report_type type, const char *fmt, ...) {
+
+    va_list ap;
+    
+    va_start (ap, fmt);
+    output_message (filename, lineno, 0, type, fmt, ap);
+    va_end (ap);
+
+}
diff --git a/report.h b/report.h
new file mode 100644 (file)
index 0000000..8fc8758
--- /dev/null
+++ b/report.h
@@ -0,0 +1,29 @@
+/******************************************************************************
+ * @file            report.h
+ *****************************************************************************/
+#ifndef     _REPORT_H
+#define     _REPORT_H
+
+enum report_type {
+
+    REPORT_ERROR = 0,
+    REPORT_FATAL_ERROR,
+    REPORT_INTERNAL_ERROR,
+    REPORT_WARNING
+
+};
+
+#if     defined (_WIN32)
+# define    COLOR_ERROR                 12
+# define    COLOR_WARNING               13
+# define    COLOR_INTERNAL_ERROR        19
+#else
+# define    COLOR_ERROR                 91
+# define    COLOR_INTERNAL_ERROR        94
+# define    COLOR_WARNING               95
+#endif
+
+unsigned long get_error_count (void);
+void report_at (const char *filename, unsigned long line_number, enum report_type type, const char *fmt, ...);
+
+#endif      /* _REPORT_H */
diff --git a/super.h b/super.h
new file mode 100644 (file)
index 0000000..d3470c1
--- /dev/null
+++ b/super.h
@@ -0,0 +1,67 @@
+/******************************************************************************
+ * @file            super.h
+ *****************************************************************************/
+#ifndef     _SUPER_H
+#define     _SUPER_H
+
+#define     EXT2_SUPERBLOCK_OFFSET      1024
+
+struct superblock {
+
+    unsigned char s_inodes_count[4];
+    unsigned char s_blocks_count[4];
+    
+    unsigned char s_r_blocks_count[4];
+    
+    unsigned char s_free_blocks_count[4];
+    unsigned char s_free_inodes_count[4];
+    
+    unsigned char s_first_data_block[4];
+    
+    unsigned char s_log_block_size[4];
+    unsigned char s_log_frag_size[4];
+    
+    unsigned char s_blocks_per_group[4];
+    unsigned char s_frags_per_group[4];
+    unsigned char s_inodes_per_group[4];
+    
+    unsigned char s_mtime[4];
+    unsigned char s_wtime[4];
+    
+    unsigned char s_mount_count[2];
+    unsigned char s_max_mount_count[2];
+    
+    unsigned char s_magic[2];
+    unsigned char s_state[2];
+    unsigned char s_errors[2];
+    
+    unsigned char s_minor_rev_level[2];
+    
+    unsigned char s_lastcheck[4];
+    unsigned char s_checkinterval[4];
+    unsigned char s_creator_os[4];
+    unsigned char s_rev_level[4];
+    
+    unsigned char s_def_resuid[2];
+    unsigned char s_def_resgid[2];
+    
+    unsigned char s_first_ino[4];
+    unsigned char s_inode_size[2];
+    unsigned char s_block_group_nr[2];
+    
+    unsigned char s_feature_compat[4];
+    unsigned char s_feature_incompat[4];
+    unsigned char s_feature_ro_compat[4];
+    
+    unsigned char s_uuid[16];
+    unsigned char s_volume_label[16];
+    unsigned char s_last_mounted[64];
+    
+    unsigned char s_algorithm_usage_bitmap[4];
+    unsigned char s_reserved[820];
+
+};
+
+#define     EXT2_SUPER_MAGIC            0xEF53
+
+#endif      /* _SUPER_H */