--- /dev/null
+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>
--- /dev/null
+#******************************************************************************
+# @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
--- /dev/null
+#******************************************************************************
+# @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 $@ $^
--- /dev/null
+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.
--- /dev/null
+/******************************************************************************
+ * @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;
+
+}
--- /dev/null
+/******************************************************************************
+ * @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 */
--- /dev/null
+/******************************************************************************
+ * @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 */
--- /dev/null
+/******************************************************************************
+ * @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;
+
+}
--- /dev/null
+/******************************************************************************
+ * @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;
+
+}
--- /dev/null
+/******************************************************************************
+ * @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;
+
+}
--- /dev/null
+/******************************************************************************
+ * @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;
+
+}
--- /dev/null
+/******************************************************************************
+ * @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);
+
+}
--- /dev/null
+/******************************************************************************
+ * @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 */
--- /dev/null
+/******************************************************************************
+ * @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 */