Initial commit
authorRobert Pengelly <robertapengelly@hotmail.com>
Sat, 26 Oct 2024 02:25:27 +0000 (03:25 +0100)
committerRobert Pengelly <robertapengelly@hotmail.com>
Sat, 26 Oct 2024 02:25:27 +0000 (03:25 +0100)
14 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]
common.c [new file with mode: 0644]
common.h [new file with mode: 0644]
inode.h [new file with mode: 0644]
mcopy.c [new file with mode: 0644]
mkfs.c [new file with mode: 0644]
mls.c [new file with mode: 0644]
mmd.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..c7d5763
--- /dev/null
@@ -0,0 +1,53 @@
+#******************************************************************************
+# @file             Makefile.unix
+#******************************************************************************
+SRCDIR              ?=  $(CURDIR)
+VPATH               :=  $(SRCDIR)
+
+CC                  :=  gcc
+CFLAGS              :=  -D_FILE_OFFSET_BITS=64 -Wall -Werror -Wextra -std=c90
+
+#CSRC                :=  common.c report.c write7x.c
+
+ifeq ($(OS), Windows_NT)
+all: mkminixfs.exe mmd.exe mls.exe mcopy.exe
+
+mkminixfs.exe: mkfs.c common.c report.c
+       $(CC) $(CFLAGS) -o $@ $^
+
+mmd.exe: mmd.c common.c report.c
+       $(CC) $(CFLAGS) -o $@ $^
+
+mls.exe: mls.c report.c
+       $(CC) $(CFLAGS) -o $@ $^
+
+mcopy.exe: mcopy.c common.c report.c
+       $(CC) $(CFLAGS) -o $@ $^
+else
+all: mkfs.minix mmd mls mcopy
+
+mkfs.minix: mkfs.c common.c report.c
+       $(CC) $(CFLAGS) -o $@ $^
+
+mmd: mmd.c common.c report.c
+       $(CC) $(CFLAGS) -o $@ $^
+
+mls: mls.c report.c
+       $(CC) $(CFLAGS) -o $@ $^
+
+mcopy: mcopy.c common.c report.c
+       $(CC) $(CFLAGS) -o $@ $^
+endif
+
+clean:
+       if [ -f mkminixfs.exe ]; then rm -rf mkminixfs.exe; fi
+       if [ -f mkfs.minix ]; then rm -rf mkfs.minix; fi
+       
+       if [ -f mmd.exe ]; then rm -rf mmd.exe; fi
+       if [ -f mmd ]; then rm -rf mmd; fi
+       
+       if [ -f mls.exe ]; then rm -rf mls.exe; fi
+       if [ -f mls ]; then rm -rf mls; fi
+       
+       if [ -f mcopy.exe ]; then rm -rf mcopy.exe; fi
+       if [ -f mcopy ]; then rm -rf mcopy; fi
diff --git a/Makefile.w32 b/Makefile.w32
new file mode 100644 (file)
index 0000000..de7a81a
--- /dev/null
@@ -0,0 +1,37 @@
+#******************************************************************************
+# @file             Makefile.w32
+#******************************************************************************
+SRCDIR              ?=  $(CURDIR)
+VPATH               :=  $(SRCDIR)
+
+CC                  :=  gcc
+CFLAGS              :=  -D_FILE_OFFSET_BITS=64 -Wall -Werror -Wextra -std=c90
+
+#CSRC                :=  common.c report.c write7x.c
+
+all: mkminixfs.exe mmd.exe mls.exe mcopy.exe
+
+clean:
+       if exist mkminixfs.exe ( del /q mkminixfs.exe )
+       if exist mkfs.minix ( del /q mkfs.minix )
+       
+       if exist mmd.exe ( del /q mmd.exe )
+       if exist mmd ( del /q mmd )
+       
+       if exist mls.exe ( del /q mls.exe )
+       if exist mls ( del /q mls )
+       
+       if exist mcopy.exe ( del /q mcopy.exe )
+       if exist mcopy ( del /q mcopy )
+
+mkminixfs.exe: mkfs.c common.c report.c
+       $(CC) $(CFLAGS) -o $@ $^
+
+mmd.exe: mmd.c common.c report.c
+       $(CC) $(CFLAGS) -o $@ $^
+
+mls.exe: mmd.c report.c
+       $(CC) $(CFLAGS) -o $@ $^
+
+mcopy.exe: mcopy.c common.c report.c
+       $(CC) $(CFLAGS) -o $@ $^
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..6728a0c
--- /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/unixfstools.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/common.c b/common.c
new file mode 100644 (file)
index 0000000..15f3d72
--- /dev/null
+++ b/common.c
@@ -0,0 +1,404 @@
+/******************************************************************************
+ * @file            common.c
+ *****************************************************************************/
+#include    <stdio.h>
+#include    <stdlib.h>
+#include    <string.h>
+
+#include    "common.h"
+#include    "inode.h"
+#include    "report.h"
+
+/*static char umap[MAX_INIT / 8] = { 0 };*/
+
+unsigned long next_inode = 0;
+unsigned long next_zone = 0;
+
+unsigned long offset = 0;
+
+char *program_name = 0;
+char *outfile = 0;
+
+char buffer[BLOCK_SIZE] = { 0 };
+
+unsigned long inode_offset = 0;
+unsigned long zone_shift = 0;
+unsigned long zone_size = 0;
+
+unsigned long zone_off = 0;
+unsigned long zone_map = 0;
+
+unsigned long ninodes = 0;
+unsigned long nzones = 0;
+
+/*static int read_and_set (int n) {
+
+    int w, s, mask, r;
+    
+    w = n / 8;
+    s = n % 8;
+    
+    mask = 1 << s;
+    
+    r = (umap[w] & mask) ? 1 : 0;
+    umap[w] |= mask;
+    
+    return r;
+
+}*/
+
+int seekto (FILE *fp, unsigned long sector) {
+    return fseek (fp, (offset + sector) * SECTOR_SIZE, SEEK_SET);
+}
+
+
+extern int alloc_zone (FILE *fp);
+
+void add_zone (FILE *fp, int n, int z, unsigned long bytes, unsigned long curr_time) {
+
+    struct inode *p, inode[INODES_PER_BLOCK];
+    
+    unsigned long b, off, blk[NR_INDIRECTS], indir, i;
+    unsigned long i_size, zone;
+    
+    b = ((n - 1) / INODES_PER_BLOCK) + inode_offset;
+    
+    memset (inode, 0, sizeof (inode));
+    get_block (fp, b, inode);
+    
+    off = (n - 1) % INODES_PER_BLOCK;
+    
+    p = &inode[off];
+    
+    i_size  = ((unsigned long) p->i_size[0]) | (((unsigned long) p->i_size[1]) << 8) | (((unsigned long) p->i_size[2]) << 16) | (((unsigned long) p->i_size[3]) << 24);
+    i_size += bytes;
+    
+    write_to_byte_array (p->i_size, i_size, 4);
+    write_to_byte_array (p->i_mod_time, curr_time, 4);
+    
+    for (i = 0; i < NR_DZONE_NUM * 2; i += 2) {
+    
+        zone = ((unsigned long) p->i_zone[i]) | (((unsigned long) p->i_zone[i + 1]) << 8);
+        
+        if (!zone) {
+        
+            write_to_byte_array (p->i_zone + i, z, 2);
+            
+            put_block (fp, b, inode);
+            return;
+        
+        }
+    
+    }
+    
+    put_block (fp, b, inode);
+    zone = ((unsigned long) p->i_zone[NR_DZONE_NUM * 2]) | (((unsigned long) p->i_zone[(NR_DZONE_NUM * 2) + 1]) << 8);
+    
+    if (!zone) {
+    
+        zone = alloc_zone (fp);
+        write_to_byte_array (p->i_zone + (NR_DZONE_NUM * 2), zone, 2);
+    
+    }
+    
+    indir = zone;
+    put_block (fp, b, inode);
+    
+    b = indir << zone_shift;
+    
+    memset (blk, 0, sizeof (blk));
+    get_block (fp, b, blk);
+    
+    for (i = 0; i < NR_INDIRECTS; i++) {
+    
+        if (!blk[i]) {
+        
+            blk[i] = z;
+            
+            put_block (fp, b, blk);
+            return;
+        
+        }
+    
+    }
+    
+    report_at (program_name, 0, REPORT_ERROR, "file has grown beyond single indirect");
+    exit (EXIT_FAILURE);
+
+}
+
+void enter_dir (FILE *fp, int parent, char *name, int child) {
+
+    struct dirent de[NR_DIR_ENTRIES];
+    
+    unsigned long i, j, k, l, b, off;
+    unsigned long zone;
+    
+    struct inode ino[INODES_PER_BLOCK];
+    char *p1, *p2;
+    
+    b = ((parent - 1) / INODES_PER_BLOCK) + inode_offset;
+    
+    memset (ino, 0, sizeof (ino));
+    get_block (fp, b, ino);
+    
+    off = (parent - 1) % INODES_PER_BLOCK;
+    
+    for (k = 0; k < NR_DZONE_NUM * 2; k += 2) {
+    
+        struct inode inode = ino[off];
+        zone = ((unsigned long) inode.i_zone[k]) | (((unsigned long) inode.i_zone[k + 1]) << 8);
+        
+        if (zone == 0) {
+        
+            zone = alloc_zone (fp);
+            write_to_byte_array (inode.i_zone, zone, 2);
+        
+        }
+        
+        for (l = 0; l < zone_size; l++) {
+        
+            memset (de, 0, sizeof (de));
+            get_block (fp, (zone << zone_shift) + l, de);
+            
+            for (i = 0; i < NR_DIR_ENTRIES; i++) {
+            
+                unsigned long inum = ((unsigned long) de[i].d_inum[0]) | (((unsigned long) de[i].d_inum[1]) << 8);
+                
+                if (!inum) {
+                
+                    write_to_byte_array (de[i].d_inum, child, 2);
+                    
+                    p1 = de[i].d_name;
+                    p2 = name;
+                    
+                    j = 14;
+                    
+                    while (j--) {
+                    
+                        *p1++ = *p2;
+                        
+                        if (*p2 != 0) {
+                            p2++;
+                        }
+                    
+                    }
+                    
+                    put_block (fp, (zone << zone_shift) + l, de);
+                    put_block (fp, b, ino);
+                    
+                    return;
+                
+                }
+            
+            }
+        
+        }
+    
+    }
+    
+    report_at (program_name, 0, REPORT_ERROR, "directory inode %d beyond direct blocks.  could not enter %s", parent, name);
+    exit (EXIT_FAILURE);
+
+}
+
+void get_block (FILE *fp, int n, void *buf) {
+
+    int k;
+    
+    /*if (read_and_set (n) == 0) {
+    
+        memset (buf, 0, BLOCK_SIZE);
+        return;
+    
+    }*/
+    
+    seekto (fp, n * 2);
+    
+    if ((k = fread (buf, BLOCK_SIZE, 1, fp)) != 1) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", outfile);
+        exit (EXIT_FAILURE);
+    
+    }
+
+}
+
+void incr_link (FILE *fp, int n) {
+
+    struct inode inode[INODES_PER_BLOCK];
+    int b, off;
+    
+    b = ((n - 1) / INODES_PER_BLOCK) + inode_offset;
+    
+    memset (inode, 0, sizeof (inode));
+    get_block (fp, b, inode);
+    
+    off = (n - 1) % INODES_PER_BLOCK;
+    
+    inode[off].i_nlinks++;
+    put_block (fp, b, inode);
+
+}
+
+void incr_size (FILE *fp, int n, unsigned long count) {
+
+    struct inode inode[INODES_PER_BLOCK];
+    
+    unsigned long size;
+    int b, off;
+    
+    b = ((n - 1) / INODES_PER_BLOCK) + inode_offset;
+    
+    memset (inode, 0, sizeof (inode));
+    get_block (fp, b, inode);
+    
+    off = (n - 1) % INODES_PER_BLOCK;
+    
+    size = ((unsigned long) inode[off].i_size[0]) | (((unsigned long) inode[off].i_size[1]) << 8);
+    size += count;
+    
+    write_to_byte_array (inode[off].i_size, size, 2);
+    put_block (fp, b, inode);
+
+}
+
+
+int get_bit (FILE *fp, unsigned long block, int bit) {
+
+    int w, s;
+    
+    memset (buffer, 0, sizeof (buffer));
+    get_block (fp, block, buffer);
+    
+    w = bit / (8 * sizeof (unsigned short));
+    s = bit % (8 * sizeof (unsigned short));
+    
+    return (buffer[w] & (1 << s));
+
+}
+
+void insert_bit (FILE *fp, unsigned long block, int bit) {
+
+    int w, s;
+    
+    memset (buffer, 0, sizeof (buffer));
+    get_block (fp, block, buffer);
+    
+    w = bit / (8 * sizeof (unsigned short));
+    s = bit % (8 * sizeof (unsigned short));
+    
+    buffer[w] |= (1 << s);
+    put_block (fp, block, buffer);
+
+}
+
+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;
+
+}
+
+void put_block (FILE *fp, int n, void *buf) {
+
+    /*if (read_and_set (n) == 0) {
+    
+        memset (buf, 0, BLOCK_SIZE);
+        return;
+    
+    }*/
+    
+    seekto (fp, n * 2);
+    
+    if (fwrite (buf, BLOCK_SIZE, 1, fp) != 1) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "failed whilst writing to '%s'", outfile);
+        exit (EXIT_FAILURE);
+    
+    }
+
+}
+
+void write_to_byte_array (unsigned char *arr, unsigned long value, int size) {
+
+    int i;
+    
+    for (i = 0; i < size; i++) {
+        arr[i] = (value >> (8 * i)) & 0xff;
+    }
+
+}
+
+
+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;
+
+}
diff --git a/common.h b/common.h
new file mode 100644 (file)
index 0000000..f22c23e
--- /dev/null
+++ b/common.h
@@ -0,0 +1,68 @@
+/******************************************************************************
+ * @file            common.h
+ *****************************************************************************/
+#ifndef     _COMMON_H
+#define     _COMMON_H
+
+#include    <stdio.h>
+
+extern unsigned long offset;
+
+extern char *outfile;
+extern char *program_name;
+
+int seekto (FILE *fp, unsigned long sector);
+
+char *xstrdup (const char *str);
+void *xmalloc (unsigned long size);
+void *xrealloc (void *ptr, unsigned long size);
+
+void write_to_byte_array (unsigned char *arr, unsigned long value, int size);
+
+#define     SECTOR_SIZE                 0x0200
+#define     BLOCK_SIZE                  0x0400
+
+#define     N_BLOCKS                    (1024L * 1024L)
+#define     N_BLOCKS16                  (128L * 1024L)
+
+#define     MAX_INIT                    (sizeof (char *) == 2 ? N_BLOCKS16 : N_BLOCKS)
+
+#define     NR_INDIRECTS                (BLOCK_SIZE / sizeof (unsigned short))
+#define     NR_DZONE_NUM                (NR_ZONE_NUMS - 2)
+
+extern char buffer[BLOCK_SIZE];
+
+void get_block (FILE *fp, int n, void *buf);
+void put_block (FILE *fp, int n, void *buf);
+
+void add_zone (FILE *fp, int n, int z, unsigned long bytes, unsigned long curr_time);
+void enter_dir (FILE *fp, int parent, char *name, int child);
+
+void incr_size (FILE *fp, int n, unsigned long count);
+void incr_link (FILE *fp, int n);
+
+int get_bit (FILE *fp, unsigned long block, int bit);
+void insert_bit (FILE *fp, unsigned long block, int bit);
+
+int mode_con (char *p);
+
+struct dirent {
+
+    unsigned char d_inum[2];
+    char d_name[14];
+
+};
+
+#define     NR_DIR_ENTRIES              (BLOCK_SIZE / sizeof (struct dirent))
+
+extern unsigned long ninodes;
+extern unsigned long nzones;
+
+extern unsigned long inode_offset;
+extern unsigned long zone_shift;
+extern unsigned long zone_size;
+
+extern unsigned long zone_off;
+extern unsigned long zone_map;
+
+#endif      /* _COMMON_H */
diff --git a/inode.h b/inode.h
new file mode 100644 (file)
index 0000000..6caf1aa
--- /dev/null
+++ b/inode.h
@@ -0,0 +1,27 @@
+/******************************************************************************
+ * @file            inode.h
+ *****************************************************************************/
+#ifndef     _INODE_H
+#define     _INODE_H
+
+#define     NR_ZONE_NUMS                9
+
+struct inode {
+
+    unsigned char i_mode[2];
+    unsigned char uid[2];
+    
+    unsigned char i_size[4];
+    unsigned char i_mod_time[4];
+    
+    unsigned char gid;
+    unsigned char i_nlinks;
+    
+    unsigned char i_zone[2 * NR_ZONE_NUMS];
+
+};
+
+#define     INODES_PER_BLOCK            (BLOCK_SIZE / sizeof (struct inode))
+#define     INODE_MAP                   2
+
+#endif      /* _INODE_H */
diff --git a/mcopy.c b/mcopy.c
new file mode 100644 (file)
index 0000000..fd0d3da
--- /dev/null
+++ b/mcopy.c
@@ -0,0 +1,590 @@
+/******************************************************************************
+ * @file            mcopy.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);
+                
+                }
+                
+                fprintf (stderr, "%p: %s\n", mode, mode + 3);
+                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 char buf[BLOCK_SIZE] = { 0 };
+static FILE *ofp = NULL;
+
+static void cleanup (void) {
+
+    if (ofp != NULL) {
+        fclose (ofp);
+    }
+
+}
+
+static unsigned long first_data_zone = 0;
+static unsigned long first_inode = 1;
+
+static int alloc_inode (FILE *fp, int mode, int userid, int groupid) {
+
+    struct inode inode[INODES_PER_BLOCK];
+    unsigned long int num, b, off;
+    
+    for (num = first_inode; num < ninodes; num++) {
+    
+        if (!get_bit (fp, INODE_MAP, num)) {
+        
+            b = ((num - 1) / INODES_PER_BLOCK) + inode_offset;
+            
+            memset (inode, 0, sizeof (inode));
+            get_block (fp, b, inode);
+            
+            off = (num - 1) % INODES_PER_BLOCK;
+            
+            write_to_byte_array (inode[off].i_mode, mode, 2);
+            write_to_byte_array (inode[off].uid, userid, 2);
+            
+            inode[off].gid = groupid;
+            put_block (fp, b, inode);
+            
+            insert_bit (fp, INODE_MAP, num);
+            return num;
+        
+        }
+    
+    }
+    
+    report_at (program_name, 0, REPORT_ERROR, "file system does not have enough inodes");
+    exit (EXIT_FAILURE);
+
+}
+
+int alloc_zone (FILE *fp) {
+
+    unsigned long b, z, i;
+    
+    for (z = first_data_zone; z < nzones; z++) {
+    
+        if (!get_bit (ofp, zone_map, z - zone_off)) {
+        
+            b = z << zone_shift;
+            
+            if ((b + zone_size) > nzones) {
+                goto _zone_error;
+            }
+            
+            memset (buf, 0, BLOCK_SIZE);
+            
+            for (i = 0; i < zone_size; i++) {
+                put_block (fp, b + i, buf);
+            }
+            
+            insert_bit (fp, zone_map, z - zone_off);
+            return z;
+        
+        }
+    
+    }
+
+_zone_error:
+
+    report_at (program_name, 0, REPORT_ERROR, "file system not big enough for all files");
+    exit (EXIT_FAILURE);
+
+}
+
+static int find_entry (int ino, char *name) {
+
+    struct inode inode[INODES_PER_BLOCK], *p;
+    struct dirent de[NR_DIR_ENTRIES];
+    
+    unsigned long b, off, i, j, zone;
+    unsigned long mode;
+    
+    b = ((ino - 1) / INODES_PER_BLOCK) + inode_offset;
+    off = (ino - 1) % INODES_PER_BLOCK;
+    
+    memset (inode, 0, sizeof (inode));
+    get_block (ofp, b, inode);
+    
+    p = &inode[off];
+    mode = ((unsigned long) p->i_mode[0]) | (((unsigned long) p->i_mode[1]) << 8);
+    
+    if ((mode & 0170000) != 0040000) {
+        return 0;
+    }
+    
+    for (i = 0; i < NR_DZONE_NUM * 2; i += 2) {
+    
+        zone = ((unsigned long) p->i_zone[i]) | (((unsigned long) p->i_zone[1]) << 8);
+        
+        if (!zone) {
+            continue;
+        }
+        
+        memset (de, 0, sizeof (de));
+        get_block (ofp, zone, de);
+        
+        for (j = 0; j < NR_DIR_ENTRIES; j++) {
+        
+            if (strcmp (de[j].d_name, name) == 0) {
+                return (((unsigned long) de[j].d_inum[0]) | (((unsigned long) de[j].d_inum[1]) << 8));
+            }
+        
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static int walk_path (char *dirname) {
+
+    unsigned long ino = first_inode;
+    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 (!(ino = find_entry (ino, p1))) {
+            
+                *p2 = '/';
+                
+                report_at (program_name, 0, REPORT_ERROR, "failed to enter '%s'", dirname);
+                return 0;
+            
+            }
+            
+            *p2 = '/';
+        
+        }
+        
+        if (!(ino = find_entry (ino, p1))) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "failed to enter '%s'", dirname);
+            return 0;
+        
+        }
+    
+    }
+    
+    return ino;
+
+}
+
+static void copy_file (FILE *fp, unsigned long parent, char *fn) {
+
+    unsigned long ino, zone, i;
+    
+    unsigned long bytes = 0, written = 0, ct = BLOCK_SIZE;
+    char *basename = fn, buf[BLOCK_SIZE], *p;
+    
+    if ((p = strrchr (fn, '/'))) {
+        basename = (p + 1);
+    }
+    
+    fseek (fp, 0, SEEK_END);
+    
+    bytes = ftell (fp);
+    fseek (fp, 0, SEEK_SET);
+    
+    if (bytes < BLOCK_SIZE) {
+        ct = bytes;
+    }
+    
+    ino = alloc_inode (ofp, mode_con (mode), 2, 1);
+    enter_dir (ofp, parent, basename, ino);
+    
+    incr_size (ofp, parent, 16L);
+    incr_link (ofp, ino);
+    
+    do {
+    
+        for (i = 0, written = 0; i < zone_size; i++, written += ct) {
+        
+            memset (buf, 0, sizeof (buf));
+            
+            if ((ct = fread (buf, 1, (bytes > BLOCK_SIZE) ? BLOCK_SIZE : bytes, fp)) > 0) {
+            
+                if (i == 0) {
+                    zone = alloc_zone (ofp);
+                }
+                
+                put_block (ofp, (zone << zone_shift) + i, buf);
+                bytes -= ct;
+            
+            }
+        
+        }
+        
+        if (ct) { add_zone (ofp, ino, zone, written, time (0)); }
+    
+    } while (ct == BLOCK_SIZE);
+
+}
+
+int main (int argc, char **argv) {
+
+    struct super_block sup = { 0 };
+    
+    unsigned long parent_ino = first_inode;
+    unsigned long s_imap_blocks, s_zmap_blocks;
+    
+    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, 2);
+    
+    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] != (SUPER_MAGIC & 0xff) || sup.s_magic[1] != ((SUPER_MAGIC >> 8) & 0xff)) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "'%s' has a unsupported file system", outfile);
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    ninodes = ((unsigned long) sup.s_ninodes[0]) | (((unsigned long) sup.s_ninodes[1]) << 8);
+    nzones = ((unsigned long) sup.s_nzones[0]) | (((unsigned long) sup.s_nzones[1]) << 8);
+    
+    s_imap_blocks = ((unsigned long) sup.s_imap_blocks[0]) | (((unsigned long) sup.s_imap_blocks[1]) << 8);
+    s_zmap_blocks = ((unsigned long) sup.s_zmap_blocks[0]) | (((unsigned long) sup.s_zmap_blocks[1]) << 8);
+    
+    first_data_zone = ((unsigned long) sup.s_first_data_zone[0]) | (((unsigned long) sup.s_first_data_zone[1]) << 8);
+    inode_offset = s_imap_blocks + s_zmap_blocks + 2;
+    
+    zone_shift = ((unsigned long) sup.s_log_zone_size[0]) | (((unsigned long) sup.s_log_zone_size[1]) << 8);
+    zone_size = 1 << zone_shift;
+    
+    zone_map = INODE_MAP + s_imap_blocks;
+    zone_off = first_data_zone - 1;
+    
+    if (root) {
+    
+        if (!(parent_ino = walk_path (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 (fp, parent_ino, files[i]);
+        fclose (fp);
+    
+    }
+    
+    return EXIT_SUCCESS;
+
+}
diff --git a/mkfs.c b/mkfs.c
new file mode 100644 (file)
index 0000000..3b52588
--- /dev/null
+++ b/mkfs.c
@@ -0,0 +1,598 @@
+/******************************************************************************
+ * @file            mkfs.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 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 char buf[BLOCK_SIZE] = { 0 };
+
+static unsigned long next_inode = 0;
+static unsigned long next_zone = 0;
+
+static int alloc_inode (FILE *fp, int mode, int userid, int groupid) {
+
+    struct inode inode[INODES_PER_BLOCK];
+    unsigned long int num, b, off;
+    
+    if ((num = next_inode++) >= ninodes) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "file system does not have enough inodes");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    b = ((num - 1) / INODES_PER_BLOCK) + inode_offset;
+    
+    memset (inode, 0, sizeof (inode));
+    get_block (fp, b, inode);
+    
+    off = (num - 1) % INODES_PER_BLOCK;
+    
+    write_to_byte_array (inode[off].i_mode, mode, 2);
+    write_to_byte_array (inode[off].uid, userid, 2);
+    
+    inode[off].gid = groupid;
+    put_block (fp, b, inode);
+    
+    insert_bit (fp, INODE_MAP, num);
+    return num;
+
+}
+
+int alloc_zone (FILE *fp) {
+
+    unsigned long b, z, i;
+    
+    z = next_zone++;
+    b = z << zone_shift;
+    
+    if ((b + zone_size) > nzones) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "file system not big enough for all files");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    memset (buf, 0, BLOCK_SIZE);
+    
+    for (i = 0; i < zone_size; i++) {
+        put_block (fp, b + i, buf);
+    }
+    
+    insert_bit (fp, zone_map, z - zone_off);
+    return z;
+
+}
+
+#define     BIT_MAP_SHIFT               13
+
+static unsigned long reserved_sectors = 4;
+static FILE *ofp = NULL;
+
+static void wipe_sectors (unsigned long start, unsigned long end) {
+
+    unsigned long i;
+    seekto (ofp, start);
+    
+    memset (buf, 0, BLOCK_SIZE);
+    
+    for (i = start; i < end; i++) {
+    
+        if (fwrite (buf, SECTOR_SIZE, 1, ofp) != 1) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "failed whilst writing blank sector");
+            exit (EXIT_FAILURE);
+        
+        }
+    
+    }
+    
+    seekto (ofp, start);
+
+}
+
+static int bitmapsize (int bits) {
+
+    int nr_blocks = (bits >> BIT_MAP_SHIFT);
+    
+    if ((nr_blocks << BIT_MAP_SHIFT) < bits) {
+        nr_blocks++;
+    }
+    
+    return nr_blocks;
+
+}
+
+static void super (unsigned long zones, unsigned long inodes) {
+
+    unsigned long inode_blocks, init_blocks;
+    unsigned long s_imap_blocks, s_zmap_blocks, s_first_data_zone;
+    
+    unsigned long zo;
+    
+    struct super_block *sup;
+    memset (buf, 0, BLOCK_SIZE);
+    
+    sup = (struct super_block *) buf;
+    
+    write_to_byte_array (sup->s_ninodes, inodes, 2);
+    write_to_byte_array (sup->s_nzones, zones, 2);
+    
+    s_imap_blocks = bitmapsize (1 + inodes);
+    s_zmap_blocks = bitmapsize (zones);
+    
+    write_to_byte_array (sup->s_imap_blocks, s_imap_blocks, 2);
+    write_to_byte_array (sup->s_zmap_blocks, s_zmap_blocks, 2);
+    
+    inode_blocks = (inodes + INODES_PER_BLOCK - 1) / INODES_PER_BLOCK;
+    inode_offset = s_imap_blocks + s_zmap_blocks + 2;
+    
+    init_blocks = inode_offset + inode_blocks;
+    
+    s_first_data_zone = (init_blocks + (1 << zone_shift) - 1) >> zone_shift;
+    write_to_byte_array (sup->s_first_data_zone, s_first_data_zone, 2);
+    
+    write_to_byte_array (sup->s_log_zone_size, zone_shift, 2);
+    write_to_byte_array (sup->s_magic, SUPER_MAGIC, 2);
+    
+    zo = 7L + NR_INDIRECTS + (NR_INDIRECTS * NR_INDIRECTS);
+    write_to_byte_array (sup->s_max_size, zo, 4);
+    
+    seekto (ofp, 2);
+    
+    if (fwrite (buf, BLOCK_SIZE, 1, ofp) != 1) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "failed whilst writing super block");
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    wipe_sectors (4, init_blocks * 2);
+    
+    next_zone = s_first_data_zone;
+    next_inode = 1;
+    
+    zone_map = INODE_MAP + s_imap_blocks;
+    
+    insert_bit (ofp, zone_map, 0);
+    insert_bit (ofp, INODE_MAP, 0);
+    
+    zone_size = 1 << zone_shift;
+    zone_off = s_first_data_zone - 1;
+
+}
+
+static void rootdir (int inode) {
+
+    add_zone (ofp, inode, alloc_zone (ofp), 32L, time (0));
+    
+    enter_dir (ofp, inode, ".", inode);
+    enter_dir (ofp, inode, "..", inode);
+    
+    incr_link (ofp, inode);
+    incr_link (ofp, inode);
+
+}
+
+static void cleanup (void) {
+
+    if (ofp != NULL) { fclose (ofp); }
+    
+    if (get_error_count () > 0) {
+        remove (outfile);
+    }
+
+}
+
+int main (int argc, char **argv) {
+
+    unsigned long image_size = 0;
+    
+    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 ((nzones = image_size / BLOCK_SIZE) < 5) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "block count too small");
+        return EXIT_FAILURE;
+    
+    }
+    
+    if (nzones > N_BLOCKS) {
+        nzones = N_BLOCKS;
+    }
+    
+    ninodes = nzones / 3;
+    
+    if (nzones >= 20000) {
+        ninodes = nzones / 4;
+    }
+    
+    if (nzones >= 40000) {
+        ninodes = nzones / 5;
+    }
+    
+    if (nzones >= 60000) {
+        ninodes = nzones / 6;
+    }
+    
+    if (nzones >= 80000) {
+        ninodes = nzones / 7;
+    }
+    
+    if (nzones >= 100000) {
+        ninodes = nzones / 8;
+    }
+    
+    ninodes += INODES_PER_BLOCK;
+    ninodes  = (ninodes / INODES_PER_BLOCK) * INODES_PER_BLOCK;
+    
+    if (ninodes < 1) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "inode count too small");
+        return EXIT_FAILURE;
+    
+    }
+    
+    if (ninodes >= USHRT_MAX) {
+        ninodes = USHRT_MAX;
+    }
+    
+    image_size += offset * SECTOR_SIZE;
+    
+    if ((ofp = fopen (outfile, "r+b")) == NULL) {
+    
+        unsigned long len;
+        
+        if ((ofp = fopen (outfile, "w+b")) == NULL) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "failed to open '%s' for writing", outfile);
+            return EXIT_FAILURE;
+        
+        }
+        
+        len = image_size;
+        
+        while (len > 0) {
+        
+            if (fwrite (buf, SECTOR_SIZE, 1, ofp) != 1) {
+            
+                report_at (program_name, 0, REPORT_ERROR, "failed whilst writing '%s'", outfile);
+                return EXIT_FAILURE;
+            
+            }
+            
+            len -= SECTOR_SIZE;
+        
+        }
+    
+    }
+    
+    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;
+        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)) > BLOCK_SIZE) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "boot sector must not exceed 1024 bytes in size");
+            
+            fclose (ifp);
+            exit (EXIT_FAILURE);
+        
+        }
+        
+        fseek (ifp, 0, SEEK_SET);
+        
+        if (fread (buf, bytes, 1, ifp) != 1) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "failed to read %s", boot_sector);
+            
+            fclose (ifp);
+            exit (EXIT_FAILURE);
+        
+        }
+        
+        fclose (ifp);
+        
+        if (fwrite (buf, BLOCK_SIZE, 1, ofp) != 1) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "to write boot sector to %s", outfile);
+            exit (EXIT_FAILURE);
+        
+        }
+    
+    }
+    
+    super (nzones, ninodes);
+    
+    rootdir (alloc_inode (ofp, 040777, 2, 2));
+    return EXIT_SUCCESS;
+
+}
diff --git a/mls.c b/mls.c
new file mode 100644 (file)
index 0000000..2b6c960
--- /dev/null
+++ b/mls.c
@@ -0,0 +1,668 @@
+/******************************************************************************
+ * @file            mls.c
+ *****************************************************************************/
+#include    <ctype.h>
+#include    <errno.h>
+#include    <limits.h>
+#include    <stdio.h>
+#include    <stdlib.h>
+#include    <string.h>
+#include    <time.h>
+
+#include    "inode.h"
+#include    "report.h"
+#include    "super.h"
+
+static const char *program_name = 0;
+static char *filename = 0;
+
+#define     SECTOR_SIZE                 0x0200
+#define     BLOCK_SIZE                  0x0400
+
+struct dirent {
+
+    unsigned char d_inum[2];
+    char d_name[14];
+
+};
+
+#define     NR_DIR_ENTRIES              (BLOCK_SIZE / sizeof (struct dirent))
+#define     NR_DZONE_NUM                (NR_ZONE_NUMS - 2)
+
+unsigned long offset = 0;
+
+#define     OPTION_INPUT                0x0001
+#define     OPTION_HELP                 0x0002
+#define     OPTION_OFFSET               0x0003
+
+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       },
+    {   "--offset",         OPTION_OFFSET,          OPTION_HAS_ARG      },
+    
+    {   0,                  0,                      0                   }
+
+};
+
+static char **entries = 0;
+static long nb_entries = 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   Print the filesystem starting at SECTOR.\n");
+    
+    }
+    
+    exit (EXIT_SUCCESS);
+
+}
+
+static 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;
+
+}
+
+static 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;
+
+}
+
+static char *xstrdup (const char *str) {
+
+    char *ptr = xmalloc (strlen (str) + 1);
+    strcpy (ptr, str);
+    
+    return ptr;
+
+}
+
+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 (&entries, &nb_entries, 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 (filename) {
+                
+                    report_at (program_name, 0, REPORT_ERROR, "multiple output files provided");
+                    exit (EXIT_FAILURE);
+                
+                }
+                
+                filename = xstrdup (optarg);
+                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 FILE *ifp = NULL;
+
+static void cleanup (void) {
+
+    if (ifp != NULL) {
+        fclose (ifp);
+    }
+
+}
+
+static int seekto (unsigned long sector) {
+    return fseek (ifp, (offset + sector) * SECTOR_SIZE, SEEK_SET);
+}
+
+static void get_block (int block, void *buf) {
+
+    seekto (block * 2);
+    
+    if (fread (buf, BLOCK_SIZE, 1, ifp) != 1) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "failed whilst reading '%s'", filename);
+        exit (EXIT_FAILURE);
+    
+    }
+
+}
+
+static unsigned long first_inode = 1;
+static unsigned long inode_offset = 0;
+
+static int find_entry (int ino, char *name) {
+
+    struct inode inode[INODES_PER_BLOCK], *p;
+    struct dirent de[NR_DIR_ENTRIES];
+    
+    unsigned long b, off, i, j, zone;
+    
+    b = ((ino - 1) / INODES_PER_BLOCK) + inode_offset;
+    off = (ino - 1) % INODES_PER_BLOCK;
+    
+    memset (inode, 0, sizeof (inode));
+    get_block (b, inode);
+    
+    p = &inode[off];
+    
+    for (i = 0; i < NR_DZONE_NUM * 2; i += 2) {
+    
+        zone = ((unsigned long) p->i_zone[i]) | (((unsigned long) p->i_zone[1]) << 8);
+        
+        if (!zone) {
+            continue;
+        }
+        
+        memset (de, 0, sizeof (de));
+        get_block (zone, de);
+        
+        for (j = 0; j < NR_DIR_ENTRIES; j++) {
+        
+            if (strcmp (de[j].d_name, name) == 0) {
+                return (((unsigned long) de[j].d_inum[0]) | (((unsigned long) de[j].d_inum[1]) << 8));
+            }
+        
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static char *rwx[] = {
+
+    "---",      "--x",      "-w-",      "-wx",
+    "r--",      "r-x",      "rw-",      "rwx",
+    "--s",      "--s",      "-ws",      "-ws",
+    "r-s",      "r-s",      "rws",      "rws"
+
+};
+
+static int walk_dir (char *dirname, int list, int multi) {
+
+    unsigned long ino = first_inode;
+    char *p1 = dirname, dt[32], *p2, *m1, *m2, *m3, c;
+    
+    struct inode inode[INODES_PER_BLOCK], inode2[INODES_PER_BLOCK];
+    struct dirent de[NR_DIR_ENTRIES];
+    
+    struct dirent *dep;
+    struct inode *ip1, *ip2;
+    
+    unsigned long block, i, j, mode, uid, size;
+    long mod_time, m, prot;
+    
+    while (*p1 && *p1 == '/') {
+        p1++;
+    }
+    
+    if (*p1) {
+    
+        for (; (p2 = strchr (p1, '/')); p1 = p2 + 1) {
+        
+            *p2 = '\0';
+            
+            if (strlen (p1) >= 14) {
+                p1[13] = '\0';
+            }
+            
+            if (!(ino = find_entry (ino, p1))) {
+            
+                *p2 = '/';
+                
+                if (!list) {
+                    return 1;
+                }
+            
+            }
+            
+            *p2 = '/';
+        
+        }
+        
+        if (!(ino = find_entry (ino, p1))) {
+        
+            if (!list) {
+                return 2;
+            }
+            
+        
+        }
+    
+    }
+    
+    block = ((ino - 1) / INODES_PER_BLOCK) + inode_offset;
+    
+    memset (inode, 0, sizeof (inode));
+    get_block (block, inode);
+    
+    ip1 = &inode[(ino - 1) % INODES_PER_BLOCK];
+    mode = ((unsigned long) ip1->i_mode[0]) | (((unsigned long) ip1->i_mode[1]) << 8);
+    
+    if ((mode & 0170000) == 0040000) {
+    
+        if (list) {
+        
+            if (multi) { printf ("%s:\n\n", dirname); }
+            
+            for (i = 0; i < NR_DZONE_NUM * 2; i += 2) {
+            
+                unsigned long zone = ((unsigned long) ip1->i_zone[i]) | (((unsigned long) ip1->i_zone[i + 1]) << 8);
+                
+                if (!zone) {
+                    continue;
+                }
+                
+                memset (de, 0, sizeof (de));
+                get_block (zone, de);
+                
+                for (j = 0; j < NR_DIR_ENTRIES; j++) {
+                
+                    unsigned long ino = ((unsigned long) de[j].d_inum[0]) | (((unsigned long) de[j].d_inum[1]) << 8);
+                    
+                    if (!ino) {
+                        continue;
+                    }
+                    
+                    dep = &de[j];
+                    
+                    if (multi) {
+                        printf ("    ");
+                    }
+                    
+                    block = ((ino - 1) / INODES_PER_BLOCK) + inode_offset;
+                    
+                    memset (inode2, 0, sizeof (inode2));
+                    get_block (block, inode2);
+                    
+                    ip2 = &inode2[(ino - 1) % INODES_PER_BLOCK];
+                    
+                    mode = ((unsigned long) ip2->i_mode[0]) | (((unsigned long) ip2->i_mode[1]) << 8);
+                    /*printf ("  %06lo", mode);*/
+                    m = (mode & 0170000);
+                    
+                    if (m == 0020000) {
+                        c = 'd';
+                    } else if (m == 0040000) {
+                        c = 'd';
+                    } else if (m == 0060000) {
+                        c = 'b';
+                    } else {
+                        c = '-';
+                    }
+                    
+                    m = mode & 07777;
+                    prot = (m >> 6) & 07;
+                    
+                    if (m & 0002000) {
+                        prot += 8;
+                    }
+                    
+                    m1 = rwx[prot];
+                    prot = (m >> 3) & 07;
+                    
+                    if (m & 0002000) {
+                        prot += 8;
+                    }
+                    
+                    m2 = rwx[prot];
+                    m3 = rwx[m & 07];
+                    
+                    printf ("%c%s%s%s  %2d", c, m1, m2, m3, ip2->i_nlinks);
+                    
+                    size = ((unsigned long) ip2->i_size[0]) | (((unsigned long) ip2->i_size[1]) << 8) | (((unsigned long) ip2->i_size[2]) << 16) | (((unsigned long) ip2->i_size[3]) << 24);
+                    printf ("  %10lu", size);
+                    
+                    uid = ((unsigned long) ip2->uid[0]) | (((unsigned long) ip2->uid[1]) << 8);
+                    printf ("  %2ld", uid);
+                    
+                    mod_time = ((unsigned long) ip2->i_mod_time[0]) | (((unsigned long) ip2->i_mod_time[1]) << 8) | (((unsigned long) ip2->i_mod_time[2]) << 16) | (((unsigned long) ip2->i_mod_time[3]) << 24);
+                    strftime (dt, sizeof (dt), "%Y-%m-%d %H:%M:%S", localtime (&mod_time));
+                    /*strftime (dt, sizeof (dt), "%b %d %Y %H:%M:%S", localtime (&mod_time));*/
+                    
+                    printf ("  %2d  %s     %s\n", ip2->gid, dt, dep->d_name);
+                
+                }
+            
+            }
+            
+            if (multi) {printf ("\n"); }
+        
+        }
+        
+        return 0;
+    
+    }
+    
+    mode = ((unsigned long) ip1->i_mode[0]) | (((unsigned long) ip1->i_mode[1]) << 8);
+    /*printf ("  %06lo", mode);*/
+    m = (mode & 0170000);
+    
+    if (m == 0020000) {
+        c = 'd';
+    } else if (m == 0040000) {
+        c = 'd';
+    } else if (m == 0060000) {
+        c = 'b';
+    } else {
+        c = '-';
+    }
+    
+    m = mode & 07777;
+    prot = (m >> 6) & 07;
+    
+    if (m & 0002000) {
+        prot += 8;
+    }
+    
+    m1 = rwx[prot];
+    prot = (m >> 3) & 07;
+    
+    if (m & 0002000) {
+        prot += 8;
+    }
+    
+    m2 = rwx[prot];
+    m3 = rwx[m & 07];
+    
+    printf ("%c%s%s%s  %2d", c, m1, m2, m3, ip1->i_nlinks);
+    
+    size = ((unsigned long) ip1->i_size[0]) | (((unsigned long) ip1->i_size[1]) << 8) | (((unsigned long) ip1->i_size[2]) << 16) | (((unsigned long) ip1->i_size[3]) << 24);
+    printf ("  %10lu", size);
+    
+    uid = ((unsigned long) ip1->uid[0]) | (((unsigned long) ip1->uid[1]) << 8);
+    printf ("  %2ld", uid);
+    
+    mod_time = ((unsigned long) ip1->i_mod_time[0]) | (((unsigned long) ip1->i_mod_time[1]) << 8) | (((unsigned long) ip1->i_mod_time[2]) << 16) | (((unsigned long) ip1->i_mod_time[3]) << 24);
+    strftime (dt, sizeof (dt), "%Y-%m-%d %H:%M:%S", localtime (&mod_time));
+    
+    printf ("  %2d  %s     %s\n", ip1->gid, dt, dirname);
+    return 3;
+
+}
+
+int main (int argc, char **argv) {
+
+    struct super_block sup = { 0 };
+    
+    unsigned long s_imap_blocks, s_zmap_blocks;
+    long multi = 0, i;
+    
+    char **found_entries = 0;
+    long nb_found_entries = 0;
+    
+    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 (!filename) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "no input file was provided");
+        return EXIT_FAILURE;
+    
+    }
+    
+    if ((ifp = fopen (filename, "rb")) == NULL) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "faild to open '%s' for reading", filename);
+        return EXIT_FAILURE;
+    
+    }
+    
+    seekto (2);
+    
+    if (fread (&sup, sizeof (sup), 1, ifp) != 1) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "failed to whilst reading '%s'", filename);
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    if (sup.s_magic[0] != (SUPER_MAGIC & 0xff) || sup.s_magic[1] != ((SUPER_MAGIC >> 8) & 0xff)) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "'%s' has a unsupported file system", filename);
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    s_imap_blocks = ((unsigned long) sup.s_imap_blocks[0]) | (((unsigned long) sup.s_imap_blocks[1]) << 8);
+    s_zmap_blocks = ((unsigned long) sup.s_zmap_blocks[0]) | (((unsigned long) sup.s_zmap_blocks[1]) << 8);
+    
+    inode_offset = s_imap_blocks + s_zmap_blocks + 2;
+    
+    for (i = 0; i < nb_entries; i++) {
+    
+        int ret = walk_dir (entries[i], 0, 0);
+        
+        if (ret == 0 || ret == 3) {
+        
+            if (ret == 0) {
+                dynarray_add (&found_entries, &nb_found_entries, entries[i]);
+            }
+            
+            multi++;
+        
+        } else if (ret == 1) {
+            report_at (program_name, 0, REPORT_ERROR, "unable to enter '%s'", entries[i]);
+        } else if (ret == 2) {
+            report_at (program_name, 0, REPORT_ERROR, "unable to list '%s'", entries[i]);
+        }
+    
+    }
+    
+    if (multi > 1) { printf ("\n"); }
+    
+    for (i = 0; i < nb_found_entries; i++) {
+    
+        if (found_entries[i]) {
+            walk_dir (found_entries[i], 1, multi > 1);
+        }
+    
+    }
+    
+    return EXIT_SUCCESS;
+
+}
diff --git a/mmd.c b/mmd.c
new file mode 100644 (file)
index 0000000..bc2bf92
--- /dev/null
+++ b/mmd.c
@@ -0,0 +1,497 @@
+/******************************************************************************
+ * @file            mmd.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"
+
+#define     OPTION_INPUT                0x0001
+#define     OPTION_HELP                 0x0002
+#define     OPTION_OFFSET               0x0003
+
+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       },
+    {   "--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_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 char buf[BLOCK_SIZE] = { 0 };
+static FILE *ofp = NULL;
+
+static void cleanup (void) {
+
+    if (ofp != NULL) {
+        fclose (ofp);
+    }
+
+}
+
+static unsigned long first_data_zone = 0;
+static unsigned long first_inode = 1;
+
+static int alloc_inode (FILE *fp, int mode, int userid, int groupid) {
+
+    struct inode inode[INODES_PER_BLOCK];
+    unsigned long int num, b, off;
+    
+    for (num = first_inode; num < ninodes; num++) {
+    
+        if (!get_bit (fp, INODE_MAP, num)) {
+        
+            b = ((num - 1) / INODES_PER_BLOCK) + inode_offset;
+            
+            memset (inode, 0, sizeof (inode));
+            get_block (fp, b, inode);
+            
+            off = (num - 1) % INODES_PER_BLOCK;
+            
+            write_to_byte_array (inode[off].i_mode, mode, 2);
+            write_to_byte_array (inode[off].uid, userid, 2);
+            
+            inode[off].gid = groupid;
+            put_block (fp, b, inode);
+            
+            insert_bit (fp, INODE_MAP, num);
+            return num;
+        
+        }
+    
+    }
+    
+    report_at (program_name, 0, REPORT_ERROR, "file system does not have enough inodes");
+    exit (EXIT_FAILURE);
+
+}
+
+int alloc_zone (FILE *fp) {
+
+    unsigned long b, z, i;
+    
+    for (z = first_data_zone; z < nzones; z++) {
+    
+        if (!get_bit (ofp, zone_map, z - zone_off)) {
+        
+            b = z << zone_shift;
+            
+            if ((b + zone_size) > nzones) {
+                goto _zone_error;
+            }
+            
+            memset (buf, 0, BLOCK_SIZE);
+            
+            for (i = 0; i < zone_size; i++) {
+                put_block (fp, b + i, buf);
+            }
+            
+            insert_bit (fp, zone_map, z - zone_off);
+            return z;
+        
+        }
+    
+    }
+
+_zone_error:
+
+    report_at (program_name, 0, REPORT_ERROR, "file system not big enough for all files");
+    exit (EXIT_FAILURE);
+
+}
+
+static int find_entry (int ino, char *name) {
+
+    struct inode inode[INODES_PER_BLOCK], *p;
+    struct dirent de[NR_DIR_ENTRIES];
+    
+    unsigned long b, off, i, j, zone;
+    unsigned long mode;
+    
+    b = ((ino - 1) / INODES_PER_BLOCK) + inode_offset;
+    off = (ino - 1) % INODES_PER_BLOCK;
+    
+    memset (inode, 0, sizeof (inode));
+    get_block (ofp, b, inode);
+    
+    p = &inode[off];
+    mode = ((unsigned long) p->i_mode[0]) | (((unsigned long) p->i_mode[1]) << 8);
+    
+    if ((mode & 0170000) != 0040000) {
+        return 0;
+    }
+    
+    for (i = 0; i < NR_DZONE_NUM * 2; i += 2) {
+    
+        zone = ((unsigned long) p->i_zone[i]) | (((unsigned long) p->i_zone[1]) << 8);
+        
+        if (!zone) {
+            continue;
+        }
+        
+        memset (de, 0, sizeof (de));
+        get_block (ofp, zone, de);
+        
+        for (j = 0; j < NR_DIR_ENTRIES; j++) {
+        
+            if (strcmp (de[j].d_name, name) == 0) {
+                return (((unsigned long) de[j].d_inum[0]) | (((unsigned long) de[j].d_inum[1]) << 8));
+            }
+        
+        }
+    
+    }
+    
+    return 0;
+
+}
+
+static void walk_dir (char *dirname) {
+
+    unsigned long parent_ino = first_inode;
+    
+    char *p1 = dirname, *p2;
+    int ino;
+    
+    while (*p1 && *p1 == '/') {
+        p1++;
+    }
+    
+    if (*p1) {
+    
+        for (; (p2 = strchr (p1, '/')); p1 = p2 + 1) {
+        
+            *p2 = '\0';
+            
+            if (strlen (p1) >= 14) {
+                p1[13] = '\0';
+            }
+            
+            if (!(parent_ino = find_entry (parent_ino, p1))) {
+            
+                *p2 = '/';
+                
+                report_at (program_name, 0, REPORT_ERROR, "failed to enter '%s'", dirname);
+                return;
+            
+            }
+            
+            *p2 = '/';
+        
+        }
+        
+        if (find_entry (parent_ino, p1)) {
+        
+            report_at (program_name, 0, REPORT_ERROR, "'%s' already exists", dirname);
+            return;
+        
+        }
+    
+    }
+    
+    ino = alloc_inode (ofp, mode_con ("d--755"), 2, 1);
+    enter_dir (ofp, parent_ino, p1, ino);
+    
+    incr_size (ofp, parent_ino, 16L);
+    incr_link (ofp, ino);
+    
+    add_zone (ofp, ino, alloc_zone (ofp), 32L, time (0));
+    
+    enter_dir (ofp, ino, ".", ino);
+    enter_dir (ofp, ino, "..", parent_ino);
+    
+    incr_link (ofp, parent_ino);
+    incr_link (ofp, ino);
+
+}
+
+int main (int argc, char **argv) {
+
+    struct super_block sup = { 0 };
+    
+    unsigned long s_imap_blocks, s_zmap_blocks;
+    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);
+    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, 2);
+    
+    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] != (SUPER_MAGIC & 0xff) || sup.s_magic[1] != ((SUPER_MAGIC >> 8) & 0xff)) {
+    
+        report_at (program_name, 0, REPORT_ERROR, "'%s' has a unsupported file system", outfile);
+        exit (EXIT_FAILURE);
+    
+    }
+    
+    ninodes = ((unsigned long) sup.s_ninodes[0]) | (((unsigned long) sup.s_ninodes[1]) << 8);
+    nzones = ((unsigned long) sup.s_nzones[0]) | (((unsigned long) sup.s_nzones[1]) << 8);
+    
+    s_imap_blocks = ((unsigned long) sup.s_imap_blocks[0]) | (((unsigned long) sup.s_imap_blocks[1]) << 8);
+    s_zmap_blocks = ((unsigned long) sup.s_zmap_blocks[0]) | (((unsigned long) sup.s_zmap_blocks[1]) << 8);
+    
+    first_data_zone = ((unsigned long) sup.s_first_data_zone[0]) | (((unsigned long) sup.s_first_data_zone[1]) << 8);
+    inode_offset = s_imap_blocks + s_zmap_blocks + 2;
+    
+    zone_shift = ((unsigned long) sup.s_log_zone_size[0]) | (((unsigned long) sup.s_log_zone_size[1]) << 8);
+    zone_size = 1 << zone_shift;
+    
+    zone_map = INODE_MAP + s_imap_blocks;
+    zone_off = first_data_zone - 1;
+    
+    for (i = 0; i < nb_dirs; i++) {
+        walk_dir (dirs[i]);
+    }
+    
+    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..1473278
--- /dev/null
+++ b/super.h
@@ -0,0 +1,25 @@
+/******************************************************************************
+ * @file            super.h
+ *****************************************************************************/
+#ifndef     _SUPER_H
+#define     _SUPER_H
+
+struct super_block {
+
+    unsigned char s_ninodes[2];
+    unsigned char s_nzones[2];
+    
+    unsigned char s_imap_blocks[2];
+    unsigned char s_zmap_blocks[2];
+    
+    unsigned char s_first_data_zone[2];
+    unsigned char s_log_zone_size[2];
+    
+    unsigned char s_max_size[4];
+    unsigned char s_magic[2];
+
+};
+
+#define     SUPER_MAGIC                 0x137F
+
+#endif      /* SUPER_H */