From: Robert Pengelly Date: Mon, 3 Jun 2024 03:32:25 +0000 (+0100) Subject: New server X-Git-Url: https://git.candlhat.org/?a=commitdiff_plain;h=e7821f2e5118c8cf4a9c8cf24ad30ea3da1da174;p=dosfstools.git New server --- e7821f2e5118c8cf4a9c8cf24ad30ea3da1da174 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/Makefile.pdw b/Makefile.pdw new file mode 100644 index 0000000..07da597 --- /dev/null +++ b/Makefile.pdw @@ -0,0 +1,34 @@ +#****************************************************************************** +# @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 diff --git a/Makefile.std b/Makefile.std new file mode 100644 index 0000000..5d4980e --- /dev/null +++ b/Makefile.std @@ -0,0 +1,35 @@ +#****************************************************************************** +# @file Makefile.std +#****************************************************************************** +AS=pdas --oformat coff +CC=gccwin +LD=pdld --no-insert-timestamp + +COPTS=-S -O2 -fno-common -ansi -I. -I../pdos/pdpclib -D__WIN32__ -D__NOBIVA__ -D__PDOS__ +COBJ=common.obj report.obj write7x.obj + +all: clean mkdosfs.exe mcopy.exe mmd.exe mls.exe + +mkdosfs.exe: lib.obj mkfs.obj $(COBJ) + $(LD) -s -o mkdosfs.exe ../pdos/pdpclib/w32start.obj lib.obj mkfs.obj $(COBJ) ../pdos/pdpclib/msvcrt.lib + +mcopy.exe: mcopy.obj $(COBJ) + $(LD) -s -o mcopy.exe ../pdos/pdpclib/w32start.obj mcopy.obj $(COBJ) ../pdos/pdpclib/msvcrt.lib + +mmd.exe: mmd.obj $(COBJ) + $(LD) -s -o mmd.exe ../pdos/pdpclib/w32start.obj mmd.obj $(COBJ) ../pdos/pdpclib/msvcrt.lib + +mls.exe: mls.obj $(COBJ) + $(LD) -s -o mls.exe ../pdos/pdpclib/w32start.obj mls.obj $(COBJ) ../pdos/pdpclib/msvcrt.lib + +.c.obj: + $(CC) $(COPTS) $< + $(AS) -o $@ $*.s + rm -f $*.s + +clean: + rm -f *.obj + rm -f mkdosfs.exe + rm -f mcopy.exe + rm -f mmd.exe + rm -f mls.exe diff --git a/Makefile.unix b/Makefile.unix new file mode 100644 index 0000000..55b2bde --- /dev/null +++ b/Makefile.unix @@ -0,0 +1,53 @@ +#****************************************************************************** +# @file Makefile.unix +#****************************************************************************** +SRCDIR ?= $(CURDIR) +VPATH := $(SRCDIR) + +CC := gcc +CFLAGS := -D_FILE_OFFSET_BITS=64 -Wall -Werror -Wextra -std=c90 + +CSRC := common.c report.c write7x.c + +ifeq ($(OS), Windows_NT) +all: 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 diff --git a/Makefile.w32 b/Makefile.w32 new file mode 100644 index 0000000..b1ac3ba --- /dev/null +++ b/Makefile.w32 @@ -0,0 +1,37 @@ +#****************************************************************************** +# @file Makefile.w32 +#****************************************************************************** +SRCDIR ?= $(CURDIR) +VPATH := $(SRCDIR) + +CC := gcc +CFLAGS := -D_FILE_OFFSET_BITS=64 -Wall -Werror -Wextra -std=c90 + +CSRC := common.c report.c write7x.c + +all: 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 $@ $^ diff --git a/README.md b/README.md new file mode 100644 index 0000000..ed1e864 --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +All source code is Public Domain. + +## Obtain the source code + + git clone https://git.candlhat.org/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. diff --git a/common.c b/common.c new file mode 100644 index 0000000..35241d6 --- /dev/null +++ b/common.c @@ -0,0 +1,84 @@ +/****************************************************************************** + * @file common.c + *****************************************************************************/ +#include +#include +#include +#include +#include +#include + +#ifndef __PDOS__ +# if defined (__GNUC__) +# include +# include +# else +# include +# 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; + +} diff --git a/common.h b/common.h new file mode 100644 index 0000000..830c2c3 --- /dev/null +++ b/common.h @@ -0,0 +1,10 @@ +/****************************************************************************** + * @file common.h + *****************************************************************************/ +#ifndef _COMMON_H +#define _COMMON_H + +extern unsigned short generate_datestamp (void); +extern unsigned short generate_timestamp (void); + +#endif /* _COMMON_H */ diff --git a/lib.c b/lib.c new file mode 100644 index 0000000..50f7dd1 --- /dev/null +++ b/lib.c @@ -0,0 +1,471 @@ +/****************************************************************************** + * @file lib.c + *****************************************************************************/ +#include +#include +#include +#include +#include +#include + +#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); + } + +} diff --git a/lib.h b/lib.h new file mode 100644 index 0000000..980675d --- /dev/null +++ b/lib.h @@ -0,0 +1,16 @@ +/****************************************************************************** + * @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 */ diff --git a/mcopy.c b/mcopy.c new file mode 100644 index 0000000..9db14f0 --- /dev/null +++ b/mcopy.c @@ -0,0 +1,2342 @@ +/****************************************************************************** + * @file mcopy.c + *****************************************************************************/ +#include +#include +#include +#include +#include +#include + +#ifndef __PDOS__ +# if defined (_WIN32) +# include +# include +# elif defined (__GNUC__) +# include +# include +# include +# if defined (__CYGWIN__) +# include +# 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); + +} diff --git a/mcopy.h b/mcopy.h new file mode 100644 index 0000000..dee7b92 --- /dev/null +++ b/mcopy.h @@ -0,0 +1,21 @@ +/****************************************************************************** + * @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 */ diff --git a/mkfs.c b/mkfs.c new file mode 100644 index 0000000..3113607 --- /dev/null +++ b/mkfs.c @@ -0,0 +1,1361 @@ +/****************************************************************************** + * @file mkfs.c + *****************************************************************************/ +#include +#include +#include +#include + +#include "common.h" +#include "lib.h" +#include "mkfs.h" +#include "msdos.h" +#include "report.h" +#include "write7x.h" + +#ifndef __PDOS__ +# if defined (__GNUC__) +# include +# include +# else +# include +# 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; + +} diff --git a/mkfs.h b/mkfs.h new file mode 100644 index 0000000..8f09e21 --- /dev/null +++ b/mkfs.h @@ -0,0 +1,23 @@ +/****************************************************************************** + * @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 */ diff --git a/mls.c b/mls.c new file mode 100644 index 0000000..5b1d9c6 --- /dev/null +++ b/mls.c @@ -0,0 +1,1068 @@ +/****************************************************************************** + * @file mls.c + *****************************************************************************/ +#include +#include +#include +#include +#include +#include + +#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 (" "); + } 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; + +} diff --git a/mmd.c b/mmd.c new file mode 100644 index 0000000..65780d4 --- /dev/null +++ b/mmd.c @@ -0,0 +1,1436 @@ +/****************************************************************************** + * @file mmd.c + *****************************************************************************/ +#include +#include +#include +#include +#include +#include + +#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; + +} diff --git a/mmd.h b/mmd.h new file mode 100644 index 0000000..0286400 --- /dev/null +++ b/mmd.h @@ -0,0 +1,19 @@ +/****************************************************************************** + * @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 */ diff --git a/msdos.h b/msdos.h new file mode 100644 index 0000000..1b4464b --- /dev/null +++ b/msdos.h @@ -0,0 +1,131 @@ +/****************************************************************************** + * @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 */ diff --git a/report.c b/report.c new file mode 100644 index 0000000..8e128ef --- /dev/null +++ b/report.c @@ -0,0 +1,150 @@ +/****************************************************************************** + * @file report.c + *****************************************************************************/ +#include +#include +#include + +#include "report.h" + +unsigned long errors = 0; + +#ifndef __PDOS__ +#if defined (_WIN32) +# include +static int OriginalConsoleColor = -1; +#endif + +static void reset_console_color (void) { + +#if defined (_WIN32) + + HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE); + + if (OriginalConsoleColor == -1) { return; } + + SetConsoleTextAttribute (hStdError, OriginalConsoleColor); + OriginalConsoleColor = -1; + +#else + + fprintf (stderr, "\033[0m"); + +#endif + +} + +static void set_console_color (int color) { + +#if defined (_WIN32) + + HANDLE hStdError = GetStdHandle (STD_ERROR_HANDLE); + WORD wColor; + + if (OriginalConsoleColor == -1) { + + CONSOLE_SCREEN_BUFFER_INFO csbi; + + if (!GetConsoleScreenBufferInfo (hStdError, &csbi)) { + return; + } + + OriginalConsoleColor = csbi.wAttributes; + + } + + wColor = (OriginalConsoleColor & 0xF0) + (color & 0xF); + SetConsoleTextAttribute (hStdError, wColor); + +#else + + fprintf (stderr, "\033[%dm", color); + +#endif + +} +#endif + +static void output_message (const char *filename, unsigned long lineno, unsigned long idx, enum report_type type, const char *fmt, va_list ap) { + + if (filename) { + + if (lineno == 0 && idx == 0) { + fprintf (stderr, "%s: ", filename); + } else { + fprintf (stderr, "%s:", filename); + } + + } + + if (lineno > 0) { + + if (idx == 0) { + fprintf (stderr, "%lu: ", lineno); + } else { + fprintf (stderr, "%lu:", lineno); + } + + } + + if (idx > 0) { + fprintf (stderr, "%lu: ", idx); + } + + if (type == REPORT_ERROR || type == REPORT_FATAL_ERROR) { + +#ifndef __PDOS__ + set_console_color (COLOR_ERROR); +#endif + + if (type == REPORT_ERROR) { + fprintf (stderr, "error:"); + } else { + fprintf (stderr, "fatal error:"); + } + + } else if (type == REPORT_INTERNAL_ERROR) { + +#ifndef __PDOS__ + set_console_color (COLOR_INTERNAL_ERROR); +#endif + + fprintf (stderr, "internal error:"); + + } else if (type == REPORT_WARNING) { + +#ifndef __PDOS__ + set_console_color (COLOR_WARNING); +#endif + + fprintf (stderr, "warning:"); + + } + +#ifndef __PDOS__ + reset_console_color (); +#endif + + fprintf (stderr, " "); + vfprintf (stderr, fmt, ap); + fprintf (stderr, "\n"); + + if (type != REPORT_WARNING) { + ++errors; + } + +} + +unsigned long get_error_count (void) { + return errors; +} + +void report_at (const char *filename, unsigned long lineno, enum report_type type, const char *fmt, ...) { + + va_list ap; + + va_start (ap, fmt); + output_message (filename, lineno, 0, type, fmt, ap); + va_end (ap); + +} diff --git a/report.h b/report.h new file mode 100644 index 0000000..8fc8758 --- /dev/null +++ b/report.h @@ -0,0 +1,29 @@ +/****************************************************************************** + * @file report.h + *****************************************************************************/ +#ifndef _REPORT_H +#define _REPORT_H + +enum report_type { + + REPORT_ERROR = 0, + REPORT_FATAL_ERROR, + REPORT_INTERNAL_ERROR, + REPORT_WARNING + +}; + +#if defined (_WIN32) +# define COLOR_ERROR 12 +# define COLOR_WARNING 13 +# define COLOR_INTERNAL_ERROR 19 +#else +# define COLOR_ERROR 91 +# define COLOR_INTERNAL_ERROR 94 +# define COLOR_WARNING 95 +#endif + +unsigned long get_error_count (void); +void report_at (const char *filename, unsigned long line_number, enum report_type type, const char *fmt, ...); + +#endif /* _REPORT_H */ diff --git a/write7x.c b/write7x.c new file mode 100644 index 0000000..41bd6b9 --- /dev/null +++ b/write7x.c @@ -0,0 +1,20 @@ +/****************************************************************************** + * @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; + +} diff --git a/write7x.h b/write7x.h new file mode 100644 index 0000000..deb84a0 --- /dev/null +++ b/write7x.h @@ -0,0 +1,10 @@ +/****************************************************************************** + * @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 */