--- /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.pdw
+#******************************************************************************
+AS=aswin
+CC=gccwin
+LD=ldwin
+
+COPTS=-S -O2 -fno-common -ansi -I. -I../pdos/pdpclib -D__WIN32__ -D__NOBIVA__ -D__PDOS__
+COBJ=common.o report.o write7x.o
+
+all: clean mkdosfs.exe mcopy.exe mmd.exe mls.exe
+
+mkdosfs.exe: lib.o mkfs.o $(COBJ)
+ $(LD) -s -o mkdosfs.exe ../pdos/pdpclib/w32start.o lib.o mkfs.o $(COBJ) ../pdos/pdpclib/msvcrt.a
+
+mcopy.exe: mcopy.o $(COBJ)
+ $(LD) -s -o mcopy.exe ../pdos/pdpclib/w32start.o mcopy.o $(COBJ) ../pdos/pdpclib/msvcrt.a
+
+mmd.exe: mmd.o $(COBJ)
+ $(LD) -s -o mmd.exe ../pdos/pdpclib/w32start.o mmd.o $(COBJ) ../pdos/pdpclib/msvcrt.a
+
+mls.exe: mls.o $(COBJ)
+ $(LD) -s -o mls.exe ../pdos/pdpclib/w32start.o mls.o $(COBJ) ../pdos/pdpclib/msvcrt.a
+
+.c.o:
+ $(CC) $(COPTS) $<
+ $(AS) -o $@ $*.s
+ rm -f $*.s
+
+clean:
+ rm -f *.o mkdosfs.exe
+ rm -f *.o mcopy.exe
+ rm -f *.o mmd.exe
+ rm -f *.o mls.exe
--- /dev/null
+#******************************************************************************\r
+# @file Makefile.std\r
+#******************************************************************************\r
+AS=pdas --oformat coff\r
+CC=gccwin\r
+LD=pdld --no-insert-timestamp\r
+\r
+COPTS=-S -O2 -fno-common -ansi -I. -I../pdos/pdpclib -D__WIN32__ -D__NOBIVA__ -D__PDOS__\r
+COBJ=common.obj report.obj write7x.obj\r
+\r
+all: clean mkdosfs.exe mcopy.exe mmd.exe mls.exe\r
+\r
+mkdosfs.exe: lib.obj mkfs.obj $(COBJ)\r
+ $(LD) -s -o mkdosfs.exe ../pdos/pdpclib/w32start.obj lib.obj mkfs.obj $(COBJ) ../pdos/pdpclib/msvcrt.lib\r
+\r
+mcopy.exe: mcopy.obj $(COBJ)\r
+ $(LD) -s -o mcopy.exe ../pdos/pdpclib/w32start.obj mcopy.obj $(COBJ) ../pdos/pdpclib/msvcrt.lib\r
+\r
+mmd.exe: mmd.obj $(COBJ)\r
+ $(LD) -s -o mmd.exe ../pdos/pdpclib/w32start.obj mmd.obj $(COBJ) ../pdos/pdpclib/msvcrt.lib\r
+\r
+mls.exe: mls.obj $(COBJ)\r
+ $(LD) -s -o mls.exe ../pdos/pdpclib/w32start.obj mls.obj $(COBJ) ../pdos/pdpclib/msvcrt.lib\r
+\r
+.c.obj:\r
+ $(CC) $(COPTS) $<\r
+ $(AS) -o $@ $*.s\r
+ rm -f $*.s\r
+\r
+clean:\r
+ rm -f *.obj\r
+ rm -f mkdosfs.exe\r
+ rm -f mcopy.exe\r
+ rm -f mmd.exe\r
+ rm -f mls.exe\r
--- /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: mkdosfs.exe mcopy.exe mmd.exe mls.exe
+
+mkdosfs.exe: lib.c mkfs.c $(CSRC)
+ $(CC) $(CFLAGS) -o $@ $^
+
+mcopy.exe: mcopy.c $(CSRC)
+ $(CC) $(CFLAGS) -o $@ $^
+
+mmd.exe: mmd.c $(CSRC)
+ $(CC) $(CFLAGS) -o $@ $^
+
+mls.exe: mls.c $(CSRC)
+ $(CC) $(CFLAGS) -o $@ $^
+else
+all: mkdosfs mcopy mmd mls
+
+mkdosfs: lib.c mkfs.c $(CSRC)
+ $(CC) $(CFLAGS) -o $@ $^
+
+mcopy: mcopy.c $(CSRC)
+ $(CC) $(CFLAGS) -o $@ $^
+
+mmd: mmd.c $(CSRC)
+ $(CC) $(CFLAGS) -o $@ $^
+
+mls: mls.c $(CSRC)
+ $(CC) $(CFLAGS) -o $@ $^
+endif
+
+clean:
+ if [ -f mkdosfs.exe ]; then rm -rf mkdosfs.exe; fi
+ if [ -f mkdosfs ]; then rm -rf mkdosfs; fi
+
+ if [ -f mcopy.exe ]; then rm -rf mcopy.exe; fi
+ if [ -f mcopy ]; then rm -rf mcopy; 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
--- /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: mkdosfs.exe mcopy.exe mmd.exe mls.exe
+
+clean:
+ if exist mkdosfs.exe ( del /q mkdosfs.exe )
+ if exist mkdosfs ( del /q mkdosfs )
+
+ if exist mcopy.exe ( del /q mcopy.exe )
+ if exist mcopy ( del /q mcopy )
+
+ 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 )
+
+mkdosfs.exe: lib.c mkfs.c $(CSRC)
+ $(CC) $(CFLAGS) -o $@ $^
+
+mcopy.exe: mcopy.c $(CSRC)
+ $(CC) $(CFLAGS) -o $@ $^
+
+mmd.exe: mmd.c $(CSRC)
+ $(CC) $(CFLAGS) -o $@ $^
+
+mls.exe: mls.c $(CSRC)
+ $(CC) $(CFLAGS) -o $@ $^
--- /dev/null
+All source code is Public Domain.
+
+## Obtain the source code
+
+ git clone https://git.candlhat.org/dosfstools.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 <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef __PDOS__
+# if defined (__GNUC__)
+# include <sys/time.h>
+# include <unistd.h>
+# else
+# include <io.h>
+# endif
+#endif
+
+#include "common.h"
+
+unsigned short generate_datestamp (void) {
+
+#if defined (__GNUC__) && !defined (__PDOS__)
+ struct timeval create_timeval;
+#else
+ time_t create_time;
+#endif
+
+ struct tm *ctime = NULL;
+
+#if defined (__GNUC__) && !defined (__PDOS__)
+
+ if (gettimeofday (&create_timeval, 0) == 0 && create_timeval.tv_sec != (time_t) -1) {
+ ctime = localtime ((time_t *) &create_timeval.tv_sec);
+ }
+
+#else
+
+ if (time (&create_time) != 0 && create_time != 0) {
+ ctime = localtime (&create_time);
+ }
+
+#endif
+
+ if (ctime != NULL && ctime->tm_year >= 80 && ctime->tm_year <= 207) {
+ return (unsigned short) (ctime->tm_mday + ((ctime->tm_mon + 1) << 5) + ((ctime->tm_year - 80) << 9));
+ }
+
+ return 1 + (1 << 5);
+
+}
+
+unsigned short generate_timestamp (void) {
+
+#if defined (__GNUC__) && !defined (__PDOS__)
+ struct timeval create_timeval;
+#else
+ time_t create_time;
+#endif
+
+ struct tm *ctime = NULL;
+
+#if defined (__GNUC__) && !defined (__PDOS__)
+
+ if (gettimeofday (&create_timeval, 0) == 0 && create_timeval.tv_sec != (time_t) -1) {
+ ctime = localtime ((time_t *) &create_timeval.tv_sec);
+ }
+
+#else
+
+ if (time (&create_time) != 0 && create_time != 0) {
+ ctime = localtime (&create_time);
+ }
+
+#endif
+
+ if (ctime != NULL && ctime->tm_year >= 80 && ctime->tm_year <= 207) {
+ return (unsigned short) ((ctime->tm_sec >> 1) + (ctime->tm_min << 5) + (ctime->tm_hour << 11));
+ }
+
+ return 0;
+
+}
--- /dev/null
+/******************************************************************************
+ * @file common.h
+ *****************************************************************************/
+#ifndef _COMMON_H
+#define _COMMON_H
+
+extern unsigned short generate_datestamp (void);
+extern unsigned short generate_timestamp (void);
+
+#endif /* _COMMON_H */
--- /dev/null
+/******************************************************************************
+ * @file lib.c
+ *****************************************************************************/
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "lib.h"
+#include "mkfs.h"
+#include "report.h"
+
+struct option {
+
+ const char *name;
+ int index, flags;
+
+};
+
+#define OPTION_NO_ARG 0x0001
+#define OPTION_HAS_ARG 0x0002
+
+enum options {
+
+ OPTION_IGNORED = 0,
+ OPTION_ARCA,
+ OPTION_BLOCKS,
+ OPTION_BOOT,
+ OPTION_FAT,
+ OPTION_HELP,
+ OPTION_NAME,
+ OPTION_OFFSET,
+ OPTION_SECTORS,
+ OPTION_VERBOSE
+
+};
+
+static struct option opts[] = {
+
+ { "F", OPTION_FAT, OPTION_HAS_ARG },
+ { "n", OPTION_NAME, OPTION_HAS_ARG },
+ { "s", OPTION_SECTORS, OPTION_HAS_ARG },
+ { "v", OPTION_VERBOSE, OPTION_NO_ARG },
+
+ { "-arca", OPTION_ARCA, OPTION_NO_ARG },
+ { "-boot", OPTION_BOOT, OPTION_HAS_ARG },
+ { "-help", OPTION_HELP, OPTION_NO_ARG },
+ { "-offset", OPTION_OFFSET, OPTION_HAS_ARG },
+ { "-sectors", OPTION_BLOCKS, 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) {
+ goto _exit;
+ }
+
+ fprintf (stderr, "Usage: %s [options] file\n\n", program_name);
+ fprintf (stderr, "Options:\n\n");
+
+ fprintf (stderr, " Short options:\n\n");
+ fprintf (stderr, " -F BITS Select FAT size BITS (12, 16, or 32).\n");
+ fprintf (stderr, " -n LABEL Set volume label as LABEL (max 11 characters).\n");
+ fprintf (stderr, " -v Verbose execution.\n");
+
+ fprintf (stderr, "\n");
+
+ fprintf (stderr, " Long options:\n\n");
+ fprintf (stderr, " --arca Use CHS-alignment.\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 (EXIT_SUCCESS);
+
+}
+
+static int is_label_char (int ch) {
+ return ((ch & 0x80) || (ch == 0x20) || isalnum (ch) || strchr ("!#$%'-@_{}~", ch));
+}
+
+char *xstrdup (const char *str) {
+
+ char *ptr = xmalloc (strlen (str) + 1);
+ strcpy (ptr, str);
+
+ return ptr;
+
+}
+
+int xstrcasecmp (const char *s1, const char *s2) {
+
+ const unsigned char *p1;
+ const unsigned char *p2;
+
+ p1 = (const unsigned char *) s1;
+ p2 = (const unsigned char *) s2;
+
+ while (*p1 != '\0') {
+
+ if (toupper (*p1) < toupper (*p2)) {
+ return (-1);
+ } else if (toupper (*p1) > toupper (*p2)) {
+ return (1);
+ }
+
+ p1++;
+ p2++;
+
+ }
+
+ if (*p2 == '\0') {
+ return (0);
+ } else {
+ return (-1);
+ }
+
+}
+
+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;
+
+}
+
+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;
+
+}
+
+void parse_args (int *pargc, char ***pargv, int optind) {
+
+ char **argv = *pargv;
+ int argc = *pargc;
+
+ struct option *popt;
+ const char *optarg, *r;
+
+ if (argc == optind) {
+ print_help ();
+ }
+
+ memset (state->label, ' ', 11);
+
+ while (optind < argc) {
+
+ r = argv[optind++];
+
+ if (r[0] != '-' || r[1] == '\0') {
+
+ if (state->outfile) {
+
+ report_at (program_name, 0, REPORT_ERROR, "multiple output files provided");
+ exit (EXIT_FAILURE);
+
+ }
+
+ state->outfile = xstrdup (r);
+ continue;
+
+ }
+
+ for (popt = opts; popt; ++popt) {
+
+ const char *p1 = popt->name;
+ const char *r1 = (r + 1);
+
+ 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_ARCA: {
+
+ state->chs_align = 1;
+ break;
+
+ }
+
+ case OPTION_BLOCKS: {
+
+ 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);
+
+ }
+
+ state->sectors = conversion;
+ break;
+
+ }
+
+ case OPTION_BOOT: {
+
+ state->boot = xstrdup (optarg);
+ break;
+
+ }
+
+ case OPTION_FAT: {
+
+ 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 fat size");
+ exit (EXIT_FAILURE);
+
+ }
+
+ switch (conversion) {
+
+ case 12:
+ case 16:
+ case 32:
+
+ break;
+
+ default:
+
+ report_at (program_name, 0, REPORT_ERROR, "fat size can either be 12, 16 or 32 bits");
+ exit (EXIT_FAILURE);
+
+ }
+
+ state->size_fat = conversion;
+ state->size_fat_by_user = 1;
+
+ break;
+
+ }
+
+ case OPTION_HELP: {
+
+ print_help ();
+ break;
+
+ }
+
+ case OPTION_NAME: {
+
+ int n;
+
+ if (strlen (optarg) > 11) {
+
+ report_at (program_name, 0, REPORT_ERROR, "volume label cannot exceed 11 characters");
+ exit (EXIT_FAILURE);
+
+ }
+
+ for (n = 0; optarg[n] != '\0'; n++) {
+
+ if (!is_label_char (optarg[n])) {
+
+ report_at (program_name, 0, REPORT_ERROR, "volume label contains an invalid character");
+ exit (EXIT_FAILURE);
+
+ }
+
+ state->label[n] = toupper (optarg[n]);
+
+ }
+
+ break;
+
+ }
+
+ case OPTION_OFFSET: {
+
+ long conversion;
+ char *temp;
+
+ 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 || (unsigned long) conversion > UINT_MAX) {
+
+ report_at (program_name, 0, REPORT_ERROR, "offset must be between 0 and %u", UINT_MAX);
+ exit (EXIT_FAILURE);
+
+ }
+
+ state->offset = conversion;
+ break;
+
+ }
+
+ case OPTION_SECTORS: {
+
+ long conversion;
+ char *temp;
+
+ errno = 0;
+ conversion = strtol (optarg, &temp, 0);
+
+ if (!*optarg || isspace ((int) *optarg) || *temp || errno) {
+
+ report_at (program_name, 0, REPORT_ERROR, "bad number for sectors per cluster");
+ exit (EXIT_FAILURE);
+
+ }
+
+ if (conversion != 1 && conversion != 2 && conversion != 4 && conversion != 8 && conversion != 16 && conversion != 32 && conversion != 64 && conversion != 128) {
+
+ report_at (program_name, 0, REPORT_ERROR, "bad number for sectors per cluster");
+ exit (EXIT_FAILURE);
+
+ }
+
+ state->sectors_per_cluster = (unsigned char) conversion;
+ break;
+
+ }
+
+ case OPTION_VERBOSE: {
+
+ state->verbose++;
+ break;
+
+ }
+
+ default: {
+
+ report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", r);
+ exit (EXIT_FAILURE);
+
+ }
+
+ }
+
+ }
+
+ if (memcmp (state->label, " ", 11) == 0) {
+ memcpy (state->label, "NO NAME ", 11);
+ }
+
+}
--- /dev/null
+/******************************************************************************
+ * @file lib.h
+ *****************************************************************************/
+#ifndef _LIB_H
+#define _LIB_H
+
+char *xstrdup (const char *str);
+int xstrcasecmp (const char *s1, const char *s2);
+
+void *xmalloc (unsigned long size);
+void *xrealloc (void *ptr, unsigned long size);
+
+void dynarray_add (void *ptab, long *nb_ptr, void *data);
+void parse_args (int *pargc, char ***pargv, int optind);
+
+#endif /* _LIB_H */
--- /dev/null
+/******************************************************************************
+ * @file mcopy.c
+ *****************************************************************************/
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef __PDOS__
+# if defined (_WIN32)
+# include <windows.h>
+# include <winioctl.h>
+# elif defined (__GNUC__)
+# include <sys/ioctl.h>
+# include <termios.h>
+# include <unistd.h>
+# if defined (__CYGWIN__)
+# include <sys/socket.h>
+# endif
+# endif
+#endif
+
+#include "common.h"
+#include "mcopy.h"
+#include "msdos.h"
+#include "report.h"
+#include "write7x.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 2048
+#endif
+
+static FILE *ofp;
+
+struct dir_info {
+
+ unsigned int current_cluster;
+
+ unsigned char current_sector;
+ unsigned char current_entry;
+ unsigned char *scratch;
+ unsigned char flags;
+
+};
+
+struct file_info {
+
+ unsigned char dir_offset;
+ unsigned int dir_sector;
+
+ unsigned char *scratch;
+ unsigned int filelen;
+
+ unsigned int cluster;
+ unsigned int pointer;
+
+ unsigned long bytes;
+
+};
+
+static struct mcopy_state *state = 0;
+static const char *program_name = 0;
+
+static struct msdos_boot_sector bs;
+static int size_fat = 0;
+
+static unsigned int cluster_count = 0;
+static unsigned int data_area = 0;
+static unsigned int info_sector = 0;
+static unsigned int number_of_fats = 0;
+static unsigned int reserved_sectors = 0;
+static unsigned int root_cluster = 0;
+static unsigned int root_dir = 0;
+static unsigned int root_entries = 0;
+static unsigned int sectors_per_cluster = 0;
+static unsigned int sectors_per_fat = 0;
+static unsigned int total_sectors = 0;
+
+struct option {
+
+ const char *name;
+ int index, flags;
+
+};
+
+#define OPTION_NO_ARG 0x0001
+#define OPTION_HAS_ARG 0x0002
+
+enum options {
+
+ OPTION_IGNORED = 1,
+ OPTION_ARCA,
+ OPTION_HELP,
+ OPTION_INPUT,
+ OPTION_OFFSET,
+ OPTION_STATUS
+
+};
+
+static struct option opts[] = {
+
+ { "i", OPTION_INPUT, OPTION_HAS_ARG },
+
+ { "-arca", OPTION_ARCA, OPTION_NO_ARG },
+ { "-help", OPTION_HELP, OPTION_NO_ARG },
+ { "-offset", OPTION_OFFSET, OPTION_HAS_ARG },
+ { "-status", OPTION_STATUS, OPTION_NO_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 (int exitval) {
+
+ if (!program_name) {
+ goto _exit;
+ }
+
+ fprintf (stderr, "Usage: %s [options] [::]source-file [::]target-file\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, " --arca Use CHS-alignment (only works for VHD images).\n");
+ fprintf (stderr, " --help Show this help information then exit.\n");
+ fprintf (stderr, " --offset SECTOR Write the filesystem starting at SECTOR.\n");
+
+_exit:
+
+ exit (exitval);
+
+}
+
+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 *pargc, char ***pargv, int optind) {
+
+ char **argv = *pargv;
+ int argc = *pargc;
+
+ struct option *popt;
+ const char *optarg, *r;
+
+ if (argc == optind) {
+ print_help (EXIT_SUCCESS);
+ }
+
+ while (optind < argc) {
+
+ r = argv[optind++];
+
+ if (r[0] != '-' || r[1] == '\0') {
+
+ dynarray_add (&state->files, &state->nb_files, xstrdup (r));
+ continue;
+
+ }
+
+ for (popt = opts; popt; ++popt) {
+
+ const char *p1 = popt->name;
+ const char *r1 = (r + 1);
+
+ 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_ARCA: {
+
+ state->chs_align = 1;
+ break;
+
+ }
+
+ case OPTION_HELP: {
+
+ print_help (EXIT_SUCCESS);
+ break;
+
+ }
+
+ case OPTION_INPUT: {
+
+ if (state->outfile) {
+
+ report_at (program_name, 0, REPORT_ERROR, "multiple output files provided");
+ exit (EXIT_FAILURE);
+
+ }
+
+ state->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 || (unsigned long) conversion > UINT_MAX) {
+
+ report_at (program_name, 0, REPORT_ERROR, "offset must be between 0 and %u", UINT_MAX);
+ exit (EXIT_FAILURE);
+
+ }
+
+ state->offset = (unsigned long) conversion;
+ break;
+
+ }
+
+ case OPTION_STATUS: {
+
+ state->status = 1;
+ break;
+
+ }
+
+ default: {
+
+ report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", r);
+ exit (EXIT_FAILURE);
+
+ }
+
+ }
+
+ }
+
+}
+
+
+#ifndef __PDOS__
+/** Get a single character from the terminal. */
+static char getch () {
+
+#if defined(_WIN32) && !defined (__PDOS__)
+
+ KEY_EVENT_RECORD keyevent;
+ INPUT_RECORD irec;
+ DWORD events;
+
+ for (;;) {
+
+ ReadConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &irec, 1, &events);
+
+ if (irec.EventType == KEY_EVENT && ((KEY_EVENT_RECORD) irec.Event.KeyEvent).bKeyDown) {
+
+ const int ca = (int) keyevent.uChar.AsciiChar;
+ const int cv = (int) keyevent.wVirtualKeyCode;
+ const int key = ca == 0 ? -cv : ca + (ca > 0 ? 0 : 256);
+
+ keyevent = (KEY_EVENT_RECORD) irec.Event.KeyEvent;
+
+ switch (key) {
+
+ case -16: continue; /* disable Shift */
+ case -17: continue; /* disable Ctrl / AltGr */
+ case -18: continue; /* disable Alt / AltGr */
+ case -220: continue; /* disable first detection of "^" key (not "^" symbol) */
+ case -221: continue; /* disable first detection of "'" key (not "'" symbol) */
+ case -191: continue; /* disable AltGr + "#" */
+ case -52: continue; /* disable AltGr + "4" */
+ case -53: continue; /* disable AltGr + "5" */
+ case -54: continue; /* disable AltGr + "6" */
+ case -12: continue; /* disable num block 5 with num lock deactivated */
+ case 13: return 10; /* enter */
+ case -46: return 127; /* delete */
+ case -49: return 251; /* ¹ */
+ case 0: continue;
+ case 1: continue; /* disable Ctrl + a (selects all text) */
+ case 2: continue; /* disable Ctrl + b */
+ case 3: continue; /* disable Ctrl + c (terminates program) */
+ case 4: continue; /* disable Ctrl + d */
+ case 5: continue; /* disable Ctrl + e */
+ case 6: continue; /* disable Ctrl + f (opens search) */
+ case 7: continue; /* disable Ctrl + g */
+ case 10: continue; /* disable Ctrl + j */
+ case 11: continue; /* disable Ctrl + k */
+ case 12: continue; /* disable Ctrl + l */
+ case 14: continue; /* disable Ctrl + n */
+ case 15: continue; /* disable Ctrl + o */
+ case 16: continue; /* disable Ctrl + p */
+ case 17: continue; /* disable Ctrl + q */
+ case 18: continue; /* disable Ctrl + r */
+ case 19: continue; /* disable Ctrl + s */
+ case 20: continue; /* disable Ctrl + t */
+ case 21: continue; /* disable Ctrl + u */
+ case 22: continue; /* disable Ctrl + v (inserts clipboard) */
+ case 23: continue; /* disable Ctrl + w */
+ case 24: continue; /* disable Ctrl + x */
+ case 25: continue; /* disable Ctrl + y */
+ case 26: continue; /* disable Ctrl + z */
+ default: return key; /* any other ASCII/virtual character */
+
+ }
+
+ }
+
+ }
+
+#else
+
+ int key;
+
+#if defined (__PDOS__)
+
+ setvbuf (stdin, NULL, _IONBF, 0);
+
+ for (;;) {
+
+#else
+
+ struct termios term;
+ int nbbytes;
+
+ tcgetattr (0, &term);
+
+ for (;;) {
+
+ term.c_lflag &= ~(ICANON | ECHO); /* turn off line buffering and echoing */
+ tcsetattr (0, TCSANOW, &term);
+
+ ioctl (0, FIONREAD, &nbbytes); /* 0 is STDIN */
+
+ while (!nbbytes) {
+
+ sleep (1);
+ fflush (stdout);
+
+ ioctl (0, FIONREAD, &nbbytes); /* 0 is STDIN */
+
+ }
+
+#endif
+
+ key = (int) getchar ();
+
+ if (key == 27 || key == 194 || key == 195) { /* escape, 194/195 is escape for °ß´äöüÄÖÜ */
+
+ key = (int) getchar ();
+
+ if (key == 91) { /* [ following escape */
+
+ key = (int) getchar (); /* get code of next char after \e[ */
+
+ if (key == 49) { /* F5-F8 */
+
+ key = 62 + (int) getchar (); /* 53, 55-57 */
+
+ if (key == 115) {
+ key++; /* F5 code is too low by 1 */
+ }
+
+ getchar (); /* take in following ~ (126), but discard code */
+
+ } else if (key == 50) { /* insert or F9-F12 */
+
+ key = (int) getchar ();
+
+ if (key == 126) { /* insert */
+ key = 45;
+ } else { /* F9-F12 */
+
+ key += 71; /* 48, 49, 51, 52 */
+
+ if (key < 121) {
+ key++; /* F11 and F12 are too low by 1 */
+ }
+
+ getchar (); /* take in following ~ (126), but discard code */
+
+ }
+
+ } else if (key == 51 || key == 53 || key == 54) { /* delete, page up/down */
+ getchar (); /* take in following ~ (126), but discard code */
+ }
+
+ } else if (key == 79) { /* F1-F4 */
+ key = 32 + (int) getchar (); /* 80-83 */
+ }
+
+ key = -key; /* use negative numbers for escaped keys */
+
+ }
+
+#if defined (__PDOS__)
+ setvbuf (stdin, NULL, _IOLBF, 0);
+#else
+
+ term.c_lflag |= (ICANON | ECHO); /* turn on line buffering and echoing */
+ tcsetattr (0, TCSANOW, &term);
+
+#endif
+
+ switch (key) {
+
+ case 127: return (char) 8; /* backspace */
+ case -27: return (char) 27; /* escape */
+ case -51: return (char) 127; /* delete */
+ case -164: return (char) 132; /* ä */
+ case -182: return (char) 148; /* ö */
+ case -188: return (char) 129; /* ü */
+ case -132: return (char) 142; /* Ä */
+ case -150: return (char) 153; /* Ö */
+ case -156: return (char) 154; /* Ü */
+ case -159: return (char) 225; /* ß */
+ case -181: return (char) 230; /* µ */
+ case -167: return (char) 245; /* § */
+ case -176: return (char) 248; /* ° */
+ case -178: return (char) 253; /* ² */
+ case -179: return (char) 252; /* ³ */
+ case -180: return (char) 239; /* ´ */
+ case -65: return (char) -38; /* up arrow */
+ case -66: return (char) -40; /* down arrow */
+ case -68: return (char) -37; /* left arrow */
+ case -67: return (char) -39; /* right arrow */
+ case -53: return (char) -33; /* page up */
+ case -54: return (char) -34; /* page down */
+ case -72: return (char) -36; /* pos1 */
+ case -70: return (char) -35; /* end */
+ case 0: continue;
+ case 1: continue; /* disable Ctrl + a */
+ case 2: continue; /* disable Ctrl + b */
+ case 3: continue; /* disable Ctrl + c (terminates program) */
+ case 4: continue; /* disable Ctrl + d */
+ case 5: continue; /* disable Ctrl + e */
+ case 6: continue; /* disable Ctrl + f */
+ case 7: continue; /* disable Ctrl + g */
+ case 8: continue; /* disable Ctrl + h */
+ case 11: continue; /* disable Ctrl + k */
+ case 12: continue; /* disable Ctrl + l */
+ case 13: continue; /* disable Ctrl + m */
+ case 14: continue; /* disable Ctrl + n */
+ case 15: continue; /* disable Ctrl + o */
+ case 16: continue; /* disable Ctrl + p */
+ case 17: continue; /* disable Ctrl + q */
+ case 18: continue; /* disable Ctrl + r */
+ case 19: continue; /* disable Ctrl + s */
+ case 20: continue; /* disable Ctrl + t */
+ case 21: continue; /* disable Ctrl + u */
+ case 22: continue; /* disable Ctrl + v */
+ case 23: continue; /* disable Ctrl + w */
+ case 24: continue; /* disable Ctrl + x */
+ case 25: continue; /* disable Ctrl + y */
+ case 26: continue; /* disable Ctrl + z (terminates program) */
+ default: return key; /* any other ASCII character */
+
+ }
+
+ }
+
+#endif
+
+}
+#endif
+
+static int check_overwrite (const char *fn) {
+
+#if defined (__PDOS__)
+
+ report_at (NULL, 0, REPORT_WARNING, "overwriting %s", fn);
+ return 1;
+
+#else
+
+ int ch;
+
+ printf ("File %s already exists.\n", fn);
+ printf ("o)verwrite s)skip (os): ");
+
+ while ((ch = tolower (getch ())) < 0) {}
+
+ if (isspace (ch)) {
+ printf ("\n");
+ } else {
+ printf ("%c\n", ch);
+ }
+
+ if (ch == 'o') {
+ return 1;
+ } else if (ch == 's') {
+ return 0;
+ }
+
+ return check_overwrite (fn);
+
+#endif
+
+}
+
+
+static int seekto (long offset) {
+ return fseek (ofp, (state->offset * 512) + offset, SEEK_SET);
+}
+
+
+static int get_next_entry (struct dir_info *di, struct msdos_dirent *de);
+static int open_dir (const char *target, struct dir_info *di);
+
+static unsigned int get_fat_entry (unsigned char *scratch, unsigned int cluster);
+static int set_fat_entry (unsigned char *scratch, unsigned int cluster, unsigned int value);
+
+static unsigned int get_free_fat (unsigned char *scratch);
+
+static int canonical_to_dir (char *dest, const char *src) {
+
+ static const char invalid_chars[] = "\"*+,./:;<=>?[\\]|";
+
+ int i, j;
+ int namelen = 0, dots = 0, extlen = 0;
+
+ memset (dest, ' ', 11);
+
+ if (*src == '\0' || *src == '.') {
+ return -1;
+ }
+
+ for (j = i = 0; *src != '\0'; i++) {
+
+ int c = (unsigned char) *src++;
+
+ if (c == '/' || c == '\\') {
+ break;
+ }
+
+ if (i >= 12) {
+ return -1;
+ }
+
+ if (i == 0 && c == 0xE5) {
+
+ /**
+ * 0xE5 in the first character of the name is a marker for delected files,
+ * it needs to be translated to 0x05.
+ */
+ c = 0x05;
+
+ } else if (c == '.') {
+
+ if (dots++) {
+ return -1;
+ }
+
+ j = 8;
+ continue;
+
+ }
+
+ if (c <= 0x20 || strchr (invalid_chars, c)) {
+ return -1;
+ }
+
+ if (dots) {
+
+ if (++extlen > 3) {
+ return -1;
+ }
+
+ } else {
+
+ if (++namelen > 8) {
+ return -1;
+ }
+
+ }
+
+ if (c >= 'a' && c <= 'z') {
+ c -= 'a' - 'A';
+ }
+
+ dest[j++] = c;
+
+ }
+
+ return 0;
+
+}
+
+static int copy_file (const char *source, struct file_info *fi, const char *fname) {
+
+ struct msdos_dirent de;
+ struct fat32_fsinfo *info;
+
+ unsigned int free_clusters;
+ unsigned int next_cluster;
+
+ FILE *ifp;
+ long bytes;
+
+ unsigned char *buffer, *temp;
+ unsigned int i, j, sector;
+
+ unsigned int clust_size = sectors_per_cluster * 512;
+ unsigned int start_cluster = 0, prev_cluster = 0, size = 0;
+
+ unsigned long flen, copied = 0;
+ unsigned int num_clusters = 0, *cluster_chain;
+
+ if ((ifp = fopen (source, "rb")) == NULL) {
+ return -1;
+ }
+
+ fseek (ifp, 0, SEEK_END);
+
+ if ((flen = ftell (ifp)) > UINT_MAX) {
+
+ fclose (ifp);
+ return -1;
+
+ }
+
+ if (!(cluster_chain = malloc (cluster_count * sizeof (unsigned int)))) {
+
+ fclose (ifp);
+ return -1;
+
+ }
+
+ num_clusters = (flen + clust_size - 1) / clust_size;
+ memset (cluster_chain, 0, cluster_count * sizeof (unsigned int));
+
+ for (i = 2, j = 0; i < cluster_count && j < num_clusters; i++) {
+
+ if (get_fat_entry (fi->scratch, i) == 0) {
+ cluster_chain[j++] = i;
+ }
+
+ }
+
+ if (j < num_clusters) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Not enough free space available");
+ free (cluster_chain);
+
+ fclose (ifp);
+ return -1;
+
+ }
+
+ fseek (ifp, 0, SEEK_SET);
+
+ if (!(buffer = (unsigned char *) malloc (clust_size))) {
+
+ free (cluster_chain);
+
+ fclose (ifp);
+ return -1;
+
+ }
+
+ memset (buffer, 0, clust_size);
+ j = 0;
+
+ while ((bytes = fread (buffer, 1, clust_size, ifp)) > 0) {
+
+ size += bytes;
+ i = cluster_chain[j++];
+
+ if (i == cluster_count) {
+
+ free (cluster_chain);
+ free (buffer);
+
+ fclose (ifp);
+ return -1;
+
+ }
+
+ if (start_cluster == 0) {
+
+ start_cluster = i;
+
+ if (seekto ((unsigned long) fi->dir_sector * 512) || fread (fi->scratch, 512, 1, ofp) != 1) {
+
+ free (cluster_chain);
+ free (buffer);
+
+ fclose (ifp);
+ return -1;
+
+ }
+
+ de = (((struct msdos_dirent *) fi->scratch)[fi->dir_offset]);
+
+ write721_to_byte_array (de.startlo, start_cluster);
+ write721_to_byte_array (de.starthi, start_cluster >> 16);
+
+ memcpy (&(((struct msdos_dirent *) fi->scratch)[fi->dir_offset]), &de, sizeof (de));
+
+ if (seekto ((unsigned long) fi->dir_sector * 512) || fwrite (fi->scratch, 512, 1, ofp) != 1) {
+
+ free (cluster_chain);
+ free (buffer);
+
+ fclose (ifp);
+ return -1;
+
+ }
+
+ } else {
+
+ if (prev_cluster == 0) {
+
+ free (cluster_chain);
+ free (buffer);
+
+ fclose (ifp);
+ return -1;
+
+ }
+
+ if (set_fat_entry (fi->scratch, prev_cluster, i) < 0) {
+
+ free (cluster_chain);
+ free (buffer);
+
+ fclose (ifp);
+ return -1;
+
+ }
+
+ }
+
+ sector = data_area + ((i - 2) * sectors_per_cluster);
+
+ if (seekto ((unsigned long) sector * 512)) {
+
+ free (cluster_chain);
+ free (buffer);
+
+ fclose (ifp);
+ return -1;
+
+ }
+
+ temp = buffer;
+
+ while (bytes > 0) {
+
+ if (fwrite (temp, 512, 1, ofp) != 1) {
+
+ free (cluster_chain);
+ free (buffer);
+
+ fclose (ifp);
+ return -1;
+
+ }
+
+ if (bytes > 512) {
+ copied += 512;
+ } else {
+ copied += bytes;
+ }
+
+ if (state->status) {
+
+ double percent = (double) copied / (double) flen;
+ printf ("\rcopied %lu bytes (%.2f%%) to %s", copied, percent * 100, fname);
+
+ }
+
+ bytes -= 512;
+ temp += 512;
+
+ }
+
+ memset (buffer, 0, clust_size);
+
+ if (set_fat_entry (fi->scratch, i, 0x0FFFFFF8) < 0) {
+
+ free (cluster_chain);
+ free (buffer);
+
+ fclose (ifp);
+ return -1;
+
+ }
+
+ prev_cluster = i;
+ if (feof (ifp)) { break; }
+
+ }
+
+ free (cluster_chain);
+ free (buffer);
+
+ fclose (ifp);
+
+ if (seekto ((unsigned long) fi->dir_sector * 512) || fread (fi->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ if (size_fat == 32) {
+
+ if (seekto ((unsigned long) info_sector * 512) || fread (fi->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ info = (struct fat32_fsinfo *) (fi->scratch + 0x1e0);
+
+ free_clusters = (unsigned int) info->free_clusters[0] | (((unsigned int) info->free_clusters[1]) << 8) | (((unsigned int) info->free_clusters[2]) << 16) | (((unsigned int) info->free_clusters[3]) << 24);
+ next_cluster = (unsigned int) info->next_cluster[0] | (((unsigned int) info->next_cluster[1]) << 8) | (((unsigned int) info->next_cluster[2]) << 16) | (((unsigned int) info->next_cluster[3]) << 24);
+
+ free_clusters -= num_clusters;
+ next_cluster += num_clusters;
+
+ write741_to_byte_array (info->free_clusters, free_clusters);
+ write741_to_byte_array (info->next_cluster, next_cluster);
+
+ if (seekto ((unsigned long) info_sector * 512) || fwrite (fi->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ }
+
+ if (seekto ((unsigned long) fi->dir_sector * 512) || fread (fi->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ de = (((struct msdos_dirent *) fi->scratch)[fi->dir_offset]);
+
+ write741_to_byte_array (de.size, size);
+ memcpy (&(((struct msdos_dirent *) fi->scratch)[fi->dir_offset]), &de, sizeof (de));
+
+ if (seekto ((unsigned long) fi->dir_sector * 512) || fwrite (fi->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ return 0;
+
+}
+
+static int get_free_dirent (const char *path, struct dir_info *di, struct msdos_dirent *de) {
+
+ int entry;
+ unsigned int i, tempclust;
+
+ if (open_dir (path, di) < 0) {
+ return -1;
+ }
+
+ entry = 0;
+ di->flags |= 0x01;
+
+ do {
+
+ entry = get_next_entry (di, de);
+
+ /*if (entry == 0 && (!de->name[0] || de->name[0] == 0xE5 || (de->attr & ATTR_LONG_NAME) == ATTR_LONG_NAME)) {*/
+ if (entry == 0 && (!de->name[0] || de->name[0] == 0xE5)) {
+
+ /*if (de->name[0] == 0xE5 || (de->attr & ATTR_LONG_NAME) == ATTR_LONG_NAME) {*/
+ if (de->name[0] == 0xE5) {
+ di->current_entry--;
+ }
+
+ return 0;
+
+ } else if (entry == -1) {
+ return -1;
+ } else if (entry == 1) {
+
+ if ((tempclust = get_free_fat (di->scratch) == 0x0FFFFFF7)) {
+ return -1;
+ }
+
+ memset (di->scratch, 0, 512);
+
+ for (i = 0; i < sectors_per_cluster; i++) {
+
+ unsigned long offset = (unsigned long) (data_area + ((tempclust - 2) * sectors_per_cluster) + i);
+
+ if (seekto (offset * 512)) {
+ return -1;
+ }
+
+ if (fwrite (di->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ }
+
+ if (set_fat_entry (di->scratch, di->current_cluster, tempclust) < 0) {
+ return -1;
+ }
+
+ di->current_cluster = tempclust;
+ di->current_entry = 0;
+ di->current_sector = 0;
+
+ if (size_fat == 12) {
+ tempclust = 0x0FF8;
+ } else if (size_fat == 16) {
+ tempclust = 0xFFF8;
+ } else if (size_fat == 32) {
+
+ struct fat32_fsinfo *info;
+
+ unsigned int free_clusters;
+ unsigned int next_cluster;
+
+ tempclust = 0x0ffffff8;
+
+ if (seekto ((unsigned long) info_sector * 512) || fread (di->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ info = (struct fat32_fsinfo *) (di->scratch + 0x1e0);
+
+ free_clusters = (unsigned int) info->free_clusters[0] | (((unsigned int) info->free_clusters[1]) << 8) | (((unsigned int) info->free_clusters[2]) << 16) | (((unsigned int) info->free_clusters[3]) << 24);
+ next_cluster = (unsigned int) info->next_cluster[0] | (((unsigned int) info->next_cluster[1]) << 8) | (((unsigned int) info->next_cluster[2]) << 16) | (((unsigned int) info->next_cluster[3]) << 24);
+
+ free_clusters--;
+ next_cluster++;
+
+ write741_to_byte_array (info->free_clusters, free_clusters);
+ write741_to_byte_array (info->next_cluster, next_cluster);
+
+ if (seekto ((unsigned long) info_sector * 512) || fwrite (di->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ } else {
+ return -1;
+ }
+
+ }
+
+ } while (!entry);
+
+ /* We should't get here! */
+ return -1;
+
+}
+
+static int get_next_entry (struct dir_info *di, struct msdos_dirent *de) {
+
+ unsigned int tempclust;
+ unsigned long offset;
+
+ if (di->current_entry >= 512 / sizeof (*de)) {
+
+ di->current_entry = 0;
+ di->current_sector++;
+
+ if (di->current_cluster == 0) {
+
+ unsigned long offset;
+
+ if (di->current_sector * (512 / sizeof (*de)) >= root_entries) {
+ return -1;
+ }
+
+ offset = (unsigned long) root_dir + di->current_sector;
+
+ if (seekto (offset * 512) || fread (di->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ } else {
+
+ if (di->current_sector >= sectors_per_cluster) {
+
+ di->current_sector = 0;
+
+ if ((size_fat == 12 && di->current_cluster >= 0x0FF7) || (size_fat == 16 && di->current_cluster >= 0xFFF7) || (size_fat == 32 && di->current_cluster >= 0x0FFFFFF7)) {
+
+ if (!(di->flags & 0x01)) {
+ return -1;
+ }
+
+ return 1;
+
+ }
+
+ tempclust = get_fat_entry (di->scratch, di->current_cluster);
+
+ if (size_fat == 12 && tempclust < 0x0FF8) {
+ di->current_cluster = tempclust;
+ } else if (size_fat == 16 && tempclust < 0xFFF8) {
+ di->current_cluster = tempclust;
+ } else if (size_fat == 32 && tempclust < 0x0FFFFFF8) {
+ di->current_cluster = tempclust;
+ } else {
+
+ tempclust = get_free_fat (di->scratch);
+
+ if (set_fat_entry (di->scratch, di->current_cluster, tempclust) < 0) {
+ return -1;
+ }
+
+ di->current_cluster = tempclust;
+
+ if (set_fat_entry (di->scratch, di->current_cluster, 0x0FFFFFF8) < 0) {
+ return -1;
+ }
+
+ }
+
+ }
+
+ offset = (unsigned long) data_area;
+ offset += ((di->current_cluster - 2) * sectors_per_cluster);
+ offset += di->current_sector;
+
+ if (seekto (offset * 512) || fread (di->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ }
+
+ }
+
+ memcpy (de, &(((struct msdos_dirent *) di->scratch)[di->current_entry]), sizeof (*de));
+
+ if (de->name[0] == 0) {
+
+ if (di->flags & 0x01) {
+ return 0;
+ }
+
+ return -1;
+
+ }
+
+ if (de->name[0] == 0x05) {
+
+ de->name[0] = 0xE5;
+ return 0;
+
+ }
+
+ di->current_entry++;
+ return 0;
+
+}
+
+static int open_dir (const char *target, struct dir_info *di) {
+
+ di->flags = 0;
+
+ if (!strlen ((char *) target) || (strlen ((char *) target) == 1 && (target[0] == '/' || target[0] == '\\'))) {
+
+ unsigned long offset;
+
+ if (size_fat == 32) {
+
+ di->current_cluster = root_cluster;
+ di->current_entry = 0;
+ di->current_sector = 0;
+
+ offset = (unsigned long) data_area + ((di->current_cluster - 2) * sectors_per_cluster);
+
+ } else {
+
+ di->current_cluster = 0;
+ di->current_entry = 0;
+ di->current_sector = 0;
+
+ offset = (unsigned long) root_dir;
+
+ }
+
+ if (seekto (offset * 512) || fread (di->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ } else {
+
+ unsigned char tmpfn[12];
+ unsigned char *ptr;
+
+ struct msdos_dirent de;
+ unsigned int result;
+
+ unsigned long offset;
+
+ if (size_fat == 32) {
+
+ di->current_cluster = root_cluster;
+ di->current_entry = 0;
+ di->current_sector = 0;
+
+ offset = (unsigned long) data_area + ((di->current_cluster - 2) * sectors_per_cluster);
+
+ } else {
+
+ di->current_cluster = 0;
+ di->current_entry = 0;
+ di->current_sector = 0;
+
+ offset = (unsigned long) root_dir;
+
+ }
+
+ if (seekto (offset * 512) || fread (di->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ ptr = (unsigned char *) target;
+
+ while ((*ptr == '/' || *ptr == '\\') && *ptr) {
+ ptr++;
+ }
+
+ while (*ptr) {
+
+ if (canonical_to_dir ((char *) tmpfn, (char *) ptr) < 0) {
+
+ fprintf (stderr, "Failed to convert to 8:3\n");
+ return -1;
+
+ }
+
+ de.name[0] = 0;
+
+ do {
+ result = get_next_entry (di, &de);
+ } while (!result && memcmp (de.name, tmpfn, 11));
+
+ if (!memcmp (de.name, tmpfn, 11) && (de.attr & ATTR_DIR) == ATTR_DIR) {
+
+ unsigned long offset;
+
+ di->current_cluster = (unsigned int) de.startlo[0] | ((unsigned int) de.startlo[1]) << 8 | ((unsigned int) de.starthi[0]) << 16 | ((unsigned int) de.starthi[1]) << 24;
+ di->current_entry = 0;
+ di->current_sector = 0;
+
+ offset = (unsigned long) (data_area + ((di->current_cluster - 2) * sectors_per_cluster));
+
+ if (seekto (offset * 512)) {
+ return -1;
+ }
+
+ if (fread (di->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ } else if (!memcmp (de.name, tmpfn, 11) && !(de.attr & ATTR_DIR)) {
+ return -1;
+ }
+
+ while (*ptr != '/' && *ptr != '\\' && *ptr) {
+ ptr++;
+ }
+
+ if (*ptr == '/' || *ptr == '\\') {
+ ptr++;
+ }
+
+ }
+
+ if (!di->current_cluster) {
+ return -1;
+ }
+
+ }
+
+ return 0;
+
+}
+
+static int open_file (const char *target, unsigned char *scratch, struct file_info *fi) {
+
+ unsigned char tmppath[PATH_MAX];
+ unsigned char filename[12];
+ unsigned char *p;
+
+ struct dir_info di;
+ struct msdos_dirent de;
+
+ unsigned short date;
+ unsigned short time;
+
+ unsigned int tempclust;
+
+ /* Zero out file structure. */
+ memset (fi, 0, sizeof (*fi));
+
+ /* Get a local copy of the target. If it's larger than PATH_MAX, abort. */
+ strncpy ((char *) tmppath, (char *) target, PATH_MAX);
+ tmppath[PATH_MAX - 1] = 0;
+
+ if (strcmp ((char *) target, (char *) tmppath)) {
+ return -1;
+ }
+
+ /* Strip leading seperators. */
+ while (tmppath[0] == '/' || tmppath[0] == '\\') {
+ strcpy ((char *) tmppath, (char *) tmppath + 1);
+ }
+
+ /* Parse filename off the end of the suppiled target. */
+ p = tmppath;
+
+ while (*(p++));
+
+ p--;
+
+ while (p > tmppath && *p != '/' && *p != '\\') {
+ p--;
+ }
+
+ if (*p == '/' || *p == '\\') {
+ p++;
+ }
+
+ if (canonical_to_dir ((char *) filename, (char *) p) < 0) {
+
+ fprintf (stderr, "Failed to convert to 8:3\n");
+ return -1;
+
+ }
+
+ if (p > tmppath) {
+ p--;
+ }
+
+ if (*p == '/' || *p == '\\' || p == tmppath) {
+ *p = 0;
+ }
+
+ di.scratch = scratch;
+
+ if (open_dir ((char *) tmppath, &di) < 0) {
+
+ fprintf (stderr, "Failed to open directory\n");
+ return -1;
+
+ }
+
+ while (!get_next_entry (&di, &de)) {
+
+ if (!memcmp (de.name, filename, 11)) {
+
+ di.current_entry--;
+
+ if (de.attr & ATTR_DIR) {
+ return -1;
+ }
+
+ if (!check_overwrite ((char *) target)) {
+ return 0;
+ }
+
+ fi->cluster = (unsigned int) de.startlo[0] | ((unsigned int) de.startlo[1]) << 8 | ((unsigned int) de.starthi[0]) << 16 | ((unsigned int) de.starthi[1]) << 24;
+
+ for (;;) {
+
+ if (size_fat == 12 && fi->cluster >= 0x0FF8) {
+ break;
+ } else if (size_fat == 16 && fi->cluster >= 0xFFF8) {
+ break;
+ } else if (size_fat == 32 && fi->cluster >= 0x0FFFFFF8) {
+ break;
+ }
+
+ tempclust = get_fat_entry (scratch, fi->cluster);
+
+ if (set_fat_entry (scratch, fi->cluster, 0) < 0) {
+ return -1;
+ }
+
+ fi->cluster = tempclust;
+
+ if (!fi->cluster || fi->cluster == 0x0FFFFFFF7) {
+ break;
+ }
+
+ }
+
+ fi->cluster = 0;
+
+ if (seekto ((unsigned long) fi->dir_sector * 512) || fread (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ date = generate_datestamp ();
+ time = generate_timestamp ();
+
+ memset (&de, 0, sizeof (de));
+ memcpy (de.name, filename, 11);
+
+ de.attr = ATTR_ARCHIVE;
+
+ write721_to_byte_array (de.ctime, time);
+ write721_to_byte_array (de.cdate, date);
+ write721_to_byte_array (de.adate, date);
+ write721_to_byte_array (de.time, time);
+ write721_to_byte_array (de.date, date);
+
+ fi->pointer = 0;
+
+ if (di.current_cluster == 0) {
+ fi->dir_sector = root_dir + di.current_sector;
+ } else {
+ fi->dir_sector = data_area + ((di.current_cluster - 2) * sectors_per_cluster) + di.current_sector;
+ }
+
+ /*fi->dir_offset = di.current_entry - 1;*/
+ fi->dir_offset = di.current_entry;
+
+ if (seekto ((unsigned long) fi->dir_sector * 512) || fread (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ memcpy (&(((struct msdos_dirent *) scratch)[fi->dir_offset]), &de, sizeof (de));
+
+ if (seekto ((unsigned long) fi->dir_sector * 512) || fwrite (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ return 0;
+
+ }
+
+ }
+
+ if (get_free_dirent ((char *) tmppath, &di, &de) < 0) {
+ return -1;
+ }
+
+ date = generate_datestamp ();
+ time = generate_timestamp ();
+
+ memset (&de, 0, sizeof (de));
+ memcpy (de.name, filename, 11);
+
+ de.attr = ATTR_ARCHIVE;
+
+ write721_to_byte_array (de.ctime, time);
+ write721_to_byte_array (de.cdate, date);
+ write721_to_byte_array (de.adate, date);
+ write721_to_byte_array (de.time, time);
+ write721_to_byte_array (de.date, date);
+
+ fi->pointer = 0;
+
+ if (di.current_cluster == 0) {
+ fi->dir_sector = root_dir + di.current_sector;
+ } else {
+ fi->dir_sector = data_area + ((di.current_cluster - 2) * sectors_per_cluster) + di.current_sector;
+ }
+
+ /*fi->dir_offset = di.current_entry - 1;*/
+ fi->dir_offset = di.current_entry;
+
+ if (seekto ((unsigned long) fi->dir_sector * 512) || fread (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ memcpy (&(((struct msdos_dirent *) scratch)[fi->dir_offset]), &de, sizeof (de));
+
+ if (seekto ((unsigned long) fi->dir_sector * 512) || fwrite (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ return 0;
+
+}
+
+static int set_fat_entry (unsigned char *scratch, unsigned int cluster, unsigned int value) {
+
+ unsigned int i, offset, sector;
+
+ if (size_fat == 12) {
+
+ offset = cluster + (cluster / 2);
+ value &= 0x0fff;
+
+ } else if (size_fat == 16) {
+
+ offset = cluster * 2;
+ value &= 0xffff;
+
+ } else if (size_fat == 32) {
+
+ offset = cluster * 4;
+ value &= 0x0fffffff;
+
+ } else {
+ return -1;
+ }
+
+ /**
+ * At this point, offset is the BYTE offset of the desired sector from the start
+ * of the FAT. Calculate the physical sector containing this FAT entry.
+ */
+ sector = (offset / 512) + reserved_sectors;
+
+ if (seekto (sector * 512) || fread (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ /**
+ * At this point, we "merely" need to extract the relevant entry. This is
+ * easy for FAT16 and FAT32, but a royal PITA for FAT12 as a single entry
+ * may span a sector boundary. The normal way around this is always to
+ * read two FAT sectors, but luxary is (by design intent) unavailable.
+ */
+ offset %= 512;
+
+ if (size_fat == 12) {
+
+ if (offset == 511) {
+
+ if (((cluster * 3) & 0x01) == 0) {
+ scratch[offset] = (unsigned char) (value & 0xFF);
+ } else {
+ scratch[offset] = (unsigned char) ((scratch[offset] & 0x0F) | ((value & 0x0F) << 4));
+ }
+
+ for (i = 0; i < number_of_fats; i++) {
+
+ unsigned long temp = sector + (i * sectors_per_fat);
+
+ if (seekto (temp * 512) < 0 || fwrite (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ }
+
+ sector++;
+
+ if (seekto (sector * 512) || fread (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ if (((cluster * 3) & 0x01) == 0) {
+ scratch[0] = (unsigned char) ((scratch[0] & 0xF0) | ((value & 0x0F00) >> 8));
+ } else {
+ scratch[0] = (unsigned char) ((value & 0x0FF0) >> 4);
+ }
+
+ goto _write_fat;
+
+ } else {
+
+ if (((cluster * 3) & 0x01) == 0) {
+
+ scratch[offset] = (unsigned char) (value & 0x00FF);
+ scratch[offset + 1] = (unsigned char) ((scratch[offset + 1] & 0x00F0) | ((value & 0x0F00) >> 8));
+
+ } else {
+
+ scratch[offset] = (unsigned char) ((scratch[offset] & 0x000F) | ((value & 0x000F) << 4));
+ scratch[offset + 1] = (unsigned char) ((value & 0x0FF0) >> 4);
+
+ }
+
+ goto _write_fat;
+
+ }
+
+ } else if (size_fat == 16) {
+
+ scratch[offset] = (value & 0xFF);
+ scratch[offset + 1] = (value >> 8) & 0xFF;
+
+ goto _write_fat;
+
+ } else if (size_fat == 32) {
+
+ scratch[offset] = (value & 0xFF);
+ scratch[offset + 1] = (value >> 8) & 0xFF;
+ scratch[offset + 2] = (value >> 16) & 0xFF;
+ scratch[offset + 3] = (scratch[offset + 3] & 0xF0) | ((value >> 24) & 0xFF);
+
+ goto _write_fat;
+
+ }
+
+ return -1;
+
+_write_fat:
+
+ for (i = 0; i < number_of_fats; i++) {
+
+ unsigned long temp = sector + (i * sectors_per_fat);
+
+ if (seekto (temp * 512) || fwrite (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ }
+
+ return 0;
+
+}
+
+static unsigned int get_fat_entry (unsigned char *scratch, unsigned int cluster) {
+
+ unsigned int offset, sector, result;
+
+ if (size_fat == 12) {
+ offset = cluster + (cluster / 2);
+ } else if (size_fat == 16) {
+ offset = cluster * 2;
+ } else if (size_fat == 32) {
+ offset = cluster * 4;
+ } else {
+ return 0x0FFFFFF7;
+ }
+
+ sector = (offset / 512) + reserved_sectors;
+
+ if (seekto ((unsigned long) sector * 512) || fread (scratch, 512, 1, ofp) != 1) {
+ return 0x0FFFFFF7;
+ }
+
+ offset %= 512;
+
+ if (size_fat == 12) {
+
+ if (offset == 511) {
+
+ result = (unsigned int) scratch[offset];
+ sector++;
+
+ if (seekto ((unsigned long) sector * 512) || fread (scratch, 512, 1, ofp) != 1) {
+ return 0x0FFFFFF7;
+ }
+
+ result |= ((unsigned int) scratch[0]) << 8;
+
+ } else {
+ result = (unsigned int) scratch[offset] | ((unsigned int) scratch[offset + 1]) << 8;
+ }
+
+ if (cluster & 1) {
+ result = result >> 4;
+ } else {
+ result = result & 0x0FFF;
+ }
+
+ } else if (size_fat == 16) {
+ result = (unsigned int) scratch[offset] | ((unsigned int) scratch[offset + 1]) << 8;
+ } else if (size_fat == 32) {
+ result = ((unsigned int) scratch[offset] | ((unsigned int) scratch[offset + 1]) << 8 | ((unsigned int) scratch[offset + 2]) << 16 | ((unsigned int) scratch[offset + 3]) << 24) & 0x0FFFFFFF;
+ } else {
+ result = 0x0FFFFFF7;
+ }
+
+ return result;
+
+}
+
+static unsigned int get_free_fat (unsigned char *scratch) {
+
+ unsigned int i, result;
+
+ for (i = 2; i < cluster_count; i++) {
+
+ if (!(result = get_fat_entry (scratch, i))) {
+ return i;
+ }
+
+ }
+
+ return 0x0FFFFFF7;
+
+}
+
+
+int get_file (const char *source, unsigned char *scratch, struct file_info *fi) {
+
+ unsigned char tmppath[PATH_MAX];
+ unsigned char filename[12];
+ unsigned char *p;
+
+ struct dir_info di;
+ struct msdos_dirent de;
+
+ /* Zero out file structure. */
+ memset (fi, 0, sizeof (*fi));
+
+ /* Get a local copy of the target. If it's larger than PATH_MAX, abort. */
+ strncpy ((char *) tmppath, (char *) source, PATH_MAX);
+ tmppath[PATH_MAX - 1] = 0;
+
+ if (strcmp ((char *) source, (char *) tmppath)) {
+ return -1;
+ }
+
+ /* Strip leading seperators. */
+ while (tmppath[0] == '/' || tmppath[0] == '\\') {
+ strcpy ((char *) tmppath, (char *) tmppath + 1);
+ }
+
+ /* Parse filename off the end of the suppiled target. */
+ p = tmppath;
+
+ while (*(p++));
+
+ p--;
+
+ while (p > tmppath && *p != '/' && *p != '\\') {
+ p--;
+ }
+
+ if (*p == '/' || *p == '\\') {
+ p++;
+ }
+
+ if (canonical_to_dir ((char *) filename, (char *) p) < 0) {
+
+ fprintf (stderr, "Failed to convert to 8:3\n");
+ return -1;
+
+ }
+
+ if (p > tmppath) {
+ p--;
+ }
+
+ if (*p == '/' || *p == '\\' || p == tmppath) {
+ *p = 0;
+ }
+
+ di.scratch = scratch;
+
+ if (open_dir ((char *) tmppath, &di) < 0) {
+
+ fprintf (stderr, "Failed to open directory\n");
+ return -1;
+
+ }
+
+ while (!get_next_entry (&di, &de)) {
+
+ if (!memcmp (de.name, filename, 11)) {
+
+ di.current_entry--;
+
+ if (de.attr & ATTR_DIR) {
+ return -1;
+ }
+
+ fi->cluster = (unsigned int) de.startlo[0] | ((unsigned int) de.startlo[1]) << 8 | ((unsigned int) de.starthi[0]) << 16 | ((unsigned int) de.starthi[1]) << 24;
+ fi->bytes = (unsigned int) de.size[0] | ((unsigned int) de.size[1]) << 8 | ((unsigned int) de.size[2]) << 16 | ((unsigned int) de.size[3]) << 24;
+
+ fi->scratch = scratch;
+ return 0;
+
+ }
+
+ }
+
+ return -1;
+
+}
+
+int copy_from_image (const char *target, struct file_info *fi, const char *fname) {
+
+ unsigned long sector, i, copied = 0;
+ FILE *tfp;
+
+ unsigned long flen = (unsigned long) fi->bytes;
+
+ if ((tfp = fopen (target, "rb")) == NULL) {
+
+ if ((tfp = fopen (target, "wb")) == NULL) {
+ return -1;
+ }
+
+ } else {
+
+ if (!check_overwrite ((char *) target)) {
+ return 0;
+ }
+
+ fclose (tfp);
+
+ if ((fopen (target, "wb")) == NULL) {
+ return -1;
+ }
+
+ }
+
+ for (;;) {
+
+ if (size_fat == 12 && fi->cluster >= 0x0FF8) {
+ break;
+ } else if (size_fat == 16 && fi->cluster >= 0xFFF8) {
+ break;
+ } else if (size_fat == 32 && fi->cluster >= 0x0FFFFFF8) {
+ break;
+ }
+
+ sector = data_area + ((fi->cluster - 2) * sectors_per_cluster);
+
+ for (i = 0; i < sectors_per_cluster; ++i) {
+
+ if (seekto (sector * 512) || fread (fi->scratch, 512, 1, ofp) != 1) {
+
+ fclose (tfp);
+ remove (target);
+
+ return -1;
+
+ }
+
+ if (fi->bytes > 512) {
+
+ if (fwrite (fi->scratch, 512, 1, tfp) != 1) {
+
+ fclose (tfp);
+ remove (target);
+
+ return -1;
+
+ }
+
+ copied += 512;
+
+ if (state->status) {
+
+ double percent = (double) copied / (double) flen;
+ printf ("\rcopied %lu bytes (%.2f%%) to %s", copied, percent * 100, fname);
+
+ }
+
+ fi->bytes -= 512;
+
+ } else {
+
+ if (fwrite (fi->scratch, fi->bytes, 1, tfp) != 1) {
+
+ fclose (tfp);
+ remove (target);
+
+ return -1;
+
+ }
+
+ fclose (tfp);
+ return 0;
+
+ }
+
+ sector++;
+
+ }
+
+ fi->cluster = get_fat_entry (fi->scratch, fi->cluster);
+
+ if (!fi->cluster || fi->cluster == 0x0FFFFFFF7) {
+ break;
+ }
+
+ }
+
+ fclose (tfp);
+ return 0;
+
+}
+
+struct vhd_footer {
+
+ unsigned char cookie[8];
+ unsigned char features[4];
+
+ struct {
+
+ unsigned char major[2];
+ unsigned char minor[2];
+
+ } version;
+
+ unsigned char next_offset[8];
+ unsigned char modified_time[4];
+ unsigned char creator_name[4];
+
+ struct {
+
+ unsigned char major[2];
+ unsigned char minor[2];
+
+ } creator_version;
+
+ unsigned char creator_host[4];
+ unsigned char disk_size[8];
+ unsigned char data_size[8];
+
+ struct {
+
+ unsigned char cylinders[2];
+ unsigned char heads_per_cyl;
+ unsigned char secs_per_track;
+
+ } disk_geom;
+
+ unsigned char disk_type[4];
+ unsigned char checksum[4];
+ unsigned char identifier[16];
+ unsigned char saved_state;
+ unsigned char reserved[427];
+
+};
+
+int main (int argc, char **argv) {
+
+ unsigned char *scratch;
+ char *target, tmppath[PATH_MAX], *source;
+
+ struct vhd_footer footer;
+ long i;
+
+ int copy_from = 1;
+
+ if (argc && *argv) {
+
+ char *p;
+ program_name = *argv;
+
+ if ((p = strrchr (program_name, '/')) || (p = strrchr (program_name, '\\'))) {
+ program_name = (p + 1);
+ }
+
+ }
+
+ state = xmalloc (sizeof (*state));
+ parse_args (&argc, &argv, 1);
+
+ if (!state->outfile || state->nb_files < 2) {
+ print_help (EXIT_FAILURE);
+ }
+
+ target = state->files[state->nb_files - 1];
+ state->nb_files--;
+
+ if (*target == ':') {
+
+ ++target;
+
+ if (*target == ':') {
+
+ copy_from = 0;
+ ++target;
+
+ }
+
+ }
+
+ if (!*target) {
+ target = "/";
+ }
+
+ if ((ofp = fopen (state->outfile, "r+b")) == NULL) {
+
+ report_at (program_name, 0, REPORT_ERROR, "faild to open '%s' for writing", state->outfile);
+ return EXIT_FAILURE;
+
+ }
+
+ if (state->chs_align) {
+
+ long size = sizeof (footer), offset;
+ fseek (ofp, 0, SEEK_END);
+
+ offset = ftell (ofp) - size;
+
+ if (!fseek (ofp, offset, SEEK_SET)) {
+
+ if (fread (&footer, size, 1, ofp) == 1) {
+
+ if (footer.cookie[0] == 0x63 && footer.cookie[1] == 0x6F && footer.cookie[2] == 0x6E && footer.cookie[3] == 0x65 && footer.cookie[4] == 0x63 && footer.cookie[5] == 0x74 && footer.cookie[6] == 0x69 && footer.cookie[7] == 0x78) {
+
+ long old = state->offset, secs_per_track = footer.disk_geom.secs_per_track;
+
+ if (state->offset % secs_per_track) {
+
+ state->offset = ((state->offset / secs_per_track) + 1) * secs_per_track;
+ report_at (program_name, 0, REPORT_WARNING, "offset changed from %lu to %lu", old, state->offset);
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ if (seekto ((unsigned long) 0) || fread (&bs, sizeof (bs), 1, ofp) != 1) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed whilst reading boot sector");
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ if (bs.boot_jump[0] != 0xEB || bs.boot_jump[1] < 0x16 || bs.boot_jump[2] != 0x90) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ sectors_per_cluster = (unsigned int) bs.sectors_per_cluster;
+ reserved_sectors = (unsigned int) bs.reserved_sectors[0] | (((unsigned int) bs.reserved_sectors[1]) << 8);
+ number_of_fats = (unsigned int) bs.no_fats;
+ root_entries = (unsigned int) bs.root_entries[0] | (((unsigned int) bs.root_entries[1]) << 8);
+ total_sectors = (unsigned int) bs.total_sectors16[0] | (((unsigned int) bs.total_sectors16[1]) << 8);
+ sectors_per_fat = (unsigned int) bs.sectors_per_fat16[0] | (((unsigned int) bs.sectors_per_fat16[1]) << 8);
+
+ if (!sectors_per_cluster || !reserved_sectors || !number_of_fats) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ if (!root_entries) {
+
+ if (bs.boot_jump[1] < 0x58) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ root_cluster = (unsigned int) bs.fat32.root_cluster[0] | (((unsigned int) bs.fat32.root_cluster[1]) << 8) | (((unsigned int) bs.fat32.root_cluster[2]) << 16) | (((unsigned int) bs.fat32.root_cluster[3]) << 24);
+
+ if (!root_cluster) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ }
+
+ if (!total_sectors) {
+
+ if (bs.boot_jump[1] < 0x22) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ total_sectors = (unsigned int) bs.total_sectors32[0] | (((unsigned int) bs.total_sectors32[1]) << 8) | (((unsigned int) bs.total_sectors32[2]) << 16) | (((unsigned int) bs.total_sectors32[3]) << 24);
+
+ if (!total_sectors) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ }
+
+ if (!sectors_per_fat) {
+
+ if (bs.boot_jump[1] < 0x58) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ sectors_per_fat = (unsigned int) bs.fat32.sectors_per_fat32[0] | (((unsigned int) bs.fat32.sectors_per_fat32[1]) << 8) | (((unsigned int) bs.fat32.sectors_per_fat32[2]) << 16) | (((unsigned int) bs.fat32.sectors_per_fat32[3]) << 24);
+
+ if (!sectors_per_fat) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ }
+
+ if (root_entries) {
+
+ root_dir = reserved_sectors + (sectors_per_fat * 2);
+ data_area = root_dir + (((root_entries * 32) + (512 - 1)) / 512);
+
+ } else {
+
+ data_area = reserved_sectors + (sectors_per_fat * 2);
+
+ /*root_dir = data_area + ((root_cluster - 2) * sectors_per_cluster);*/
+ /*root_dir = root_cluster;*/
+
+ }
+
+ cluster_count = (total_sectors - data_area) / sectors_per_cluster;
+
+ if (bs.boot_jump[1] == 0x58) {
+
+ info_sector = (unsigned int) bs.fat32.info_sector[0] | (((unsigned int) bs.fat32.info_sector[1]) << 8);
+
+ if (!info_sector) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ size_fat = 32;
+
+ } else if (cluster_count <= MAX_CLUST_12) {
+ size_fat = 12;
+ } else if (cluster_count >= MIN_CLUST_16 && cluster_count <= MAX_CLUST_16) {
+ size_fat = 16;
+ } else {
+
+ report_at (program_name, 0, REPORT_ERROR, "FAT is not 12, 16 or 32 bits");
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ if (target && target[strlen (target) - 1] != '/' && target[strlen (target) - 1] != '\\') {
+
+ if (state->nb_files > 1) {
+
+ report_at (program_name, 0, REPORT_ERROR, "cannot copy multiple files to %s", target);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ }
+
+ if (!(scratch = (unsigned char *) malloc (512))) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Out of memory");
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ for (i = 0; i < state->nb_files; ++i) {
+
+ char *p, *ptr;
+ int need_fn = 0;
+
+ unsigned long pathlen;
+ struct file_info fi;
+
+ FILE *tmpfp;
+ source = state->files[i];
+
+ if (copy_from) {
+
+ if (memcmp (source, "::", 2)) {
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ print_help (EXIT_FAILURE);
+
+ }
+
+ source += 2;
+
+ }
+
+ if (*target == '/' || *target == '\\') {
+ target++;
+ }
+
+ pathlen = strlen (target);
+
+ if (target[pathlen - 1] == '/' || target[pathlen - 1] == '\\') {
+
+ need_fn = 1;
+ ptr = source;
+
+ if ((p = strrchr (ptr, '/')) || (p = strrchr (ptr, '\\'))) {
+ ptr = (p + 1);
+ }
+
+ pathlen += strlen (ptr);
+
+ }
+
+ if (pathlen > PATH_MAX) {
+
+ report_at (program_name, 0, REPORT_ERROR, "target too long");
+ free (scratch);
+
+ fclose (ofp);
+ return EXIT_FAILURE;
+
+ }
+
+ memset (tmppath, 0, PATH_MAX);
+ strncpy (tmppath, target, strlen (target));
+
+ if (need_fn) {
+ strncat (tmppath, ptr, strlen (ptr));
+ }
+
+ if (copy_from) {
+
+ if (get_file (source, scratch, &fi) < 0) {
+
+ report_at (program_name, 0, REPORT_ERROR, "file '%s' does not exist", source);
+ free (scratch);
+
+ fclose (ofp);
+ return EXIT_FAILURE;
+
+ }
+
+ if (copy_from_image (target, &fi, tmppath) < 0) {
+
+ if (state->status) {
+ printf ("\n");
+ }
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to copy %s", source);
+ free (scratch);
+
+ fclose (ofp);
+ return EXIT_FAILURE;
+
+ }
+
+ if (state->status) {
+ printf ("\n");
+ }
+
+ continue;
+
+ }
+
+ if (!(tmpfp = fopen (source, "rb"))) {
+
+ report_at (program_name, 0, REPORT_ERROR, "file not found - %s", source);
+ continue;
+
+ }
+
+ fclose (tmpfp);
+
+ if (open_file (tmppath, scratch, &fi) < 0) {
+
+ if (!target || *target == '\0') {
+
+ target = source;
+
+ if ((p = strrchr (target, '/')) || (p = strrchr (target, '\\'))) {
+ target = (p + 1);
+ }
+
+ }
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to create %s", target);
+ continue;
+
+ }
+
+ fi.scratch = scratch;
+
+ if (copy_file (source, &fi, tmppath) < 0) {
+
+ if (state->status) {
+ printf ("\n");
+ }
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to copy %s", source);
+ continue;
+
+ }
+
+ if (state->status) {
+ printf ("\n");
+ }
+
+ }
+
+ free (scratch);
+ fclose (ofp);
+
+ return (get_error_count () > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+
+}
--- /dev/null
+/******************************************************************************
+ * @file mcopy.h
+ *****************************************************************************/
+#ifndef _MCOPY_H
+#define _MCOPY_H
+
+struct mcopy_state {
+
+ char **files;
+ long nb_files;
+
+ int status;
+
+ const char *outfile;
+ size_t offset;
+
+ int chs_align;
+
+};
+
+#endif /* _MCOPY_H */
--- /dev/null
+/******************************************************************************
+ * @file mkfs.c
+ *****************************************************************************/
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "lib.h"
+#include "mkfs.h"
+#include "msdos.h"
+#include "report.h"
+#include "write7x.h"
+
+#ifndef __PDOS__
+# if defined (__GNUC__)
+# include <sys/time.h>
+# include <unistd.h>
+# else
+# include <io.h>
+# endif
+#endif
+
+static int align_structures = 1;
+
+static unsigned long image_size = 0;
+static FILE *ofp;
+
+static long total_sectors = 0;
+
+static int heads_per_cylinder = 255;
+static int sectors_per_track = 63;
+
+static unsigned int backup_boot = 0;
+static unsigned int cluster_count = 0;
+static unsigned int hidden_sectors = 0;
+static unsigned int info_sector = 0;
+static unsigned int media_descriptor = 0xf8;
+static unsigned int number_of_fats = 2;
+static unsigned int reserved_sectors = 0;
+static unsigned int root_cluster = 2;
+static unsigned int root_entries = 512;
+static unsigned int sectors_per_cluster = 4;
+static unsigned int sectors_per_fat = 0;
+
+struct mkfs_state *state = 0;
+const char *program_name = 0;
+
+static unsigned char dummy_boot_code[] =
+
+ "\x31\xC0" /* xor ax, ax */
+ "\xFA" /* cli */
+ "\x8E\xD0" /* mov ss, ax */
+ "\xBC\x00\x7C" /* mov sp, 0x7c00 */
+ "\xFB" /* sti */
+ "\x0E\x1F" /* push cs, pop ds */
+ "\xEB\x19" /* jmp XSTRING */
+
+ "\x5E" /* PRN: pop si */
+ "\xFC" /* cld */
+ "\xAC" /* XLOOP: lodsb */
+ "\x08\xC0" /* or al, al */
+ "\x74\x09" /* jz EOF */
+ "\xB4\x0E" /* mov ah, 0x0e */
+ "\xBB\x07\x00" /* mov bx, 7 */
+ "\xCD\x10" /* int 0x10 */
+ "\xEB\xF2" /* jmp short XLOOP */
+
+ "\x31\xC0" /* EOF: xor ax, ax */
+ "\xCD\x16" /* int 0x16 */
+ "\xCD\x19" /* int 0x19 */
+ "\xF4" /* HANG: hlt */
+ "\xEB\xFD" /* jmp short HANG */
+
+ "\xE8\xE4\xFF" /* XSTRING: call PRN */
+
+ "Non-System disk or disk read error\r\n"
+ "Replace and strike any key when ready\r\n";
+
+static int cdiv (int a, int b) {
+ return (a + b - 1) / b;
+}
+
+static int seekto (long offset) {
+ return fseek (ofp, (state->offset * 512) + offset, SEEK_SET);
+}
+
+static int set_fat_entry (unsigned int cluster, unsigned int value) {
+
+ unsigned char *scratch;
+ unsigned int i, offset, sector;
+
+ if (!(scratch = (unsigned char *) malloc (512))) {
+ return -1;
+ }
+
+ if (state->size_fat == 12) {
+
+ offset = cluster + (cluster / 2);
+ value &= 0x0fff;
+
+ } else if (state->size_fat == 16) {
+
+ offset = cluster * 2;
+ value &= 0xffff;
+
+ } else if (state->size_fat == 32) {
+
+ offset = cluster * 4;
+ value &= 0x0fffffff;
+
+ } else {
+
+ free (scratch);
+ return -1;
+
+ }
+
+ /**
+ * At this point, offset is the BYTE offset of the desired sector from the start
+ * of the FAT. Calculate the physical sector containing this FAT entry.
+ */
+ sector = (offset / 512) + reserved_sectors;
+
+ if (seekto (sector * 512)) {
+
+ free (scratch);
+
+ report_at (program_name, 0, REPORT_ERROR, "failed whilst seeking %s", state->outfile);
+ return -1;
+
+ }
+
+ if (fread (scratch, 512, 1, ofp) != 1) {
+
+ free (scratch);
+
+ report_at (program_name, 0, REPORT_ERROR, "failed whilst reading %s", state->outfile);
+ return -1;
+
+ }
+
+ /**
+ * At this point, we "merely" need to extract the relevant entry. This is
+ * easy for FAT16 and FAT32, but a royal PITA for FAT12 as a single entry
+ * may span a sector boundary. The normal way around this is always to
+ * read two FAT sectors, but luxary is (by design intent) unavailable.
+ */
+ offset %= 512;
+
+ if (state->size_fat == 12) {
+
+ if (offset == 511) {
+
+ if (((cluster * 3) & 0x01) == 0) {
+ scratch[offset] = (unsigned char) (value & 0xFF);
+ } else {
+ scratch[offset] = (unsigned char) ((scratch[offset] & 0x0F) | (value & 0xF0));
+ }
+
+ for (i = 0; i < number_of_fats; i++) {
+
+ long temp = sector + (i * sectors_per_fat);
+
+ if (seekto (temp * 512) || fwrite (scratch, 512, 1, ofp) != 1) {
+
+ free (scratch);
+ return -1;
+
+ }
+
+ }
+
+ sector++;
+
+ if (seekto (sector * 512) || fread (scratch, 512, 1, ofp) != 1) {
+
+ free (scratch);
+ return -1;
+
+ }
+
+ if (((cluster * 3) & 0x01) == 0) {
+ scratch[0] = (unsigned char) ((scratch[0] & 0xF0) | (value & 0x0F));
+ } else {
+ scratch[0] = (unsigned char) (value & 0xFF00);
+ }
+
+ goto _write_fat;
+
+ } else {
+
+ if (((cluster * 3) & 0x01) == 0) {
+
+ scratch[offset] = (unsigned char) (value & 0x00FF);
+ scratch[offset + 1] = (unsigned char) ((scratch[offset + 1] & 0x00F0) | ((value & 0x0F00) >> 8));
+
+ } else {
+
+ scratch[offset] = (unsigned char) ((scratch[offset] & 0x000F) | ((value & 0x000F) << 4));
+ scratch[offset + 1] = (unsigned char) ((value & 0x0FF0) >> 4);
+
+ }
+
+ goto _write_fat;
+
+ }
+
+ } else if (state->size_fat == 16) {
+
+ scratch[offset] = (value & 0xFF);
+ scratch[offset + 1] = (value >> 8) & 0xFF;
+
+ goto _write_fat;
+
+ } else if (state->size_fat == 32) {
+
+ scratch[offset] = (value & 0xFF);
+ scratch[offset + 1] = (value >> 8) & 0xFF;
+ scratch[offset + 2] = (value >> 16) & 0xFF;
+ scratch[offset + 3] = (scratch[offset + 3] & 0xF0) | ((value >> 24) & 0xFF);
+
+ goto _write_fat;
+
+ }
+
+ free (scratch);
+ return -1;
+
+_write_fat:
+
+ for (i = 0; i < number_of_fats; i++) {
+
+ long temp = sector + (i * sectors_per_fat);
+
+ if (seekto (temp * 512) || fwrite (scratch, 512, 1, ofp) != 1) {
+
+ free (scratch);
+ return -1;
+
+ }
+
+ }
+
+ free (scratch);
+ return 0;
+
+}
+
+static unsigned int align_object (unsigned int sectors, unsigned int clustsize) {
+
+ if (align_structures) {
+ return (sectors + clustsize - 1) & ~(clustsize - 1);
+ }
+
+ return sectors;
+
+}
+
+static unsigned int generate_volume_id (void) {
+
+#if defined (__PDOS__)
+
+ srand (time (NULL));
+
+ /* rand() returns int from [0,RAND_MAX], therefor only 31-bits. */
+ return (((unsigned int) (rand () & 0xFFFF)) << 16) | ((unsigned int) (rand() & 0xFFFF));
+
+#elif defined (__GNUC__)
+
+ struct timeval now;
+
+ if (gettimeofday (&now, 0) != 0 || now.tv_sec == (time_t) -1 || now.tv_sec < 0) {
+
+ srand (getpid ());
+
+ /*- rand() returns int from [0,RAND_MAX], therefor only 31-bits. */
+ return (((unsigned int) (rand () & 0xFFFF)) << 16) | ((unsigned int) (rand() & 0xFFFF));
+
+ }
+
+ /* volume id = current time, fudged for more uniqueness. */
+ return ((unsigned int) now.tv_sec << 20) | (unsigned int) now.tv_usec;
+
+#elif defined (__WATCOMC__)
+
+ srand (getpid ());
+
+ /* rand() returns int from [0,RAND_MAX], therefor only 31-bits. */
+ return (((unsigned int) (rand () & 0xFFFF)) << 16) | ((unsigned int) (rand() & 0xFFFF));
+
+#endif
+
+}
+
+static void establish_bpb (void) {
+
+ unsigned int maxclustsize, root_dir_sectors;
+
+ unsigned int clust12, clust16, clust32;
+ unsigned int fatdata1216, fatdata32;
+ unsigned int fatlength12, fatlength16, fatlength32;
+ unsigned int maxclust12, maxclust16, maxclust32;
+
+ long cylinder_times_heads;
+ long sectors_per_cylinder;
+
+ if ((unsigned long) total_sectors > UINT_MAX) {
+
+ report_at (program_name, 0, REPORT_WARNING, "target too large, space at end will be left unused.\n");
+ total_sectors = UINT_MAX;
+
+ }
+
+ if (total_sectors > (long) 65535 * 16 * 63) {
+
+ sectors_per_track = 63;
+ heads_per_cylinder = 255;
+
+ cylinder_times_heads = total_sectors / sectors_per_track;
+
+ } else {
+
+ sectors_per_track = 17;
+ cylinder_times_heads = total_sectors / sectors_per_track;
+
+ heads_per_cylinder = (cylinder_times_heads + 1023) >> 10;
+
+ if (heads_per_cylinder < 4) {
+ heads_per_cylinder = 4;
+ }
+
+ if (cylinder_times_heads >= heads_per_cylinder << 10 || heads_per_cylinder > 16) {
+
+ sectors_per_track = 31;
+ heads_per_cylinder = 16;
+
+ cylinder_times_heads = total_sectors / sectors_per_track;
+
+ }
+
+ if (cylinder_times_heads >= heads_per_cylinder << 10) {
+
+ sectors_per_track = 63;
+ heads_per_cylinder = 16;
+
+ cylinder_times_heads = total_sectors / sectors_per_track;
+
+ }
+
+ }
+
+ sectors_per_cylinder = heads_per_cylinder * sectors_per_track;
+
+ hidden_sectors = state->offset;
+ total_sectors = state->sectors;
+
+ if (state->chs_align && hidden_sectors % sectors_per_track) {
+
+ hidden_sectors = ((hidden_sectors / sectors_per_track) + 1) * sectors_per_track;
+ report_at (program_name, 0, REPORT_WARNING, "hidden sectors changed from %lu to %lu", state->offset, hidden_sectors);
+
+ }
+
+ if (state->chs_align) {
+
+ long adjustment;
+
+ if (total_sectors % sectors_per_cylinder) {
+ total_sectors = ((total_sectors / sectors_per_cylinder) + 1) * sectors_per_cylinder;
+ }
+
+ if ((unsigned long) (hidden_sectors + total_sectors) > state->offset + state->sectors) {
+
+ adjustment = (hidden_sectors + total_sectors) - (state->offset + state->sectors);
+ total_sectors -= adjustment;
+
+ }
+
+ if ((adjustment = (hidden_sectors + total_sectors) % sectors_per_cylinder)) {
+ total_sectors -= adjustment;
+ }
+
+ if ((unsigned long) total_sectors != state->sectors) {
+ report_at (program_name, 0, REPORT_WARNING, "total sectors changed from %lu to %lu", state->sectors, total_sectors);
+ }
+
+ }
+
+ state->offset = hidden_sectors;
+ state->sectors = total_sectors;
+
+ switch (total_sectors) {
+
+ case 320: /* 160KB 5.25" */
+
+ sectors_per_cluster = 2;
+ root_entries = 112;
+ media_descriptor = 0xfe;
+ sectors_per_track = 8;
+ heads_per_cylinder = 1;
+ break;
+
+ case 360: /* 180KB 5.25" */
+
+ sectors_per_cluster = 2;
+ root_entries = 112;
+ media_descriptor = 0xfc;
+ sectors_per_track = 9;
+ heads_per_cylinder = 1;
+ break;
+
+ case 640: /* 320KB 5.25" */
+
+ sectors_per_cluster = 2;
+ root_entries = 112;
+ media_descriptor = 0xff;
+ sectors_per_track = 8;
+ heads_per_cylinder = 2;
+ break;
+
+ case 720: /* 360KB 5.25" */
+
+ sectors_per_cluster = 2;
+ root_entries = 112;
+ media_descriptor = 0xfd;
+ sectors_per_track = 9;
+ heads_per_cylinder = 2;
+ break;
+
+ case 1280: /* 640KB 5.25" / 3.5" */
+
+ sectors_per_cluster = 2;
+ root_entries = 112;
+ media_descriptor = 0xfb;
+ sectors_per_track = 8;
+ heads_per_cylinder = 2;
+ break;
+
+ case 1440: /* 720KB 5.25" / 3.5" */
+
+ sectors_per_cluster = 2;
+ root_entries = 112;
+ media_descriptor = 0xf9;
+ sectors_per_track = 9;
+ heads_per_cylinder = 2;
+ break;
+
+ case 1640: /* 820KB 3.5" */
+
+ sectors_per_cluster = 2;
+ root_entries = 112;
+ media_descriptor = 0xf9;
+ sectors_per_track = 10;
+ heads_per_cylinder = 2;
+ break;
+
+ case 2400: /* 1.20MB 5.25" / 3.5" */
+
+ sectors_per_cluster = 1;
+ root_entries = 224;
+ media_descriptor = 0xf9;
+ sectors_per_track = 15;
+ heads_per_cylinder = 2;
+ break;
+
+ case 2880: /* 1.44MB 3.5" */
+
+ sectors_per_cluster = 1;
+ root_entries = 224;
+ media_descriptor = 0xf0;
+ sectors_per_track = 18;
+ heads_per_cylinder = 2;
+ break;
+
+ case 3360: /* 1.68MB 3.5" */
+
+ sectors_per_cluster = 1;
+ root_entries = 224;
+ media_descriptor = 0xf0;
+ sectors_per_track = 21;
+ heads_per_cylinder = 2;
+ break;
+
+ case 3444: /* 1.72MB 3.5" */
+
+ sectors_per_cluster = 1;
+ root_entries = 224;
+ media_descriptor = 0xf0;
+ sectors_per_track = 21;
+ heads_per_cylinder = 2;
+ break;
+
+ case 5760: /* 2.88MB 3.5" */
+
+ sectors_per_cluster = 2;
+ root_entries = 240;
+ media_descriptor = 0xf0;
+ sectors_per_track = 36;
+ heads_per_cylinder = 2;
+ break;
+
+ }
+
+ if (!state->size_fat && total_sectors >= 1048576) {
+ state->size_fat = 32;
+ }
+
+ if (state->size_fat == 32) {
+
+ root_entries = 0;
+
+ /*
+ * For FAT32, try to do the same as M$'s format command
+ * (see http://www.win.tue.nl/~aeb/linux/fs/fat/fatgen103.pdf p. 20):
+ *
+ * fs size <= 260M: 0.5k clusters
+ * fs size <= 8G: 4k clusters
+ * fs size <= 16G: 8k clusters
+ * fs size <= 32G: 16k clusters
+ * fs size > 32G: 32k clusters
+ */
+ sectors_per_cluster = (total_sectors > 32 * 1024 * 1024 * 2 ? 64 :
+ total_sectors > 16 * 1024 * 1024 * 2 ? 32 :
+ total_sectors > 8 * 1024 * 1024 * 2 ? 16 :
+ total_sectors > 260 * 1024 * 2 ? 8 : 1);
+
+ }
+
+ if (state->sectors_per_cluster) {
+ sectors_per_cluster = state->sectors_per_cluster;
+ }
+
+ if (!reserved_sectors) {
+ reserved_sectors = (state->size_fat == 32 ? 32 : 1);
+ }
+
+ /*if (align_structures) {*/
+
+ /** Align number of sectors to be multiple of sectors per track, needed by DOS and mtools. */
+ /*total_sectors = total_sectors / sectors_per_track * sectors_per_track;*/
+
+ /*}*/
+
+ if (total_sectors <= 8192) {
+
+ if (align_structures && state->verbose) {
+ report_at (program_name, 0, REPORT_WARNING, "Disabling alignment due to tiny filsystem\n");
+ }
+
+ align_structures = 0;
+
+ }
+
+ maxclustsize = 128;
+ root_dir_sectors = cdiv (root_entries * 32, 512);
+
+ do {
+
+ fatdata32 = total_sectors - align_object (reserved_sectors, sectors_per_cluster);
+ fatdata1216 = fatdata32 - align_object (root_dir_sectors, sectors_per_cluster);
+
+ if (state->verbose) {
+ fprintf (stderr, "Trying with %d sectors/cluster:\n", sectors_per_cluster);
+ }
+
+ /**
+ * The factor 2 below avoids cut-off errors for number_of_fats == 1.
+ * The "number_of_fats * 3" is for the reserved first two FAT entries.
+ */
+ clust12 = 2 * ((long) fatdata1216 * 512 + number_of_fats * 3) / (2 * (int) sectors_per_cluster * 512 + number_of_fats * 3);
+ fatlength12 = cdiv (((clust12 + 2) * 3 + 1) >> 1, 512);
+ fatlength12 = align_object (fatlength12, sectors_per_cluster);
+
+ /**
+ * Need to recalculate number of clusters, since the unused parts of the
+ * FATs and data area together could make up space for an additional,
+ * not really present cluster.
+ */
+ clust12 = (fatdata1216 - number_of_fats * fatlength12) / sectors_per_cluster;
+ maxclust12 = (fatlength12 * 2 * 512) / 3;
+
+ if (maxclust12 > MAX_CLUST_12) {
+ maxclust12 = MAX_CLUST_12;
+ }
+
+ if (state->verbose && (state->size_fat == 0 || state->size_fat == 12)) {
+ fprintf (stderr, "Trying FAT12: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n", clust12, fatlength12, maxclust12, MAX_CLUST_12);
+ }
+
+ if (clust12 > maxclust12) {
+
+ clust12 = 0;
+
+ if (state->verbose && (state->size_fat == 0 || state->size_fat == 12)) {
+ fprintf (stderr, "Trying FAT12: too many clusters\n");
+ }
+
+ }
+
+ clust16 = ((long) fatdata1216 * 512 + number_of_fats * 4) / ((int) sectors_per_cluster * 512 + number_of_fats * 2);
+ fatlength16 = cdiv ((clust16 + 2) * 2, 512);
+ fatlength16 = align_object (fatlength16, sectors_per_cluster);
+
+ /**
+ * Need to recalculate number of clusters, since the unused parts of the
+ * FATs and data area together could make up space for an additional,
+ * not really present cluster.
+ */
+ clust16 = (fatdata1216 - number_of_fats * fatlength16) / sectors_per_cluster;
+ maxclust16 = (fatlength16 * 512) / 2;
+
+ if (maxclust16 > MAX_CLUST_16) {
+ maxclust16 = MAX_CLUST_16;
+ }
+
+ if (state->verbose && (state->size_fat == 0 || state->size_fat == 16)) {
+ fprintf (stderr, "Trying FAT16: #clu=%u, fatlen=%u, maxclu=%u, limit=%u/%u\n", clust16, fatlength16, maxclust16, MIN_CLUST_16, MAX_CLUST_16);
+ }
+
+ if (clust16 > maxclust16) {
+
+ clust16 = 0;
+
+ if (state->verbose && (state->size_fat == 0 || state->size_fat == 16)) {
+ fprintf (stderr, "Trying FAT16: too many clusters\n");
+ }
+
+ }
+
+ /** This avoids that the filesystem will be misdetected as having a 12-bit FAT. */
+ if (clust16 && clust16 < MIN_CLUST_16) {
+
+ clust16 = 0;
+
+ if (state->verbose && (state->size_fat == 0 || state->size_fat == 16)) {
+ fprintf (stderr, "Trying FAT16: not enough clusters, would be misdected as FAT12\n");
+ }
+
+ }
+
+ clust32 = ((long) fatdata32 * 512 + number_of_fats * 8) / ((int) sectors_per_cluster * 512 + number_of_fats * 4);
+ fatlength32 = cdiv ((clust32 + 2) * 4, 512);
+ fatlength32 = align_object (fatlength32, sectors_per_cluster);
+
+ /**
+ * Need to recalculate number of clusters, since the unused parts of the
+ * FATs and data area together could make up space for an additional,
+ * not really present cluster.
+ */
+ clust32 = (fatdata32 - number_of_fats * fatlength32) / sectors_per_cluster;
+ maxclust32 = (fatlength32 * 512) / 4;
+
+ if (maxclust32 > MAX_CLUST_32) {
+ maxclust32 = MAX_CLUST_32;
+ }
+
+ if (state->verbose && (state->size_fat == 0 || state->size_fat == 32)) {
+ fprintf (stderr, "Trying FAT32: #clu=%u, fatlen=%u, maxclu=%u, limit=%u/%u\n", clust32, fatlength32, maxclust32, MIN_CLUST_32, MAX_CLUST_32);
+ }
+
+ if (clust32 > maxclust32) {
+
+ clust32 = 0;
+
+ if (state->verbose && (state->size_fat == 0 || state->size_fat == 32)) {
+ fprintf (stderr, "Trying FAT32: too many clusters\n");
+ }
+
+ }
+
+ if (clust32 && clust32 < MIN_CLUST_32 && !(state->size_fat_by_user && state->size_fat == 32)) {
+
+ clust32 = 0;
+
+ if (state->verbose && (state->size_fat == 0 || state->size_fat == 32)) {
+ fprintf (stderr, "Trying FAT32: not enough clusters\n");
+ }
+
+ }
+
+ if ((clust12 && (state->size_fat == 0 || state->size_fat == 12)) || (clust16 && (state->size_fat == 0 || state->size_fat == 16)) || (clust32 && state->size_fat == 32)) {
+ break;
+ }
+
+ sectors_per_cluster <<= 1;
+
+ } while (sectors_per_cluster && sectors_per_cluster <= maxclustsize);
+
+ /** Use the optimal FAT size if not specified. */
+ if (!state->size_fat) {
+
+ state->size_fat = (clust16 > clust12 ? 16 : 12);
+
+ if (state->verbose) {
+ report_at (program_name, 0, REPORT_WARNING, "Choosing %d-bits for FAT\n", state->size_fat);
+ }
+
+ }
+
+ switch (state->size_fat) {
+
+ case 12:
+
+ cluster_count = clust12;
+ sectors_per_fat = fatlength12;
+ break;
+
+ case 16:
+
+ cluster_count = clust16;
+ sectors_per_fat = fatlength16;
+ break;
+
+ case 32:
+
+ cluster_count = clust32;
+ sectors_per_fat = fatlength32;
+ break;
+
+ default:
+
+ report_at (program_name, 0, REPORT_ERROR, "FAT not 12, 16 or 32 bits");
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ exit (EXIT_FAILURE);
+
+ }
+
+ /** Adjust the reserved number of sectors for alignment. */
+ reserved_sectors = align_object (reserved_sectors, sectors_per_cluster);
+
+ /** Adjust the number of root directory entries to help enforce alignment. */
+ if (align_structures) {
+ root_entries = align_object (root_dir_sectors, sectors_per_cluster) * (512 >> 5);
+ }
+
+ if (state->size_fat == 32) {
+
+ if (!info_sector) {
+ info_sector = 1;
+ }
+
+ if (!backup_boot) {
+
+ if (reserved_sectors >= 7 && info_sector != 6) {
+ backup_boot = 6;
+ } else if (reserved_sectors > 3 + info_sector && info_sector != reserved_sectors - 2 && info_sector != reserved_sectors - 1) {
+ backup_boot = reserved_sectors - 2;
+ } else if (reserved_sectors >= 3 && info_sector != reserved_sectors - 1) {
+ backup_boot = reserved_sectors - 1;
+ }
+
+ }
+
+ if (backup_boot) {
+
+ if (backup_boot == info_sector) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Backup boot sector must not be the same as the info sector (%d)", info_sector);
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ exit (EXIT_FAILURE);
+
+ } else if (backup_boot >= reserved_sectors) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Backup boot sector must be a reserved sector");
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ exit (EXIT_FAILURE);
+
+ }
+
+ }
+
+ if (state->verbose) {
+ fprintf (stderr, "Using sector %d as backup boot sector (0 = none)\n", backup_boot);
+ }
+
+ }
+
+ if (!cluster_count) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Not enough clusters to make a viable filesystem");
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ exit (EXIT_FAILURE);
+
+ }
+
+}
+
+static void add_volume_label (void) {
+
+ struct msdos_dirent *de;
+ unsigned short date, time;
+
+ unsigned char *scratch;
+ long offset = 0;
+
+ if (!(scratch = (unsigned char *) malloc (512))) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Failed to allocate memory");
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ exit (EXIT_FAILURE);
+
+ }
+
+ if (state->size_fat == 32) {
+
+ long temp = reserved_sectors + (sectors_per_fat * 2);
+ offset += temp + ((root_cluster - 2) * sectors_per_cluster);
+
+ } else {
+ offset += reserved_sectors + (sectors_per_fat * 2);
+ }
+
+ if (seekto (offset * 512) || fwrite (scratch, 512, 1, ofp) != 1) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Failed whilst reading root directory");
+ free (scratch);
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ exit (EXIT_FAILURE);
+
+ }
+
+ de = (struct msdos_dirent *) scratch;
+ memset (de, 0, sizeof (*de));
+
+ date = generate_datestamp ();
+ time = generate_timestamp ();
+
+ memcpy (de->name, state->label, 11);
+ de->attr = ATTR_VOLUME_ID;
+
+ write721_to_byte_array (de->ctime, time);
+ write721_to_byte_array (de->cdate, date);
+ write721_to_byte_array (de->adate, date);
+ write721_to_byte_array (de->time, time);
+ write721_to_byte_array (de->date, date);
+
+ if (seekto (offset * 512) || fwrite (scratch, 512, 1, ofp) != 1) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Failed whilst writing root directory");
+ free (scratch);
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ exit (EXIT_FAILURE);
+
+ }
+
+ free (scratch);
+
+}
+
+static void wipe_target (void) {
+
+ unsigned int i, sectors_to_wipe = 0;
+ void *blank;
+
+ if (!(blank = malloc (512))) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Failed to allocate memory");
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ exit (EXIT_FAILURE);
+
+ }
+
+ memset (blank, 0, 512);
+
+ sectors_to_wipe += reserved_sectors;
+ sectors_to_wipe += sectors_per_fat * 2;
+
+ if (root_entries) {
+ sectors_to_wipe += (root_entries * 32) / 512;
+ } else {
+ sectors_to_wipe += 1;
+ }
+
+ seekto (0);
+
+ for (i = 0; i < sectors_to_wipe; i++) {
+
+ if (fwrite (blank, 512, 1, ofp) != 1) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Failed whilst writing blank sector");
+ free (blank);
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ exit (EXIT_FAILURE);
+
+ }
+
+ }
+
+ free (blank);
+
+}
+
+static void write_reserved (void) {
+
+ struct msdos_boot_sector bs;
+ struct msdos_volume_info *vi = (state->size_fat == 32 ? &bs.fstype._fat32.vi : &bs.fstype._oldfat.vi);
+
+ memset (&bs, 0, sizeof (bs));
+
+ if (state->boot) {
+
+ FILE *ifp;
+
+ if ((ifp = fopen (state->boot, "rb")) == NULL) {
+
+ report_at (program_name, 0, REPORT_ERROR, "unable to open %s", state->boot);
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ exit (EXIT_FAILURE);
+
+ }
+
+ fseek (ifp, 0, SEEK_END);
+
+ if (ftell (ifp) != sizeof (bs)) {
+
+ report_at (program_name, 0, REPORT_ERROR, "boot sector must be %lu bytes in size", (unsigned long) sizeof (bs));
+ fclose (ifp);
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ exit (EXIT_FAILURE);
+
+ }
+
+ fseek (ifp, 0, SEEK_SET);
+
+ if (fread (&bs, sizeof (bs), 1, ifp) != 1) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to read %s", state->boot);
+ fclose (ifp);
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ exit (EXIT_FAILURE);
+
+ }
+
+ fclose (ifp);
+
+ } else {
+
+ bs.boot_jump[0] = 0xEB;
+ bs.boot_jump[1] = ((state->size_fat == 32 ? (char *) &bs.fstype._fat32.boot_code : (char *) &bs.fstype._oldfat.boot_code) - (char *) &bs) - 2;
+ bs.boot_jump[2] = 0x90;
+
+ if (state->size_fat == 32) {
+ memcpy (bs.fstype._fat32.boot_code, dummy_boot_code, sizeof (dummy_boot_code));
+ } else {
+ memcpy (bs.fstype._oldfat.boot_code, dummy_boot_code, sizeof (dummy_boot_code));
+ }
+
+ bs.boot_sign[0] = 0x55;
+ bs.boot_sign[1] = 0xAA;
+
+ }
+
+ if (bs.boot_jump[0] != 0xEB || bs.boot_jump[1] < 0x16 || bs.boot_jump[2] != 0x90) {
+ goto _write_reserved;
+ }
+
+ memcpy (bs.system_id, "MSWIN4.1", 8);
+
+ bs.sectors_per_cluster = sectors_per_cluster;
+ bs.no_fats = number_of_fats;
+ bs.media_descriptor = media_descriptor;
+
+ write721_to_byte_array (bs.bytes_per_sector, 512);
+ write721_to_byte_array (bs.reserved_sectors, reserved_sectors);
+ write721_to_byte_array (bs.root_entries, root_entries);
+ write721_to_byte_array (bs.total_sectors16, total_sectors);
+ write721_to_byte_array (bs.sectors_per_fat16, sectors_per_fat);
+
+ if (bs.boot_jump[1] < 0x22) {
+ goto _write_reserved;
+ }
+
+ write721_to_byte_array (bs.sectors_per_track, sectors_per_track);
+ write721_to_byte_array (bs.heads_per_cylinder, heads_per_cylinder);
+ write741_to_byte_array (bs.hidden_sectors, hidden_sectors);
+
+ if (total_sectors > USHRT_MAX) {
+
+ write721_to_byte_array (bs.total_sectors16, 0);
+ write741_to_byte_array (bs.total_sectors32, total_sectors);
+
+ }
+
+ if (state->size_fat == 32) {
+
+ if (bs.boot_jump[1] < 0x58) {
+ goto _write_reserved;
+ }
+
+ write721_to_byte_array (bs.sectors_per_fat16, 0);
+ write741_to_byte_array (bs.fstype._fat32.sectors_per_fat32, sectors_per_fat);
+
+ write741_to_byte_array (bs.fstype._fat32.root_cluster, root_cluster);
+ write721_to_byte_array (bs.fstype._fat32.info_sector, info_sector);
+ write721_to_byte_array (bs.fstype._fat32.backup_boot, backup_boot);
+
+ vi->drive_no = (media_descriptor == 0xF8 ? 0x80 : 0x00);
+ vi->ext_boot_sign = 0x29;
+
+ write741_to_byte_array (vi->volume_id, generate_volume_id ());
+ memcpy (vi->volume_label, state->label, 11);
+ memcpy (vi->fs_type, "FAT32 ", 8);
+
+ } else {
+
+ if (bs.boot_jump[1] < 0x3C) {
+ goto _write_reserved;
+ }
+
+ vi->drive_no = (media_descriptor == 0xF8 ? 0x80 : 0x00);
+ vi->ext_boot_sign = 0x29;
+
+ write741_to_byte_array (vi->volume_id, generate_volume_id ());
+ memcpy (vi->volume_label, state->label, 11);
+
+ if (state->size_fat == 12) {
+ memcpy (vi->fs_type, "FAT12 ", 8);
+ } else {
+ memcpy (vi->fs_type, "FAT16 ", 8);
+ }
+
+ }
+
+_write_reserved:
+
+ seekto (0);
+
+ if (fwrite (&bs, sizeof (bs), 1, ofp) != 1) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Failed whilst writing %s", state->outfile);
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ exit (EXIT_FAILURE);
+
+ }
+
+ if (state->size_fat == 32) {
+
+ if (info_sector) {
+
+ struct fat32_fsinfo *info;
+ unsigned char *buffer;
+
+ if (seekto (info_sector * 512)) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Failed whilst seeking %s", state->outfile);
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ exit (EXIT_FAILURE);
+
+ }
+
+ if (!(buffer = (unsigned char *) malloc (512))) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Failed to allocate memory");
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ exit (EXIT_FAILURE);
+
+ }
+
+ memset (buffer, 0, 512);
+
+ /** fsinfo structure is at offset 0x1e0 in info sector by observation. */
+ info = (struct fat32_fsinfo *) (buffer + 0x1e0);
+
+ /** Info sector magic. */
+ buffer[0] = 'R';
+ buffer[1] = 'R';
+ buffer[2] = 'a';
+ buffer[3] = 'A';
+
+ /** Magic for fsinfo structure. */
+ write741_to_byte_array (info->signature, 0x61417272);
+
+ /** We've allocated cluster 2 for the root directory. */
+ write741_to_byte_array (info->free_clusters, cluster_count - 1);
+ write741_to_byte_array (info->next_cluster, 2);
+
+ /** Info sector also must have boot sign. */
+ write721_to_byte_array (buffer + 0x1fe, 0xAA55);
+
+ if (fwrite (buffer, 512, 1, ofp) != 1) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Failed whilst writing info sector");
+ free (buffer);
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ exit (EXIT_FAILURE);
+
+ }
+
+ free (buffer);
+
+ }
+
+ if (backup_boot) {
+
+ if (seekto (backup_boot * 512) || fwrite (&bs, sizeof (bs), 1, ofp) != 1) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Failed whilst writing info sector");
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ exit (EXIT_FAILURE);
+
+ }
+
+ }
+
+ }
+
+}
+
+struct vhd_footer {
+
+ unsigned char cookie[8];
+ unsigned char features[4];
+
+ struct {
+
+ unsigned char major[2];
+ unsigned char minor[2];
+
+ } version;
+
+ unsigned char next_offset[8];
+ unsigned char modified_time[4];
+ unsigned char creator_name[4];
+
+ struct {
+
+ unsigned char major[2];
+ unsigned char minor[2];
+
+ } creator_version;
+
+ unsigned char creator_host[4];
+ unsigned char disk_size[8];
+ unsigned char data_size[8];
+
+ struct {
+
+ unsigned char cylinders[2];
+ unsigned char heads_per_cyl;
+ unsigned char secs_per_track;
+
+ } disk_geom;
+
+ unsigned char disk_type[4];
+ unsigned char checksum[4];
+ unsigned char identifier[16];
+ unsigned char saved_state;
+ unsigned char reserved[427];
+
+};
+
+int main (int argc, char **argv) {
+
+ if (argc && *argv) {
+
+ char *p;
+ program_name = *argv;
+
+ if ((p = strrchr (program_name, '/')) || (p = strrchr (program_name, '\\'))) {
+ program_name = (p + 1);
+ }
+
+ }
+
+ state = xmalloc (sizeof (*state));
+ parse_args (&argc, &argv, 1);
+
+ if (!state->outfile) {
+
+ report_at (program_name, 0, REPORT_ERROR, "no outfile file provided");
+ return EXIT_FAILURE;
+
+ }
+
+ image_size = state->sectors * 512;
+ image_size += state->offset * 512;
+
+ if ((ofp = fopen (state->outfile, "r+b")) == NULL) {
+
+ unsigned long len;
+ void *zero;
+
+ if ((ofp = fopen (state->outfile, "w+b")) == NULL) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to open '%s' for writing", state->outfile);
+ return EXIT_FAILURE;
+
+ }
+
+ len = image_size;
+ zero = xmalloc (512);
+
+ while (len > 0) {
+
+ if (fwrite (zero, 512, 1, ofp) != 1) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed whilst writing '%s'", state->outfile);
+ fclose (ofp);
+
+ free (zero);
+ remove (state->outfile);
+
+ return EXIT_FAILURE;
+
+ }
+
+ len -= 512;
+
+ }
+
+ free (zero);
+
+ } else {
+
+ struct vhd_footer footer;
+ long size = sizeof (footer);
+
+ fseek (ofp, 0, SEEK_END);
+ image_size = ftell (ofp);
+
+ if (!fseek (ofp, ftell (ofp) - size, SEEK_SET)) {
+
+ if (fread (&footer, size, 1, ofp) == 1) {
+
+ if (footer.cookie[0] == 0x63 && footer.cookie[1] == 0x6F && footer.cookie[2] == 0x6E && footer.cookie[3] == 0x65 && footer.cookie[4] == 0x63 && footer.cookie[5] == 0x74 && footer.cookie[6] == 0x69 && footer.cookie[7] == 0x78) {
+
+ /* Okay, if we reach this we'll assume that we have a hard disk image so subtract the footer size and 512 for the MBR. */
+ image_size -= size;
+ image_size -= 512;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ total_sectors = image_size / 512;
+ establish_bpb ();
+
+ seekto (0);
+
+ if (state->offset * 512 > image_size) {
+
+ report_at (program_name, 0, REPORT_ERROR, "size (%lu) of %s is less than the requested offset (%lu)", image_size, state->outfile, state->offset * 512);
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ return EXIT_FAILURE;
+
+ }
+
+ image_size -= state->offset * 512;
+
+ if (state->sectors) {
+
+ if (state->sectors * 512 > image_size) {
+
+ report_at (program_name, 0, REPORT_ERROR, "size (%lu) of %s is less than the requested size (%lu)", image_size, state->outfile, state->sectors * 512);
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ return EXIT_FAILURE;
+
+ }
+
+ }
+
+ wipe_target ();
+ write_reserved ();
+
+ if (set_fat_entry (0, 0xFFFFFF00 | media_descriptor) < 0 || set_fat_entry (1, 0xFFFFFFFF) < 0) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Failed whilst setting FAT entry");
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ return EXIT_FAILURE;
+
+ }
+
+ if (state->size_fat == 32) {
+
+ if (set_fat_entry (2, 0x0FFFFFF8) < 0) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Failed whilst setting FAT entry");
+
+ fclose (ofp);
+ remove (state->outfile);
+
+ return EXIT_FAILURE;
+
+ }
+
+ }
+
+ if (memcmp (state->label, "NO NAME ", 11) != 0) {
+ add_volume_label ();
+ }
+
+ fclose (ofp);
+ return EXIT_SUCCESS;
+
+}
--- /dev/null
+/******************************************************************************
+ * @file parted.h
+ *****************************************************************************/
+#ifndef _PARTED_H
+#define _PARTED_H
+
+struct mkfs_state {
+
+ const char *boot, *outfile;
+ char label[12];
+
+ int create, size_fat, size_fat_by_user, verbose;
+ unsigned long sectors, offset;
+
+ unsigned char sectors_per_cluster;
+ int chs_align;
+
+};
+
+extern struct mkfs_state *state;
+extern const char *program_name;
+
+#endif /* _PARTED_H */
--- /dev/null
+/******************************************************************************
+ * @file mls.c
+ *****************************************************************************/
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "msdos.h"
+#include "report.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 2048
+#endif
+
+struct mls_state {
+
+ char **dirs;
+ long nb_dirs;
+
+ const char *outfile;
+ unsigned long offset;
+
+ int chs_align;
+
+};
+
+static FILE *ifp;
+
+struct dir_info {
+
+ unsigned int current_cluster;
+
+ unsigned char current_sector;
+ unsigned char current_entry;
+ unsigned char *scratch;
+ unsigned char flags;
+
+};
+
+static struct mls_state *state = 0;
+static const char *program_name = 0;
+
+static struct msdos_boot_sector bs;
+static int size_fat = 0;
+
+static unsigned int cluster_count = 0;
+static unsigned int data_area = 0;
+static unsigned int info_sector = 0;
+static unsigned int number_of_fats = 0;
+static unsigned int reserved_sectors = 0;
+static unsigned int root_cluster = 0;
+static unsigned int root_dir = 0;
+static unsigned int root_entries = 0;
+static unsigned int sectors_per_cluster = 0;
+static unsigned int sectors_per_fat = 0;
+static unsigned int total_sectors = 0;
+
+struct option {
+
+ const char *name;
+ int index, flags;
+
+};
+
+#define OPTION_NO_ARG 0x0001
+#define OPTION_HAS_ARG 0x0002
+
+enum options {
+
+ OPTION_IGNORED = 1,
+ OPTION_ARCA,
+ OPTION_HELP,
+ OPTION_INPUT,
+ OPTION_OFFSET
+
+};
+
+static struct option opts[] = {
+
+ { "i", OPTION_INPUT, OPTION_HAS_ARG },
+
+ { "-arca", OPTION_ARCA, OPTION_NO_ARG },
+ { "-help", OPTION_HELP, OPTION_NO_ARG },
+ { "-offset", OPTION_OFFSET, 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 (int exitval) {
+
+ if (!program_name) {
+ goto _exit;
+ }
+
+ 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, " --arca Use CHS-alignment (only works for VHD images).\n");
+ fprintf (stderr, " --help Show this help information then exit.\n");
+ fprintf (stderr, " --offset SECTOR Read the filesystem starting at SECTOR.\n");
+
+_exit:
+
+ exit (exitval);
+
+}
+
+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 *pargc, char ***pargv, int optind) {
+
+ char **argv = *pargv;
+ int argc = *pargc;
+
+ struct option *popt;
+ const char *optarg, *r;
+
+ if (argc == optind) {
+ print_help (EXIT_SUCCESS);
+ }
+
+ while (optind < argc) {
+
+ r = argv[optind++];
+
+ if (r[0] != '-' || r[1] == '\0') {
+
+ dynarray_add (&state->dirs, &state->nb_dirs, xstrdup (r));
+ continue;
+
+ }
+
+ for (popt = opts; popt; ++popt) {
+
+ const char *p1 = popt->name;
+ const char *r1 = (r + 1);
+
+ 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_ARCA: {
+
+ state->chs_align = 1;
+ break;
+
+ }
+
+ case OPTION_HELP: {
+
+ print_help (EXIT_SUCCESS);
+ break;
+
+ }
+
+ case OPTION_INPUT: {
+
+ if (state->outfile) {
+
+ report_at (program_name, 0, REPORT_ERROR, "multiple output files provided");
+ exit (EXIT_FAILURE);
+
+ }
+
+ state->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 || (unsigned long) conversion > UINT_MAX) {
+
+ report_at (program_name, 0, REPORT_ERROR, "offset must be between 0 and %u", UINT_MAX);
+ exit (EXIT_FAILURE);
+
+ }
+
+ state->offset = (unsigned long) conversion;
+ break;
+
+ }
+
+ default: {
+
+ report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", r);
+ exit (EXIT_FAILURE);
+
+ }
+
+ }
+
+ }
+
+}
+
+
+static int seekto (long offset) {
+ return fseek (ifp, (state->offset * 512) + offset, SEEK_SET);
+}
+
+static int get_next_entry (struct dir_info *di, struct msdos_dirent *de);
+static int open_dir (const char *target, struct dir_info *di);
+
+static unsigned int get_fat_entry (unsigned char *scratch, unsigned int cluster);
+
+static int canonical_to_dir (char *dest, const char *src) {
+
+ static const char invalid_chars[] = "\"*+,./:;<=>?[\\]|";
+
+ int i, j;
+ int namelen = 0, dots = 0, extlen = 0;
+
+ memset (dest, ' ', 11);
+
+ if (*src == '\0' || *src == '.') {
+ return -1;
+ }
+
+ for (j = i = 0; *src != '\0'; i++) {
+
+ int c = (unsigned char) *src++;
+
+ if (c == '/' || c == '\\') {
+ break;
+ }
+
+ if (i >= 12) {
+ return -1;
+ }
+
+ if (i == 0 && c == 0xE5) {
+
+ /**
+ * 0xE5 in the first character of the name is a marker for delected files,
+ * it needs to be translated to 0x05.
+ */
+ c = 0x05;
+
+ } else if (c == '.') {
+
+ if (dots++) {
+ return -1;
+ }
+
+ j = 8;
+ continue;
+
+ }
+
+ if (c <= 0x20 || strchr (invalid_chars, c)) {
+ return -1;
+ }
+
+ if (dots) {
+
+ if (++extlen > 3) {
+ return -1;
+ }
+
+ } else {
+
+ if (++namelen > 8) {
+ return -1;
+ }
+
+ }
+
+ if (c >= 'a' && c <= 'z') {
+ c -= 'a' - 'A';
+ }
+
+ dest[j++] = c;
+
+ }
+
+ return 0;
+
+}
+
+static int get_next_entry (struct dir_info *di, struct msdos_dirent *de) {
+
+ unsigned long offset;
+
+ if (di->current_entry >= 512 / sizeof (*de)) {
+
+ di->current_entry = 0;
+ di->current_sector++;
+
+ if (di->current_cluster == 0) {
+
+ unsigned long offset;
+
+ if (di->current_sector * (512 / sizeof (*de)) >= root_entries) {
+ return -1;
+ }
+
+ offset = (unsigned long) root_dir + di->current_sector;
+
+ if (seekto (offset * 512) || fread (di->scratch, 512, 1, ifp) != 1) {
+ return -1;
+ }
+
+ } else {
+
+ if (di->current_sector >= sectors_per_cluster) {
+
+ di->current_sector = 0;
+
+ if ((size_fat == 12 && di->current_cluster >= 0x0FF7) || (size_fat == 16 && di->current_cluster >= 0xFFF7) || (size_fat == 32 && di->current_cluster >= 0x0FFFFFF7)) {
+
+ if (!(di->flags & 0x01)) {
+ return -1;
+ }
+
+ return 1;
+
+ }
+
+ di->current_cluster = get_fat_entry (di->scratch, di->current_cluster);
+
+ }
+
+ offset = (unsigned long) data_area;
+ offset += ((di->current_cluster - 2) * sectors_per_cluster);
+ offset += di->current_sector;
+
+ if (seekto (offset * 512) || fread (di->scratch, 512, 1, ifp) != 1) {
+ return -1;
+ }
+
+ }
+
+ }
+
+ memcpy (de, &(((struct msdos_dirent *) di->scratch)[di->current_entry]), sizeof (*de));
+
+ if (de->name[0] == 0) {
+
+ if (di->flags & 0x01) {
+ return 0;
+ }
+
+ return -1;
+
+ }
+
+ if (de->name[0] == 0x05) {
+
+ de->name[0] = 0xE5;
+ return 0;
+
+ }
+
+ di->current_entry++;
+ return 0;
+
+}
+
+static int open_dir (const char *target, struct dir_info *di) {
+
+ di->flags = 0;
+
+ if (!strlen ((char *) target) || (strlen ((char *) target) == 1 && (target[0] == '/' || target[0] == '\\'))) {
+
+ unsigned long offset;
+
+ if (size_fat == 32) {
+
+ di->current_cluster = root_cluster;
+ di->current_entry = 0;
+ di->current_sector = 0;
+
+ offset = (unsigned long) data_area + ((di->current_cluster - 2) * sectors_per_cluster);
+
+ } else {
+
+ di->current_cluster = 0;
+ di->current_entry = 0;
+ di->current_sector = 0;
+
+ offset = (unsigned long) root_dir;
+
+ }
+
+ if (seekto (offset * 512) || fread (di->scratch, 512, 1, ifp) != 1) {
+ return -1;
+ }
+
+ } else {
+
+ unsigned char tmpfn[12];
+ unsigned char *ptr;
+
+ struct msdos_dirent de;
+ unsigned int result;
+
+ unsigned long offset;
+
+ if (size_fat == 32) {
+
+ di->current_cluster = root_cluster;
+ di->current_entry = 0;
+ di->current_sector = 0;
+
+ offset = (unsigned long) data_area + ((di->current_cluster - 2) * sectors_per_cluster);
+
+ } else {
+
+ di->current_cluster = 0;
+ di->current_entry = 0;
+ di->current_sector = 0;
+
+ offset = (unsigned long) root_dir;
+
+ }
+
+ if (seekto (offset * 512) || fread (di->scratch, 512, 1, ifp) != 1) {
+ return -1;
+ }
+
+ ptr = (unsigned char *) target;
+
+ while ((*ptr == '/' || *ptr == '\\') && *ptr) {
+ ptr++;
+ }
+
+ while (*ptr) {
+
+ if (canonical_to_dir ((char *) tmpfn, (char *) ptr) < 0) {
+
+ fprintf (stderr, "Failed to convert to 8:3\n");
+ return -1;
+
+ }
+
+ de.name[0] = 0;
+
+ do {
+ result = get_next_entry (di, &de);
+ } while (!result && memcmp (de.name, tmpfn, 11));
+
+ if (!memcmp (de.name, tmpfn, 11) && (de.attr & ATTR_DIR) == ATTR_DIR) {
+
+ unsigned long offset;
+
+ di->current_cluster = (unsigned int) de.startlo[0] | ((unsigned int) de.startlo[1]) << 8 | ((unsigned int) de.starthi[0]) << 16 | ((unsigned int) de.starthi[1]) << 24;
+ di->current_entry = 0;
+ di->current_sector = 0;
+
+ offset = (unsigned long) (data_area + ((di->current_cluster - 2) * sectors_per_cluster));
+
+ if (seekto (offset * 512)) {
+ return -1;
+ }
+
+ if (fread (di->scratch, 512, 1, ifp) != 1) {
+ return -1;
+ }
+
+ } else if (!memcmp (de.name, tmpfn, 11) && !(de.attr & ATTR_DIR)) {
+ return -1;
+ }
+
+ while (*ptr != '/' && *ptr != '\\' && *ptr) {
+ ptr++;
+ }
+
+ if (*ptr == '/' || *ptr == '\\') {
+ ptr++;
+ }
+
+ }
+
+ if (!di->current_cluster) {
+ return -1;
+ }
+
+ }
+
+ return 0;
+
+}
+
+static unsigned int get_fat_entry (unsigned char *scratch, unsigned int cluster) {
+
+ unsigned int offset, sector, result;
+
+ if (size_fat == 12) {
+ offset = cluster + (cluster / 2);
+ } else if (size_fat == 16) {
+ offset = cluster * 2;
+ } else if (size_fat == 32) {
+ offset = cluster * 4;
+ } else {
+ return 0x0FFFFFF7;
+ }
+
+ sector = (offset / 512) + reserved_sectors;
+
+ if (seekto ((unsigned long) sector * 512) || fread (scratch, 512, 1, ifp) != 1) {
+ return 0x0FFFFFF7;
+ }
+
+ offset %= 512;
+
+ if (size_fat == 12) {
+
+ if (offset == 511) {
+
+ result = (unsigned int) scratch[offset];
+ sector++;
+
+ if (seekto ((unsigned long) sector * 512) || fread (scratch, 512, 1, ifp) != 1) {
+ return 0x0FFFFFF7;
+ }
+
+ result |= ((unsigned int) scratch[0]) << 8;
+
+ } else {
+ result = (unsigned int) scratch[offset] | ((unsigned int) scratch[offset + 1]) << 8;
+ }
+
+ if (cluster & 1) {
+ result = result >> 4;
+ } else {
+ result = result & 0x0FFF;
+ }
+
+ } else if (size_fat == 16) {
+ result = (unsigned int) scratch[offset] | ((unsigned int) scratch[offset + 1]) << 8;
+ } else if (size_fat == 32) {
+ result = ((unsigned int) scratch[offset] | ((unsigned int) scratch[offset + 1]) << 8 | ((unsigned int) scratch[offset + 2]) << 16 | ((unsigned int) scratch[offset + 3]) << 24) & 0x0FFFFFFF;
+ } else {
+ result = 0x0FFFFFF7;
+ }
+
+ return result;
+
+}
+
+struct vhd_footer {
+
+ unsigned char cookie[8];
+ unsigned char features[4];
+
+ struct {
+
+ unsigned char major[2];
+ unsigned char minor[2];
+
+ } version;
+
+ unsigned char next_offset[8];
+ unsigned char modified_time[4];
+ unsigned char creator_name[4];
+
+ struct {
+
+ unsigned char major[2];
+ unsigned char minor[2];
+
+ } creator_version;
+
+ unsigned char creator_host[4];
+ unsigned char disk_size[8];
+ unsigned char data_size[8];
+
+ struct {
+
+ unsigned char cylinders[2];
+ unsigned char heads_per_cyl;
+ unsigned char secs_per_track;
+
+ } disk_geom;
+
+ unsigned char disk_type[4];
+ unsigned char checksum[4];
+ unsigned char identifier[16];
+ unsigned char saved_state;
+ unsigned char reserved[427];
+
+};
+
+int main (int argc, char **argv) {
+
+ unsigned char *scratch;
+ char *target, filename[13];
+
+ struct dir_info di;
+ struct msdos_dirent de;
+
+ unsigned int bytes, timestamp;
+ long i, j, k;
+
+ struct vhd_footer footer;
+
+ if (argc && *argv) {
+
+ char *p;
+ program_name = *argv;
+
+ if ((p = strrchr (program_name, '/')) || (p = strrchr (program_name, '\\'))) {
+ program_name = (p + 1);
+ }
+
+ }
+
+ state = xmalloc (sizeof (*state));
+ parse_args (&argc, &argv, 1);
+
+ if (!state->outfile) {
+ print_help (EXIT_FAILURE);
+ }
+
+ if (state->nb_dirs == 0) {
+ dynarray_add (&state->dirs, &state->nb_dirs, "/");
+ }
+
+ if ((ifp = fopen (state->outfile, "rb")) == NULL) {
+
+ report_at (program_name, 0, REPORT_ERROR, "faild to open '%s' for reading", state->outfile);
+ return EXIT_FAILURE;
+
+ }
+
+ if (state->chs_align) {
+
+ long size = sizeof (footer), offset;
+ fseek (ifp, 0, SEEK_END);
+
+ offset = ftell (ifp) - size;
+
+ if (!fseek (ifp, offset, SEEK_SET)) {
+
+ if (fread (&footer, size, 1, ifp) == 1) {
+
+ if (footer.cookie[0] == 0x63 && footer.cookie[1] == 0x6F && footer.cookie[2] == 0x6E && footer.cookie[3] == 0x65 && footer.cookie[4] == 0x63 && footer.cookie[5] == 0x74 && footer.cookie[6] == 0x69 && footer.cookie[7] == 0x78) {
+
+ long old = state->offset, secs_per_track = footer.disk_geom.secs_per_track;
+
+ if (state->offset % secs_per_track) {
+
+ state->offset = ((state->offset / secs_per_track) + 1) * secs_per_track;
+ report_at (program_name, 0, REPORT_WARNING, "offset changed from %lu to %lu", old, state->offset);
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ if (seekto ((unsigned long) 0) || fread (&bs, sizeof (bs), 1, ifp) != 1) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed whilst reading boot sector");
+ fclose (ifp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ if (bs.boot_jump[0] != 0xEB || bs.boot_jump[1] < 0x16 || bs.boot_jump[2] != 0x90) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ifp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ sectors_per_cluster = (unsigned int) bs.sectors_per_cluster;
+ reserved_sectors = (unsigned int) bs.reserved_sectors[0] | (((unsigned int) bs.reserved_sectors[1]) << 8);
+ number_of_fats = (unsigned int) bs.no_fats;
+ root_entries = (unsigned int) bs.root_entries[0] | (((unsigned int) bs.root_entries[1]) << 8);
+ total_sectors = (unsigned int) bs.total_sectors16[0] | (((unsigned int) bs.total_sectors16[1]) << 8);
+ sectors_per_fat = (unsigned int) bs.sectors_per_fat16[0] | (((unsigned int) bs.sectors_per_fat16[1]) << 8);
+
+ if (!sectors_per_cluster || !reserved_sectors || !number_of_fats) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ifp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ if (!root_entries) {
+
+ if (bs.boot_jump[1] < 0x58) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ifp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ root_cluster = (unsigned int) bs.fat32.root_cluster[0] | (((unsigned int) bs.fat32.root_cluster[1]) << 8) | (((unsigned int) bs.fat32.root_cluster[2]) << 16) | (((unsigned int) bs.fat32.root_cluster[3]) << 24);
+
+ if (!root_cluster) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ifp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ }
+
+ if (!total_sectors) {
+
+ if (bs.boot_jump[1] < 0x22) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ifp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ total_sectors = (unsigned int) bs.total_sectors32[0] | (((unsigned int) bs.total_sectors32[1]) << 8) | (((unsigned int) bs.total_sectors32[2]) << 16) | (((unsigned int) bs.total_sectors32[3]) << 24);
+
+ if (!total_sectors) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ifp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ }
+
+ if (!sectors_per_fat) {
+
+ if (bs.boot_jump[1] < 0x58) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ifp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ sectors_per_fat = (unsigned int) bs.fat32.sectors_per_fat32[0] | (((unsigned int) bs.fat32.sectors_per_fat32[1]) << 8) | (((unsigned int) bs.fat32.sectors_per_fat32[2]) << 16) | (((unsigned int) bs.fat32.sectors_per_fat32[3]) << 24);
+
+ if (!sectors_per_fat) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ifp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ }
+
+ if (root_entries) {
+
+ root_dir = reserved_sectors + (sectors_per_fat * 2);
+ data_area = root_dir + (((root_entries * 32) + (512 - 1)) / 512);
+
+ } else {
+
+ data_area = reserved_sectors + (sectors_per_fat * 2);
+
+ /*root_dir = data_area + ((root_cluster - 2) * sectors_per_cluster);*/
+ /*root_dir = root_cluster;*/
+
+ }
+
+ cluster_count = (total_sectors - data_area) / sectors_per_cluster;
+
+ if (bs.boot_jump[1] == 0x58) {
+
+ info_sector = (unsigned int) bs.fat32.info_sector[0] | (((unsigned int) bs.fat32.info_sector[1]) << 8);
+
+ if (!info_sector) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ifp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ size_fat = 32;
+
+ } else if (cluster_count <= MAX_CLUST_12) {
+ size_fat = 12;
+ } else if (cluster_count >= MIN_CLUST_16 && cluster_count <= MAX_CLUST_16) {
+ size_fat = 16;
+ } else {
+
+ report_at (program_name, 0, REPORT_ERROR, "FAT is not 12, 16 or 32 bits");
+ fclose (ifp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ if (!(scratch = (unsigned char *) malloc (512))) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Out of memory");
+ fclose (ifp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ di.scratch = scratch;
+
+ for (i = 0; i < state->nb_dirs; i++) {
+
+ target = state->dirs[i];
+
+ if (*target == '/' || *target == '\\') {
+ target++;
+ }
+
+ if (open_dir ((char *) target, &di) < 0) {
+
+ fprintf (stderr, "failed to open directory\n");
+ return -1;
+
+ }
+
+ if (state->nb_dirs > 1) {
+
+ if (!*target) {
+ printf ("/\n\n");
+ } else {
+ printf ("%s:\n\n", target);
+ }
+
+ }
+
+ while (!get_next_entry (&di, &de)) {
+
+ /*if (!de.name[0] || de.name[0] == 0xE5 || (de.attr & ATTR_LONG_NAME) == ATTR_LONG_NAME) {*/
+ if (!de.name[0] || de.name[0] == 0xE5) {
+ continue;
+ }
+
+ if ((de.attr & ATTR_VOLUME_ID) == ATTR_VOLUME_ID) {
+ continue;
+ }
+
+ memset (filename, 0, 13);
+
+ for (j = 0, k = 0; j < 11; ++j) {
+
+ if (j < 8 && de.name[j] == ' ') {
+
+ if ((de.attr & ATTR_DIR) != ATTR_DIR && k > 0 && filename[k - 1] != '.') {
+ filename[k++] = '.';
+ }
+
+ } else {
+
+ if (j == 8 && (de.attr & ATTR_DIR) != ATTR_DIR && filename[k - 1] != '.') {
+ filename[k++] = '.';
+ }
+
+ filename[k++] = de.name[j];
+
+ }
+
+ }
+
+ if (state->nb_dirs > 1) {
+ printf (" ");
+ }
+
+ printf ("%s", filename);
+
+ while (k < 12) {
+
+ printf (" ");
+ k++;
+
+ }
+
+ if ((de.attr & ATTR_DIR) == ATTR_DIR) {
+ printf (" <DIR> ");
+ } else {
+ printf (" ");
+ }
+
+ if ((de.attr & ATTR_DIR) == ATTR_DIR) {
+ printf (" ");
+ } else {
+
+ bytes = (unsigned int) de.size[0] | (((unsigned int) de.size[1]) << 8) | (((unsigned int) de.size[2]) << 16) | (((unsigned int) de.size[3]) << 24);
+ printf ("%10u", bytes);
+
+ }
+
+ timestamp = (unsigned short) de.date[0] | (((unsigned short) de.date[1]) << 8);
+ printf (" %04d-%02d-%02d", ((timestamp >> 9) & 0x3f) + 1980, (timestamp >> 5) & 0x0f, timestamp & 0x1f);
+
+ timestamp = (unsigned short) de.time[0] | (((unsigned short) de.time[1]) << 8);
+ printf (" %02d:%02d:%02d", (timestamp >> 11) & 0x3f, (timestamp >> 5) & 0x3f, (timestamp & 0x1f) << 1);
+
+ printf ("\n");
+
+ }
+
+ if (state->nb_dirs > 1 && i < state->nb_dirs - 1) {
+ printf ("\n");
+ }
+
+ }
+
+ free (scratch);
+ fclose (ifp);
+
+ 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 "common.h"
+#include "mmd.h"
+#include "msdos.h"
+#include "report.h"
+#include "write7x.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 2048
+#endif
+
+static FILE *ofp;
+
+struct dir_info {
+
+ unsigned int current_cluster;
+ unsigned int root_cluster;
+
+ unsigned char current_sector;
+ unsigned char current_entry;
+ unsigned char *scratch;
+ unsigned char flags;
+
+};
+
+static struct mmd_state *state = 0;
+static const char *program_name = 0;
+
+static struct msdos_boot_sector bs;
+static int size_fat = 0;
+
+static unsigned int cluster_count = 0;
+static unsigned int data_area = 0;
+static unsigned int info_sector = 0;
+static unsigned int number_of_fats = 0;
+static unsigned int reserved_sectors = 0;
+static unsigned int root_cluster = 0;
+static unsigned int root_dir = 0;
+static unsigned int root_entries = 0;
+static unsigned int sectors_per_cluster = 0;
+static unsigned int sectors_per_fat = 0;
+static unsigned int total_sectors = 0;
+
+struct option {
+
+ const char *name;
+ int index, flags;
+
+};
+
+#define OPTION_NO_ARG 0x0001
+#define OPTION_HAS_ARG 0x0002
+
+enum options {
+
+ OPTION_IGNORED = 1,
+ OPTION_ARCA,
+ OPTION_HELP,
+ OPTION_INPUT,
+ OPTION_OFFSET
+
+};
+
+static struct option opts[] = {
+
+ { "i", OPTION_INPUT, OPTION_HAS_ARG },
+
+ { "-arca", OPTION_ARCA, OPTION_NO_ARG },
+ { "-help", OPTION_HELP, OPTION_NO_ARG },
+ { "-offset", OPTION_OFFSET, 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 (int exitval) {
+
+ if (!program_name) {
+ goto _exit;
+ }
+
+ 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, " --arca Use CHS-alignment (only works for VHD images).\n");
+ fprintf (stderr, " --help Show this help information then exit.\n");
+ fprintf (stderr, " --offset SECTOR Write the filesystem starting at SECTOR.\n");
+
+_exit:
+
+ exit (exitval);
+
+}
+
+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 *pargc, char ***pargv, int optind) {
+
+ char **argv = *pargv;
+ int argc = *pargc;
+
+ struct option *popt;
+ const char *optarg, *r;
+
+ if (argc == optind) {
+ print_help (EXIT_SUCCESS);
+ }
+
+ while (optind < argc) {
+
+ r = argv[optind++];
+
+ if (r[0] != '-' || r[1] == '\0') {
+
+ dynarray_add (&state->dirs, &state->nb_dirs, xstrdup (r));
+ continue;
+
+ }
+
+ for (popt = opts; popt; ++popt) {
+
+ const char *p1 = popt->name;
+ const char *r1 = (r + 1);
+
+ 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_ARCA: {
+
+ state->chs_align = 1;
+ break;
+
+ }
+
+ case OPTION_HELP: {
+
+ print_help (EXIT_SUCCESS);
+ break;
+
+ }
+
+ case OPTION_INPUT: {
+
+ if (state->outfile) {
+
+ report_at (program_name, 0, REPORT_ERROR, "multiple output files provided");
+ exit (EXIT_FAILURE);
+
+ }
+
+ state->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 || (unsigned long) conversion > UINT_MAX) {
+
+ report_at (program_name, 0, REPORT_ERROR, "offset must be between 0 and %u", UINT_MAX);
+ exit (EXIT_FAILURE);
+
+ }
+
+ state->offset = (unsigned long) conversion;
+ break;
+
+ }
+
+ default: {
+
+ report_at (program_name, 0, REPORT_ERROR, "unsupported option '%s'", r);
+ exit (EXIT_FAILURE);
+
+ }
+
+ }
+
+ }
+
+}
+
+
+static int seekto (long offset) {
+ return fseek (ofp, (state->offset * 512) + offset, SEEK_SET);
+}
+
+
+static int create_dir (const char *target, unsigned char *scratch);
+static int get_next_entry (struct dir_info *di, struct msdos_dirent *de);
+static int get_free_dirent (const char *path, struct dir_info *di, struct msdos_dirent *de);
+static int open_dir (const char *target, struct dir_info *di);
+
+static unsigned int get_fat_entry (unsigned char *scratch, unsigned int cluster);
+static int set_fat_entry (unsigned char *scratch, unsigned int cluster, unsigned int value);
+
+static unsigned int get_free_fat (unsigned char *scratch);
+
+static int canonical_to_dir (char *dest, const char *src) {
+
+ static const char invalid_chars[] = "\"*+,./:;<=>?[\\]|";
+
+ int i, j;
+ int namelen = 0, dots = 0, extlen = 0;
+
+ memset (dest, ' ', 11);
+
+ if (*src == '\0' || *src == '.') {
+ return -1;
+ }
+
+ for (j = i = 0; *src != '\0'; i++) {
+
+ int c = (unsigned char) *src++;
+
+ if (c == '/' || c == '\\') {
+ break;
+ }
+
+ if (i >= 12) {
+ return -1;
+ }
+
+ if (i == 0 && c == 0xE5) {
+
+ /**
+ * 0xE5 in the first character of the name is a marker for delected files,
+ * it needs to be translated to 0x05.
+ */
+ c = 0x05;
+
+ } else if (c == '.') {
+
+ if (dots++) {
+ return -1;
+ }
+
+ j = 8;
+ continue;
+
+ }
+
+ if (c <= 0x20 || strchr (invalid_chars, c)) {
+ return -1;
+ }
+
+ if (dots) {
+
+ if (++extlen > 3) {
+ return -1;
+ }
+
+ } else {
+
+ if (++namelen > 8) {
+ return -1;
+ }
+
+ }
+
+ if (c >= 'a' && c <= 'z') {
+ c -= 'a' - 'A';
+ }
+
+ dest[j++] = c;
+
+ }
+
+ return 0;
+
+}
+
+static int create_dir (const char *target, unsigned char *scratch) {
+
+ unsigned char tmppath[PATH_MAX];
+ unsigned char filename[12];
+ unsigned char *p;
+
+ struct dir_info di;
+ struct msdos_dirent de;
+
+ unsigned short date;
+ unsigned short time;
+
+ unsigned int cluster, i;
+ unsigned int dir_sector;
+ unsigned int dir_offset;
+
+ /* Get a local copy of the target. If it's larger than PATH_MAX, abort. */
+ strncpy ((char *) tmppath, (char *) target, PATH_MAX);
+ tmppath[PATH_MAX - 1] = 0;
+
+ if (strcmp ((char *) target, (char *) tmppath)) {
+ return -1;
+ }
+
+ /* Strip leading seperators. */
+ while (tmppath[0] == '/' || tmppath[0] == '\\') {
+ strcpy ((char *) tmppath, (char *) tmppath + 1);
+ }
+
+ /* Parse filename off the end of the suppiled target. */
+ p = tmppath;
+
+ while (*(p++));
+
+ p--;
+
+ while (p > tmppath && *p != '/' && *p != '\\') {
+ p--;
+ }
+
+ if (*p == '/' || *p == '\\') {
+ p++;
+ }
+
+ if (canonical_to_dir ((char *) filename, (char *) p) < 0) {
+
+ fprintf (stderr, "Failed to convert to 8:3\n");
+ return -1;
+
+ }
+
+ if (p > tmppath) {
+ p--;
+ }
+
+ if (*p == '/' || *p == '\\' || p == tmppath) {
+ *p = 0;
+ }
+
+ di.scratch = scratch;
+
+ if (open_dir ((char *) tmppath, &di) < 0) {
+
+ fprintf (stderr, "Failed to open directory\n");
+ return -1;
+
+ }
+
+ while (!get_next_entry (&di, &de)) {
+
+ /*if (!de.name[0] || de.name[0] == 0xE5 || (de.attr & ATTR_LONG_NAME) == ATTR_LONG_NAME) {*/
+ if (!de.name[0] || de.name[0] == 0xE5) {
+ continue;
+ }
+
+ if (!memcmp (de.name, filename, 11)) {
+
+ fprintf (stderr, "%s already exists.\n", target);
+ return -1;
+
+ }
+
+ }
+
+ if (get_free_dirent ((char *) tmppath, &di, &de) < 0) {
+ return -1;
+ }
+
+ date = generate_datestamp ();
+ time = generate_timestamp ();
+
+ memset (&de, 0, sizeof (de));
+ memcpy (de.name, filename, 11);
+
+ cluster = get_free_fat (scratch);
+
+ if (cluster == 0x0FFFFFF7 || set_fat_entry (scratch, cluster, 0x0FFFFFF8) < 0) {
+ return -1;
+ }
+
+ write721_to_byte_array (de.startlo, cluster);
+ write721_to_byte_array (de.starthi, cluster >> 16);
+
+ de.attr = ATTR_DIR;
+
+ write721_to_byte_array (de.ctime, time);
+ write721_to_byte_array (de.cdate, date);
+ write721_to_byte_array (de.adate, date);
+ write721_to_byte_array (de.time, time);
+ write721_to_byte_array (de.date, date);
+
+ if (di.current_cluster == 0) {
+ dir_sector = root_dir + di.current_sector;
+ } else {
+ dir_sector = data_area + ((di.current_cluster - 2) * sectors_per_cluster) + di.current_sector;
+ }
+
+ /*fi->dir_offset = di.current_entry - 1;*/
+ dir_offset = di.current_entry;
+
+ if (seekto ((unsigned long) dir_sector * 512) || fread (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ memcpy (&(((struct msdos_dirent *) scratch)[dir_offset]), &de, sizeof (de));
+
+ if (seekto ((unsigned long) dir_sector * 512) || fwrite (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ memset (scratch, 0, 512);
+
+ for (i = 0; i < sectors_per_cluster; i++) {
+
+ unsigned long offset = (unsigned long) (data_area + ((cluster - 2) * sectors_per_cluster) + i);
+
+ if (seekto (offset * 512)) {
+ return -1;
+ }
+
+ if (fwrite (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ }
+
+ dir_offset = 0;
+ dir_sector = data_area + ((cluster - 2) * sectors_per_cluster);
+
+ memset (de.name, ' ', 11);
+ de.name[0] = '.';
+
+ write721_to_byte_array (de.startlo, cluster);
+ write721_to_byte_array (de.starthi, cluster >> 16);
+
+ memcpy (&(((struct msdos_dirent *) scratch)[dir_offset]), &de, sizeof (de));
+ dir_offset++;
+
+ de.name[1] = '.';
+
+ if (di.current_cluster == root_cluster) {
+ di.current_cluster = 0;
+ }
+
+ if (di.root_cluster == root_cluster) {
+ di.root_cluster = 0;
+ }
+
+ write721_to_byte_array (de.startlo, di.root_cluster);
+ write721_to_byte_array (de.starthi, di.root_cluster >> 16);
+
+ memcpy (&(((struct msdos_dirent *) scratch)[dir_offset]), &de, sizeof (de));
+
+ if (seekto ((unsigned long) dir_sector * 512) || fwrite (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ if (size_fat == 32) {
+
+ struct fat32_fsinfo *info;
+
+ unsigned int free_clusters;
+ unsigned int next_cluster;
+
+ if (seekto ((unsigned long) info_sector * 512) || fread (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ info = (struct fat32_fsinfo *) (scratch + 0x1e0);
+
+ free_clusters = (unsigned int) info->free_clusters[0] | (((unsigned int) info->free_clusters[1]) << 8) | (((unsigned int) info->free_clusters[2]) << 16) | (((unsigned int) info->free_clusters[3]) << 24);
+ next_cluster = (unsigned int) info->next_cluster[0] | (((unsigned int) info->next_cluster[1]) << 8) | (((unsigned int) info->next_cluster[2]) << 16) | (((unsigned int) info->next_cluster[3]) << 24);
+
+ free_clusters--;
+ next_cluster++;
+
+ write741_to_byte_array (info->free_clusters, free_clusters);
+ write741_to_byte_array (info->next_cluster, next_cluster);
+
+ if (seekto ((unsigned long) info_sector * 512) || fwrite (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ }
+
+ return 0;
+
+}
+
+static int get_free_dirent (const char *path, struct dir_info *di, struct msdos_dirent *de) {
+
+ int entry;
+ unsigned int i, tempclust;
+
+ if (open_dir (path, di) < 0) {
+ return -1;
+ }
+
+ entry = 0;
+ di->flags |= 0x01;
+
+ do {
+
+ entry = get_next_entry (di, de);
+
+ /*if (entry == 0 && (!de->name[0] || de->name[0] == 0xE5 || (de->attr & ATTR_LONG_NAME) == ATTR_LONG_NAME)) {*/
+ if (entry == 0 && (!de->name[0] || de->name[0] == 0xE5)) {
+
+ /*if (de->name[0] == 0xE5 || (de->attr & ATTR_LONG_NAME) == ATTR_LONG_NAME) {*/
+ if (de->name[0] == 0xE5) {
+ di->current_entry--;
+ }
+
+ return 0;
+
+ } else if (entry == -1) {
+ return -1;
+ } else if (entry == 1) {
+
+ if ((tempclust = get_free_fat (di->scratch) == 0x0FFFFFF7)) {
+ return -1;
+ }
+
+ memset (di->scratch, 0, 512);
+
+ for (i = 0; i < sectors_per_cluster; i++) {
+
+ unsigned long offset = (unsigned long) (data_area + ((tempclust - 2) * sectors_per_cluster) + i);
+
+ if (seekto (offset * 512)) {
+ return -1;
+ }
+
+ if (fwrite (di->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ }
+
+ i = 0;
+
+ if (set_fat_entry (di->scratch, di->current_cluster, tempclust) < 0) {
+ return -1;
+ }
+
+ di->current_cluster = tempclust;
+ di->current_entry = 0;
+ di->current_sector = 0;
+
+ if (size_fat == 12) {
+ tempclust = 0x0FF8;
+ } else if (size_fat == 16) {
+ tempclust = 0xFFF8;
+ } else if (size_fat == 32) {
+
+ struct fat32_fsinfo *info;
+
+ unsigned int free_clusters;
+ unsigned int next_cluster;
+
+ tempclust = 0x0ffffff8;
+
+ if (seekto ((unsigned long) info_sector * 512) || fread (di->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ info = (struct fat32_fsinfo *) (di->scratch + 0x1e0);
+
+ free_clusters = (unsigned int) info->free_clusters[0] | (((unsigned int) info->free_clusters[1]) << 8) | (((unsigned int) info->free_clusters[2]) << 16) | (((unsigned int) info->free_clusters[3]) << 24);
+ next_cluster = (unsigned int) info->next_cluster[0] | (((unsigned int) info->next_cluster[1]) << 8) | (((unsigned int) info->next_cluster[2]) << 16) | (((unsigned int) info->next_cluster[3]) << 24);
+
+ free_clusters--;
+ next_cluster++;
+
+ write741_to_byte_array (info->free_clusters, free_clusters);
+ write741_to_byte_array (info->next_cluster, next_cluster);
+
+ if (seekto ((unsigned long) info_sector * 512) || fwrite (di->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ } else {
+ return -1;
+ }
+
+ }
+
+ } while (!entry);
+
+ /* We should't get here! */
+ return -1;
+
+}
+
+static int get_next_entry (struct dir_info *di, struct msdos_dirent *de) {
+
+ unsigned long offset;
+
+ if (di->current_entry >= 512 / sizeof (*de)) {
+
+ di->current_entry = 0;
+ di->current_sector++;
+
+ if (di->current_cluster == 0) {
+
+ unsigned long offset;
+
+ if (di->current_sector * (512 / sizeof (*de)) >= root_entries) {
+ return -1;
+ }
+
+ offset = (unsigned long) root_dir + di->current_sector;
+
+ if (seekto (offset * 512) || fread (di->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ } else {
+
+ if (di->current_sector >= sectors_per_cluster) {
+
+ di->current_sector = 0;
+
+ if ((size_fat == 12 && di->current_cluster >= 0x0FF7) || (size_fat == 16 && di->current_cluster >= 0xFFF7) || (size_fat == 32 && di->current_cluster >= 0x0FFFFFF7)) {
+
+ if (!(di->flags & 0x01)) {
+ return -1;
+ }
+
+ return 1;
+
+ }
+
+ di->current_cluster = get_fat_entry (di->scratch, di->current_cluster);
+
+ }
+
+ offset = (unsigned long) data_area;
+ offset += ((di->current_cluster - 2) * sectors_per_cluster);
+ offset += di->current_sector;
+
+ if (seekto (offset * 512) || fread (di->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ }
+
+ }
+
+ memcpy (de, &(((struct msdos_dirent *) di->scratch)[di->current_entry]), sizeof (*de));
+
+ if (de->name[0] == 0) {
+
+ if (di->flags & 0x01) {
+ return 0;
+ }
+
+ return -1;
+
+ }
+
+ if (de->name[0] == 0x05) {
+
+ de->name[0] = 0xE5;
+ return 0;
+
+ }
+
+ di->current_entry++;
+ return 0;
+
+}
+
+static int open_dir (const char *target, struct dir_info *di) {
+
+ di->flags = 0;
+
+ if (!strlen ((char *) target) || (strlen ((char *) target) == 1 && (target[0] == '/' || target[0] == '\\'))) {
+
+ unsigned long offset;
+
+ if (size_fat == 32) {
+
+ di->current_cluster = root_cluster;
+ di->root_cluster = root_cluster;
+ di->current_entry = 0;
+ di->current_sector = 0;
+
+ offset = (unsigned long) data_area + ((di->current_cluster - 2) * sectors_per_cluster);
+
+ } else {
+
+ di->current_cluster = 0;
+ di->root_cluster = 0;
+ di->current_entry = 0;
+ di->current_sector = 0;
+
+ offset = (unsigned long) root_dir;
+
+ }
+
+ if (seekto (offset * 512) || fread (di->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ } else {
+
+ unsigned char tmpfn[12];
+ unsigned char *ptr;
+
+ struct msdos_dirent de;
+ unsigned int result;
+
+ unsigned long offset;
+
+ if (size_fat == 32) {
+
+ di->current_cluster = root_cluster;
+ di->current_entry = 0;
+ di->current_sector = 0;
+
+ offset = (unsigned long) data_area + ((di->current_cluster - 2) * sectors_per_cluster);
+
+ } else {
+
+ di->current_cluster = 0;
+ di->current_entry = 0;
+ di->current_sector = 0;
+
+ offset = (unsigned long) root_dir;
+
+ }
+
+ if (seekto (offset * 512) || fread (di->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ ptr = (unsigned char *) target;
+
+ while ((*ptr == '/' || *ptr == '\\') && *ptr) {
+ ptr++;
+ }
+
+ while (*ptr) {
+
+ if (canonical_to_dir ((char *) tmpfn, (char *) ptr) < 0) {
+
+ fprintf (stderr, "Failed to convert to 8:3\n");
+ return -1;
+
+ }
+
+ de.name[0] = 0;
+
+ do {
+ result = get_next_entry (di, &de);
+ } while (!result && memcmp (de.name, tmpfn, 11));
+
+ if (!memcmp (de.name, tmpfn, 11) && (de.attr & ATTR_DIR) == ATTR_DIR) {
+
+ unsigned long offset;
+
+ di->current_cluster = (unsigned int) de.startlo[0] | ((unsigned int) de.startlo[1]) << 8 | ((unsigned int) de.starthi[0]) << 16 | ((unsigned int) de.starthi[1]) << 24;
+ di->root_cluster = di->current_cluster;
+ di->current_entry = 0;
+ di->current_sector = 0;
+
+ offset = (unsigned long) (data_area + ((di->current_cluster - 2) * sectors_per_cluster));
+
+ if (seekto (offset * 512)) {
+ return -1;
+ }
+
+ if (fread (di->scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ } else if (!memcmp (de.name, tmpfn, 11) && !(de.attr & ATTR_DIR)) {
+ return -1;
+ }
+
+ while (*ptr != '/' && *ptr != '\\' && *ptr) {
+ ptr++;
+ }
+
+ if (*ptr == '/' || *ptr == '\\') {
+ ptr++;
+ }
+
+ }
+
+ if (!di->current_cluster) {
+ return -1;
+ }
+
+ }
+
+ return 0;
+
+}
+
+static int set_fat_entry (unsigned char *scratch, unsigned int cluster, unsigned int value) {
+
+ unsigned int i, offset, sector;
+
+ if (size_fat == 12) {
+
+ offset = cluster + (cluster / 2);
+ value &= 0x0fff;
+
+ } else if (size_fat == 16) {
+
+ offset = cluster * 2;
+ value &= 0xffff;
+
+ } else if (size_fat == 32) {
+
+ offset = cluster * 4;
+ value &= 0x0fffffff;
+
+ } else {
+ return -1;
+ }
+
+ /**
+ * At this point, offset is the BYTE offset of the desired sector from the start
+ * of the FAT. Calculate the physical sector containing this FAT entry.
+ */
+ sector = (offset / 512) + reserved_sectors;
+
+ if (seekto (sector * 512) || fread (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ /**
+ * At this point, we "merely" need to extract the relevant entry. This is
+ * easy for FAT16 and FAT32, but a royal PITA for FAT12 as a single entry
+ * may span a sector boundary. The normal way around this is always to
+ * read two FAT sectors, but luxary is (by design intent) unavailable.
+ */
+ offset %= 512;
+
+ if (size_fat == 12) {
+
+ if (offset == 511) {
+
+ if (((cluster * 3) & 0x01) == 0) {
+ scratch[offset] = (unsigned char) (value & 0xFF);
+ } else {
+ scratch[offset] = (unsigned char) ((scratch[offset] & 0x0F) | (value & 0xF0));
+ }
+
+ for (i = 0; i < number_of_fats; i++) {
+
+ unsigned long temp = sector + (i * sectors_per_fat);
+
+ if (seekto (temp * 512) < 0 || fwrite (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ }
+
+ sector++;
+
+ if (seekto (sector) || fread (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ if (((cluster * 3) & 0x01) == 0) {
+ scratch[0] = (unsigned char) ((scratch[0] & 0xF0) | (value & 0x0F));
+ } else {
+ scratch[0] = (unsigned char) (value & 0xFF00);
+ }
+
+ goto _write_fat;
+
+ } else {
+
+ if (((cluster * 3) & 0x01) == 0) {
+
+ scratch[offset] = (unsigned char) (value & 0x00FF);
+ scratch[offset + 1] = (unsigned char) ((scratch[offset + 1] & 0x00F0) | ((value & 0x0F00) >> 8));
+
+ } else {
+
+ scratch[offset] = (unsigned char) ((scratch[offset] & 0x000F) | ((value & 0x000F) << 4));
+ scratch[offset + 1] = (unsigned char) ((value & 0x0FF0) >> 4);
+
+ }
+
+ goto _write_fat;
+
+ }
+
+ } else if (size_fat == 16) {
+
+ scratch[offset] = (value & 0xFF);
+ scratch[offset + 1] = (value >> 8) & 0xFF;
+
+ goto _write_fat;
+
+ } else if (size_fat == 32) {
+
+ scratch[offset] = (value & 0xFF);
+ scratch[offset + 1] = (value >> 8) & 0xFF;
+ scratch[offset + 2] = (value >> 16) & 0xFF;
+ scratch[offset + 3] = (scratch[offset + 3] & 0xF0) | ((value >> 24) & 0xFF);
+
+ goto _write_fat;
+
+ }
+
+ return -1;
+
+_write_fat:
+
+ for (i = 0; i < number_of_fats; i++) {
+
+ unsigned long temp = sector + (i * sectors_per_fat);
+
+ if (seekto (temp * 512) || fwrite (scratch, 512, 1, ofp) != 1) {
+ return -1;
+ }
+
+ }
+
+ return 0;
+
+}
+
+static unsigned int get_fat_entry (unsigned char *scratch, unsigned int cluster) {
+
+ unsigned int offset, sector, result;
+
+ if (size_fat == 12) {
+ offset = cluster + (cluster / 2);
+ } else if (size_fat == 16) {
+ offset = cluster * 2;
+ } else if (size_fat == 32) {
+ offset = cluster * 4;
+ } else {
+ return 0x0FFFFFF7;
+ }
+
+ sector = (offset / 512) + reserved_sectors;
+
+ if (seekto ((unsigned long) sector * 512) || fread (scratch, 512, 1, ofp) != 1) {
+ return 0x0FFFFFF7;
+ }
+
+ offset %= 512;
+
+ if (size_fat == 12) {
+
+ if (offset == 511) {
+
+ result = (unsigned int) scratch[offset];
+ sector++;
+
+ if (seekto ((unsigned long) sector * 512) || fread (scratch, 512, 1, ofp) != 1) {
+ return 0x0FFFFFF7;
+ }
+
+ result |= ((unsigned int) scratch[0]) << 8;
+
+ } else {
+ result = (unsigned int) scratch[offset] | ((unsigned int) scratch[offset + 1]) << 8;
+ }
+
+ if (cluster & 1) {
+ result = result >> 4;
+ } else {
+ result = result & 0x0FFF;
+ }
+
+ } else if (size_fat == 16) {
+ result = (unsigned int) scratch[offset] | ((unsigned int) scratch[offset + 1]) << 8;
+ } else if (size_fat == 32) {
+ result = ((unsigned int) scratch[offset] | ((unsigned int) scratch[offset + 1]) << 8 | ((unsigned int) scratch[offset + 2]) << 16 | ((unsigned int) scratch[offset + 3]) << 24) & 0x0FFFFFFF;
+ } else {
+ result = 0x0FFFFFF7;
+ }
+
+ return result;
+
+}
+
+static unsigned int get_free_fat (unsigned char *scratch) {
+
+ unsigned int i, result = 0xFFFFFFFF;
+
+ for (i = 2; i < cluster_count; i++) {
+
+ result = get_fat_entry (scratch, i);
+
+ if (!result) {
+ return i;
+ }
+
+ }
+
+ return 0x0FFFFFF7;
+
+}
+
+struct vhd_footer {
+
+ unsigned char cookie[8];
+ unsigned char features[4];
+
+ struct {
+
+ unsigned char major[2];
+ unsigned char minor[2];
+
+ } version;
+
+ unsigned char next_offset[8];
+ unsigned char modified_time[4];
+ unsigned char creator_name[4];
+
+ struct {
+
+ unsigned char major[2];
+ unsigned char minor[2];
+
+ } creator_version;
+
+ unsigned char creator_host[4];
+ unsigned char disk_size[8];
+ unsigned char data_size[8];
+
+ struct {
+
+ unsigned char cylinders[2];
+ unsigned char heads_per_cyl;
+ unsigned char secs_per_track;
+
+ } disk_geom;
+
+ unsigned char disk_type[4];
+ unsigned char checksum[4];
+ unsigned char identifier[16];
+ unsigned char saved_state;
+ unsigned char reserved[427];
+
+};
+
+int main (int argc, char **argv) {
+
+ unsigned char *scratch;
+ char *target;
+
+ struct vhd_footer footer;
+ long i;
+
+ if (argc && *argv) {
+
+ char *p;
+ program_name = *argv;
+
+ if ((p = strrchr (program_name, '/')) || (p = strrchr (program_name, '\\'))) {
+ program_name = (p + 1);
+ }
+
+ }
+
+ state = xmalloc (sizeof (*state));
+ parse_args (&argc, &argv, 1);
+
+ if (!state->outfile || state->nb_dirs == 0) {
+ print_help (EXIT_FAILURE);
+ }
+
+ if ((ofp = fopen (state->outfile, "r+b")) == NULL) {
+
+ report_at (program_name, 0, REPORT_ERROR, "faild to open '%s' for writing", state->outfile);
+ return EXIT_FAILURE;
+
+ }
+
+ if (state->chs_align) {
+
+ long size = sizeof (footer), offset;
+ fseek (ofp, 0, SEEK_END);
+
+ offset = ftell (ofp) - size;
+
+ if (!fseek (ofp, offset, SEEK_SET)) {
+
+ if (fread (&footer, size, 1, ofp) == 1) {
+
+ if (footer.cookie[0] == 0x63 && footer.cookie[1] == 0x6F && footer.cookie[2] == 0x6E && footer.cookie[3] == 0x65 && footer.cookie[4] == 0x63 && footer.cookie[5] == 0x74 && footer.cookie[6] == 0x69 && footer.cookie[7] == 0x78) {
+
+ long old = state->offset, secs_per_track = footer.disk_geom.secs_per_track;
+
+ if (state->offset % secs_per_track) {
+
+ state->offset = ((state->offset / secs_per_track) + 1) * secs_per_track;
+ report_at (program_name, 0, REPORT_WARNING, "offset changed from %lu to %lu", old, state->offset);
+
+ }
+
+ }
+
+ }
+
+ }
+
+ }
+
+ if (seekto ((unsigned long) 0) || fread (&bs, sizeof (bs), 1, ofp) != 1) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed whilst reading boot sector");
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ if (bs.boot_jump[0] != 0xEB || bs.boot_jump[1] < 0x16 || bs.boot_jump[2] != 0x90) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ sectors_per_cluster = (unsigned int) bs.sectors_per_cluster;
+ reserved_sectors = (unsigned int) bs.reserved_sectors[0] | (((unsigned int) bs.reserved_sectors[1]) << 8);
+ number_of_fats = (unsigned int) bs.no_fats;
+ root_entries = (unsigned int) bs.root_entries[0] | (((unsigned int) bs.root_entries[1]) << 8);
+ total_sectors = (unsigned int) bs.total_sectors16[0] | (((unsigned int) bs.total_sectors16[1]) << 8);
+ sectors_per_fat = (unsigned int) bs.sectors_per_fat16[0] | (((unsigned int) bs.sectors_per_fat16[1]) << 8);
+
+ if (!sectors_per_cluster || !reserved_sectors || !number_of_fats) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ if (!root_entries) {
+
+ if (bs.boot_jump[1] < 0x58) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ root_cluster = (unsigned int) bs.fat32.root_cluster[0] | (((unsigned int) bs.fat32.root_cluster[1]) << 8) | (((unsigned int) bs.fat32.root_cluster[2]) << 16) | (((unsigned int) bs.fat32.root_cluster[3]) << 24);
+
+ if (!root_cluster) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ }
+
+ if (!total_sectors) {
+
+ if (bs.boot_jump[1] < 0x22) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ total_sectors = (unsigned int) bs.total_sectors32[0] | (((unsigned int) bs.total_sectors32[1]) << 8) | (((unsigned int) bs.total_sectors32[2]) << 16) | (((unsigned int) bs.total_sectors32[3]) << 24);
+
+ if (!total_sectors) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ }
+
+ if (!sectors_per_fat) {
+
+ if (bs.boot_jump[1] < 0x58) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ sectors_per_fat = (unsigned int) bs.fat32.sectors_per_fat32[0] | (((unsigned int) bs.fat32.sectors_per_fat32[1]) << 8) | (((unsigned int) bs.fat32.sectors_per_fat32[2]) << 16) | (((unsigned int) bs.fat32.sectors_per_fat32[3]) << 24);
+
+ if (!sectors_per_fat) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ }
+
+ if (root_entries) {
+
+ root_dir = reserved_sectors + (sectors_per_fat * 2);
+ data_area = root_dir + (((root_entries * 32) + (512 - 1)) / 512);
+
+ } else {
+
+ data_area = reserved_sectors + (sectors_per_fat * 2);
+
+ /*root_dir = data_area + ((root_cluster - 2) * sectors_per_cluster);*/
+ /*root_dir = root_cluster;*/
+
+ }
+
+ cluster_count = (total_sectors - data_area) / sectors_per_cluster;
+
+ if (bs.boot_jump[1] == 0x58) {
+
+ info_sector = (unsigned int) bs.fat32.info_sector[0] | (((unsigned int) bs.fat32.info_sector[1]) << 8);
+
+ if (!info_sector) {
+
+ report_at (program_name, 0, REPORT_ERROR, "%s does not have a valid FAT boot sector", state->outfile);
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ size_fat = 32;
+
+ } else if (cluster_count <= MAX_CLUST_12) {
+ size_fat = 12;
+ } else if (cluster_count >= MIN_CLUST_16 && cluster_count <= MAX_CLUST_16) {
+ size_fat = 16;
+ } else {
+
+ report_at (program_name, 0, REPORT_ERROR, "FAT is not 12, 16 or 32 bits");
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ if (!(scratch = (unsigned char *) malloc (512))) {
+
+ report_at (program_name, 0, REPORT_ERROR, "Out of memory");
+ fclose (ofp);
+
+ return EXIT_FAILURE;
+
+ }
+
+ for (i = 0; i < state->nb_dirs; ++i) {
+
+ target = state->dirs[i];
+
+ if (*target == '/' || *target == '\\') {
+ target++;
+ }
+
+ if (create_dir (target, scratch) < 0) {
+
+ report_at (program_name, 0, REPORT_ERROR, "failed to create %s", target);
+ free (scratch);
+
+ fclose (ofp);
+ return EXIT_FAILURE;
+
+ }
+
+ }
+
+ free (scratch);
+ fclose (ofp);
+
+ return EXIT_SUCCESS;
+
+}
--- /dev/null
+/******************************************************************************
+ * @file mmd.h
+ *****************************************************************************/
+#ifndef _MMD_H
+#define _MMD_H
+
+struct mmd_state {
+
+ char **dirs;
+ long nb_dirs;
+
+ const char *outfile;
+ unsigned long offset;
+
+ int chs_align;
+
+};
+
+#endif /* _MMD_H */
--- /dev/null
+/******************************************************************************
+ * @file msdos.h
+ *****************************************************************************/
+#ifndef _MSDOS_H
+#define _MSDOS_H
+
+/**
+ * According to Microsoft FAT specification (fatgen103.doc) disk with
+ * 4085 clusters (or more) is FAT16, but Microsoft Windows FAT driver
+ * fastfat.sys detects disk with less then 4087 clusters as FAT12.
+ * Linux FAT drivers msdos.ko and vfat.ko detect disk with at least
+ * 4085 clusters as FAT16, therefore for compatibility reasons with
+ * both systems disallow formatting disks to 4085 or 4086 clusters.
+ */
+#define MAX_CLUST_12 4084
+#define MIN_CLUST_16 4087
+
+/**
+ * According to Microsoft FAT specification (fatgen103.doc) disk with
+ * 65525 clusters (or more) is FAT32, but Microsoft Windows FAT driver
+ * fastfat.sys, Linux FAT drivers msdos.ko and vfat.ko detect disk as
+ * FAT32 when Sectors Per FAT (fat_length) is set to zero. And not by
+ * number of clusters. Still there is cluster upper limit for FAT16.
+ */
+#define MAX_CLUST_16 65524
+#define MIN_CLUST_32 65525
+
+/**
+ * M$ says the high 4 bits of a FAT32 FAT entry are reserved and don't belong
+ * to the cluster number. So the max. cluster# is based on 2^28
+ */
+#define MAX_CLUST_32 268435446
+
+/** File Attributes. */
+#define ATTR_NONE 0x00
+#define ATTR_READONLY 0x01
+#define ATTR_HIDDEN 0x02
+#define ATTR_SYSTEM 0x04
+#define ATTR_VOLUME_ID 0x08
+#define ATTR_DIR 0x10
+#define ATTR_ARCHIVE 0x20
+/*#define ATTR_LONG_NAME (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID)*/
+
+struct msdos_volume_info {
+
+ unsigned char drive_no;
+ unsigned char boot_flags;
+ unsigned char ext_boot_sign;
+ unsigned char volume_id[4];
+ unsigned char volume_label[11];
+ unsigned char fs_type[8];
+
+};
+
+struct msdos_boot_sector {
+
+ unsigned char boot_jump[3];
+ unsigned char system_id[8];
+ unsigned char bytes_per_sector[2];
+ unsigned char sectors_per_cluster;
+ unsigned char reserved_sectors[2];
+ unsigned char no_fats;
+ unsigned char root_entries[2];
+ unsigned char total_sectors16[2];
+ unsigned char media_descriptor;
+ unsigned char sectors_per_fat16[2];
+ unsigned char sectors_per_track[2];
+ unsigned char heads_per_cylinder[2];
+ unsigned char hidden_sectors[4];
+ unsigned char total_sectors32[4];
+
+ union {
+
+ struct {
+
+ struct msdos_volume_info vi;
+ unsigned char boot_code[448];
+
+ } _oldfat;
+
+ struct {
+
+ unsigned char sectors_per_fat32[4];
+ unsigned char flags[2];
+ unsigned char version[2];
+ unsigned char root_cluster[4];
+ unsigned char info_sector[2];
+ unsigned char backup_boot[2];
+ unsigned short reserved2[6];
+
+ struct msdos_volume_info vi;
+ unsigned char boot_code[420];
+
+ } _fat32;
+
+ } fstype;
+
+ unsigned char boot_sign[2];
+
+};
+
+#define oldfat fstype._oldfat
+#define fat32 fstype._fat32
+
+struct fat32_fsinfo {
+
+ unsigned char reserved1[4];
+ unsigned char signature[4];
+ unsigned char free_clusters[4];
+ unsigned char next_cluster[4];
+
+};
+
+struct msdos_dirent {
+
+ unsigned char name[11];
+ unsigned char attr;
+ unsigned char lcase;
+ unsigned char ctime_cs;
+ unsigned char ctime[2];
+ unsigned char cdate[2];
+ unsigned char adate[2];
+ unsigned char starthi[2];
+ unsigned char time[2];
+ unsigned char date[2];
+ unsigned char startlo[2];
+ unsigned char size[4];
+
+};
+
+#endif /* _MSDOS_H */
--- /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 write7x.c
+ *****************************************************************************/
+#include "write7x.h"
+
+void write721_to_byte_array (unsigned char *dest, unsigned short val) {
+
+ dest[0] = (val & 0xFF);
+ dest[1] = (val >> 8) & 0xFF;
+
+}
+
+void write741_to_byte_array (unsigned char *dest, unsigned int val) {
+
+ dest[0] = (val & 0xFF);
+ dest[1] = (val >> 8) & 0xFF;
+ dest[2] = (val >> 16) & 0xFF;
+ dest[3] = (val >> 24) & 0xFF;
+
+}
--- /dev/null
+/******************************************************************************
+ * @file write7x.h
+ *****************************************************************************/
+#ifndef _WRITE7X_H
+#define _WRITE7X_H
+
+void write721_to_byte_array (unsigned char *dest, unsigned short val);
+void write741_to_byte_array (unsigned char *dest, unsigned int val);
+
+#endif /* _WRITE7X_H */