summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2003-12-09 00:56:21 +0000
committerH. Peter Anvin <hpa@zytor.com>2003-12-09 00:56:21 +0000
commitf0b6dde4a767127a27fad43ef98b17de651c67a0 (patch)
tree3a027b6bfb40d0f725271262ce4c32294aea34d5
parentd170a4fdba3fe7577db4097c5a87f06ddd4f2f53 (diff)
downloadmkfatfs-f0b6dde4a767127a27fad43ef98b17de651c67a0.tar.gz
mkfatfs-f0b6dde4a767127a27fad43ef98b17de651c67a0.tar.xz
mkfatfs-f0b6dde4a767127a27fad43ef98b17de651c67a0.zip
Lots of work on long filename supportHEADmaster
-rw-r--r--Makefile6
-rw-r--r--dirtree.c220
-rw-r--r--dirtree.h5
-rw-r--r--geo.h1
-rw-r--r--getgeo.c50
-rw-r--r--vfat.c32
6 files changed, 272 insertions, 42 deletions
diff --git a/Makefile b/Makefile
index 329066b..17097e3 100644
--- a/Makefile
+++ b/Makefile
@@ -19,10 +19,11 @@ CC = gcc
CFLAGS = -g -O -Wall -D_FILE_OFFSET_BITS=64 $(SYSLXFLAGS)
LDFLAGS = -g
LIBS =
+PERL = perl
OBJS = alloc.o dirtree.o main.o xmalloc.o xstdio.o genfat.o \
clustparm.o writedir.o writefile.o writechain.o writefs.o \
- gensuper.o date.o syslinux.o getgeo.o
+ gensuper.o date.o syslinux.o getgeo.o cp437.o
SRCS = alloc.c dirtree.c main.c xmalloc.c xstdio.c genfat.c \
clustparm.c writedir.c writefile.c writechain.c writefs.c \
gensuper.c date.c syslinux.c getgeo.c
@@ -45,6 +46,9 @@ genfatimage: $(OBJS) $(SYSLXLIB)
getgeo: getgeo.c
$(CC) $(CFLAGS) $(LDFLAGS) -DSTANDALONE -o $@ $< $(LIBS)
+cp437.c: CP437.TXT unitbl.pl
+ $(PERL) unitbl.pl < CP437.TXT > $@ || rm -f $@
+
clean:
rm -f *.o genfatimage getgeo
diff --git a/dirtree.c b/dirtree.c
index b8c28f0..5582b43 100644
--- a/dirtree.c
+++ b/dirtree.c
@@ -1,7 +1,7 @@
#ident "$Id$"
/* ----------------------------------------------------------------------- *
*
- * Copyright 2001 H. Peter Anvin - All Rights Reserved
+ * Copyright 2001-2003 H. Peter Anvin - All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,27 +24,134 @@
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
+#include <iconv.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#include "dirtree.h"
#include "xmalloc.h"
+#define VFAT_MAX_LEN 64
+#define VFAT_MAX_SLOTS ((VFAT_MAX_LEN+12)/13)
+
extern const char *program;
+struct uni2cp {
+ uint16_t uni;
+ uint8_t cp;
+ uint8_t upper;
+};
+extern struct uni2cp cp437;
-static int
-dos_mangle_name(const char *name, char *buf, uint32_t numtail)
+static iconv_t iconv_utf16;
+
+static void
+fat_mangle_setup(void)
+{
+ iconv_utf16 = iconv_open("UTF-16LE", "");
+ if ( iconv_utf16 == (iconv_t)-1 ) {
+ fprintf("%s: failed to iconv_open UTF-16LE: %s\n",
+ program, strerror(errno));
+ exit(1);
+ }
+}
+
+#define VFAT_LOSSY_CONVERSION 0x01 /* Unrepresentable character */
+#define VFAT_UPPER_CASE 0x02 /* A..Z */
+#define VFAT_BAD_CHAR 0x04 /* Forbidden character */
+#define VFAT_EXTENDED_CHAR 0x08 /* char >= 0x80 */
+
+static unsigned int
+fat_uni_to_cp(char *cp, uint16_t *uni, int uni_len)
{
- const char *dosforbidden =
+ /* NUL is implied; space is technically legal but rarely used in practice */
+ static const char dosforbidden[] =
"\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017"
"\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
- " \"\'\\/[]|<>,*$+";
+ " \"\'\\/[]|<>,*$+\177\377";
+ static const char cp437toupper[256] =
+ "\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017"
+ "\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+ " !\"#$%&\'()*+,-./0123456789:;<=>?"
+ "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
+ "`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~\177"
+ "\200\232\220A\216A\217\200EEEIII\216\217"
+ "\220\222\222O\231OUUY\231\232\233\234\235\236\237"
+ "\240\241\242\243\245\245\246\247\250\251\252\253\254\255\256\257"
+ "\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277"
+ "\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317"
+ "\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337"
+ "\340\341\342\343\344\344\346\347\350\351\352\353\354\355\356\357"
+ "\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377";
+ uint16_t uc, uref;
+ int ui, ul, uh;
+ struct uni2cp *ux;
+ uint8_t cpc;
+ int name_flags = 0;
+ int ext_flags = 0;
+ int found_dot = 0;
+
+ while ( uni_len-- ) {
+ uc = read16((le16_t *)(uni++));
+
+ ui = 128; ul = 0; uh = 255;
+ while ( uc != (uref = cp437[ui].uni) && ul < uh ) {
+ if ( uc < uref ) {
+ ul = ui+1;
+ } else if ( uc > uref ) {
+ uh = ui-1;
+ }
+ ui = (ul+uh)/2;
+ }
+ if ( uc != uref ) {
+ cpc = '_';
+ ext_flags |= VFAT_LOSSY_CONVERSION;
+ } else {
+ cpc = cp437[uni].cp;
+ if ( strchr(dosforbidden, cpc) ) {
+ cpc = '_';
+ ext_flags |= VFAT_BAD_CHAR;
+ } else if ( cpc >= 'A' && cpc <= 'Z' ) {
+ ext_flags |= VFAT_UPPER_CASE;
+ } else if ( cpc >= 0x80 ) {
+ ext_flags |= VFAT_EXTENDED_CHAR;
+ }
+ }
+ if ( cpc == '.' ) {
+ name_flags |= ext_flags;
+ ext_flags = 0;
+ found_dot = 1;
+ }
+
+ *cp++ = cp437toupper[cpc];
+ }
+ *cp = '\0';
+
+ return found_dot ? (name_flags | (ext_flags << 4)) : ext_flags;
+}
+
+static int
+fat_mangle_name(const char *name, char *buf)
+{
const char *dot;
char *cp, *endp;
char xc;
const char numtail_str[9];
+ char *cpname;
+ char *inb, *outb;
+ int inbl, outbl;
+
+ if ( !iconv_cp437 )
+ fat_mangle_setup();
- dot = strrchr(name, '.'); /* Find last dot */
+ cpname = xmalloc((inbl = outbl = strlen(name))+1);
+
+ inb = name;
+ outb = cpname;
+ iconv(iconv_cp437, NULL, NULL, NULL, NULL); /* Reset input state */
+ iconv(iconv_cp437, &inb, &inbl, &outb, &outbl);
+ *outb = '\0';
+
+ dot = strrchr(cpname, '.'); /* Find last dot */
if ( dot == name )
dot = NULL; /* Leading dot? */
@@ -52,10 +159,10 @@ dos_mangle_name(const char *name, char *buf, uint32_t numtail)
memset(buf, ' ', 11);
*endp = '\0';
- for ( ; *name ; name++ ) {
- xc = *name;
+ for ( ; *cpname ; cpname++ ) {
+ xc = *cpname;
if ( xc == '.' ) {
- if ( name == dot ) {
+ if ( cpname == dot ) {
/* Last dot */
cp = buf+8;
cp[0] = ' ';
@@ -82,15 +189,101 @@ dos_mangle_name(const char *name, char *buf, uint32_t numtail)
if ( cp < endp )
*cp++ = xc;
}
+ return 0;
+}
+
+/* Add a numeric tail to an already mangled iname */
+static void
+fat_set_numtail(char *oname, char *iname, int numtail)
+{
+ int nc, char *cp;
+ char numtail_str[9];
+
+ memcpy(oname, iname, 11);
if ( numtail ) {
- int nc = sprintf(numtail_str, "~%X", numtail);
- cp = buf+8-nc;
+ nc = snprintf(numtail_str, 9, "~%u", numtail);
+ cp = oname+8-nc;
while ( cp > buf && cp[-1] == ' ' ) cp--;
memcpy(cp, numtail_str, nc);
}
+}
- return 0;
+static void
+dos_mangle_name(struct direntry *dirent,
+ struct direntry *prev,
+ int prev_cnt)
+{
+ (void)prev; (void)prev_cnt;
+
+ dirent->nfatents = 1; /* Only 1 entry/file required */
+ dirent->fatents = xmalloc(sizeof(struct fat_dirent));
+ fat_mangle_name(dirent->name, dirent->fatents->name, 0);
+}
+
+static void
+int vfat_mangle_name(struct direntry *dirent,
+ struct direntry *prev,
+ int prev_cnt)
+{
+ uint16_t buffer[VFAT_MAX_LEN];
+ uint16_t *xb;
+ char *inb, *outb;
+ int inbl, outbl, xl, nx;
+ int seq = 1;
+ int i;
+ uint16_t cv;
+ static const char *vfat_bad_long =
+ "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17"
+ "\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37"
+ "\"*/:<>?\\|\177";
+
+ inb = str;
+ inbl = strlen(inb);
+ outb = (char *)buffer;
+ outbl = VFAT_MAX_LEN << 1;
+
+ iconv(iconv_utf16, NULL, NULL, NULL, NULL); /* Reset input state */
+ iconv(iconv_utf16, &inb, &inbl, &outb, &outbl);
+
+ xl = VFAT_MAX_LEN - (outbl >> 1);
+ xb = buffer;
+
+ /* The VFAT spec says trailing periods and leading and trailing spaces
+ are ignored. However, it doesn't specify if those should actually
+ be stripped. Ignore that for now and let the filesystem worry about it. */
+
+ for ( i = 0 ; i < xl ; i++ ) {
+ cv = read16((le16_t *)&buffer[i]);
+ if ( cv <= 127 && strchr(vfat_bad_long, cv) )
+ write16((le16_t *)&buffer[i], '_'); /* Bad character, replace with _ */
+ }
+
+ while ( xl ) {
+ memset(slots, 0, sizeof(*slots));
+ slots->id = seq++;
+ slots->attribute = 0x0f; /* Part of a long name */
+ slots->alias_csum = csum;
+
+ nx = min(xl,5);
+ memcpy(slots->name0, xb, nx<<1);
+ xl -= nx;
+
+ nx = min(xl,6);
+ memcpy(slots->name5, xb, nx<<1);
+ xl -= nx;
+
+ nx = min(xl,2);
+ memcpy(slots->name11, xb, nx<<1);
+ xl -= nx;
+
+ if ( !xl )
+ slots->id |= 0x40; /* Last slot */
+
+ slots++;
+ }
+
+ return seq-1;
}
@@ -163,7 +356,8 @@ make_dir_tree(const char *root, int *dirsizeptr, struct direntry *parent)
thisent->next = NULL;
/* Generate mangled DOS filename (eventually) */
- dos_mangle_name(thisent->name, thisent->mangled_name, 0);
+ /* FIX: Handle VFAT */
+ dos_mangle_name(thisent);
/* Add to linked list */
*link = thisent;
diff --git a/dirtree.h b/dirtree.h
index 23b0dc3..2ad8bf6 100644
--- a/dirtree.h
+++ b/dirtree.h
@@ -21,11 +21,11 @@
#define DIRTREE_H
#include <sys/stat.h>
+#include "fat.h"
struct direntry {
char *name; /* Filename */
char *path; /* Full path */
- char mangled_name[12]; /* Mangled */
struct stat st; /* stat info */
struct direntry *dir; /* Directory contents (directory only) */
unsigned int dirsize; /* Cardinality of directory */
@@ -35,7 +35,10 @@ struct direntry {
unsigned int attribute; /* DOS attribute byte */
unsigned int clusters; /* Number of clusters allocated */
unsigned int cluster; /* Assigned cluster (once we get there) */
+ int nfatents; /* Number of FAT entries required */
+ struct fat_dirent *fatents; /* Directory entries in FAT format */
struct direntry *nextbyclust; /* Next pointer in on-disk order */
+
};
struct direntry *
diff --git a/geo.h b/geo.h
index 070504e..81df986 100644
--- a/geo.h
+++ b/geo.h
@@ -24,6 +24,7 @@
struct image_geometry {
unsigned long c, h, s;
+ unsigned char idbyte;
uint64_t size, offset;
int secshift;
};
diff --git a/getgeo.c b/getgeo.c
index 1de9463..2a17a65 100644
--- a/getgeo.c
+++ b/getgeo.c
@@ -44,15 +44,17 @@ get_geometry(int fd, struct image_geometry *geo)
unsigned int secsize;
const struct image_geometry *sg;
const struct image_geometry standard_geometries[] = {
- { 40, 1, 8, 160<<10, 0, 9 },
- { 40, 1, 9, 180<<10, 0, 9 },
- { 40, 2, 8, 320<<10, 0, 9 },
- { 40, 2, 9, 360<<10, 0, 9 },
- { 80, 2, 9, 720<<10, 0, 9 },
- { 80, 2, 15, 1200<<10, 0, 9 },
- { 80, 2, 18, 1440<<10, 0, 9 },
- { 80, 2, 36, 2880<<10, 0, 9 },
- { 0, 0, 0, 0, 0, 0 }
+ { 40, 1, 8, 0xfe, 160<<10, 0, 9 }, /* 160K 5.25" SSDD standard */
+ { 40, 1, 9, 0xfc, 180<<10, 0, 9 }, /* 180K 5.25" SSDD standard */
+ { 40, 2, 8, 0xff, 320<<10, 0, 9 }, /* 320K 5.25" DSDD standard */
+ { 40, 2, 9, 0xfd, 360<<10, 0, 9 }, /* 360K 5.25" DSDD standard */
+ { 80, 2, 9, 0xf9, 720<<10, 0, 9 }, /* 720K 3.5" DSDD standard */
+ { 80, 2, 15, 0xf9, 1200<<10, 0, 9 }, /* 1200K 5.25" DSHD standard */
+ { 80, 2, 18, 0xf0, 1440<<10, 0, 9 }, /* 1440K 3.5" DSHD standard */
+ { 80, 2, 21, 0xf0, 1680<<10, 0, 9 }, /* 1680K 3.5" DSHD extended */
+ { 82, 2, 21, 0xf0, 1722<<10, 0, 9 }, /* 1722K 3.5" DSHD extended */
+ { 80, 2, 36, 0xf0, 2880<<10, 0, 9 }, /* 2880K 3.5" DSED standard */
+ { 0, 0, 0, 0, 0, 0, 0 }
};
if ( fstat(fd, &st) )
@@ -61,7 +63,7 @@ get_geometry(int fd, struct image_geometry *geo)
memset(geo, 0, sizeof(*geo));
if ( S_ISBLK(st.st_mode) ) {
-#if 1 /*def BLKGETSIZE64*/
+#ifdef BLKGETSIZE64
if ( !ioctl(fd, BLKGETSIZE64, &blksize64) ) {
geo->size = blksize64;
} else
@@ -76,16 +78,26 @@ get_geometry(int fd, struct image_geometry *geo)
}
}
- if ( !ioctl(fd, HDIO_GETGEO, &hdgeo) ) {
+ if ( !ioctl(fd, FDGETPRM, &fdgeo) ) {
+ geo->c = fdgeo.track; /* IS THIS CORRECT?!? */
+ geo->h = fdgeo.head;
+ geo->s = fdgeo.sect;
+ geo->idbyte = 0xf0; /* Floppy disk "other" */
+ } else if ( !ioctl(fd, HDIO_GETGEO, &hdgeo) ) {
/* Got the geometry */
geo->c = hdgeo.cylinders; /* Don't use... may be truncated */
geo->h = hdgeo.heads;
geo->s = hdgeo.sectors;
+ geo->idbyte = 0xf8; /* Hard disk */
geo->offset = (uint64_t)hdgeo.start << geo->secshift;
- } else if ( !ioctl(fd, FDGETPRM, &fdgeo) ) {
- geo->c = fdgeo.track; /* IS THIS CORRECT?!? */
- geo->h = fdgeo.head;
- geo->s = fdgeo.sect;
+ }
+
+ /* If it's one of the standard floppy geometries set the ID byte correctly */
+ for ( sg = standard_geometries ; sg->size ; sg++ ) {
+ if ( sg->c == geo->c && sg->h == geo->h && sg->s == geo->s ) {
+ geo->idbyte = sg->idbyte;
+ break;
+ }
}
} else if ( S_ISREG(st.st_mode) ) {
/* Plain file; set the size and hunt for a standard geometry, otherwise
@@ -98,8 +110,8 @@ get_geometry(int fd, struct image_geometry *geo)
return 0;
}
}
-
- /* DO SOMETHING! */
+ /* FIX: Assume it's a hard disk and make something up */
+ geo->idbyte = 0xf8;
} else if ( S_ISDIR(st.st_mode) ) {
errno = EISDIR;
return -1;
@@ -128,8 +140,8 @@ int main(int argc, char *argv[])
exit(1);
}
- printf("c = %lu, h = %lu, s = %lu\n",
- geo.c, geo.h, geo.s);
+ printf("c = %lu, h = %lu, s = %lu, id = 0x%02x\n",
+ geo.c, geo.h, geo.s, geo.idbyte);
printf("size = %llu\n", geo.size);
printf("secshift = %d\n", geo.secshift);
diff --git a/vfat.c b/vfat.c
index 801156b..45d468a 100644
--- a/vfat.c
+++ b/vfat.c
@@ -24,9 +24,6 @@
#include "fat.h"
#include "minmax.h"
-#define VFAT_MAX_LEN 64
-#define VFAT_MAX_SLOTS ((VFAT_MAX_LEN+12)/13)
-
extern const char *program;
static iconv_t iconv_utf16;
@@ -50,22 +47,38 @@ int vfat_make_long_name(struct fat_vfat_slot *slots,
char *inb, *outb;
int inbl, outbl, xl, nx;
int seq = 1;
-
+ int i;
+ uint16_t cv;
+ static const char *vfat_bad_long =
+ "\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17"
+ "\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37"
+ "\"*/:<>?\\|\177";
+
inb = str;
inbl = strlen(inb);
outb = (char *)buffer;
- outbl = VFAT_MAX_LEN << 1;
-
+ outbl = VFAT_MAX_LEN << 1;
+
iconv(iconv_utf16, NULL, NULL, NULL, NULL); /* Reset input state */
iconv(iconv_utf16, &inb, &inbl, &outb, &outbl);
-
+
xl = VFAT_MAX_LEN - (outbl >> 1);
xb = buffer;
+ /* The VFAT spec says trailing periods and leading and trailing spaces
+ are ignored. However, it doesn't specify if those should actually
+ be stripped. Ignore that for now and let the filesystem worry about it. */
+
+ for ( i = 0 ; i < xl ; i++ ) {
+ cv = read16((le16_t *)&buffer[i]);
+ if ( cv <= 127 && strchr(vfat_bad_long, cv) )
+ write16((le16_t *)&buffer[i], '_'); /* Bad character, replace with _ */
+ }
+
while ( xl ) {
memset(slots, 0, sizeof(*slots));
slots->id = seq++;
- slots->attribute = 0xef; /* FIX */
+ slots->attribute = 0x0f; /* Part of a long name */
slots->alias_csum = csum;
nx = min(xl,5);
@@ -80,6 +93,9 @@ int vfat_make_long_name(struct fat_vfat_slot *slots,
memcpy(slots->name11, xb, nx<<1);
xl -= nx;
+ if ( !xl )
+ slots->id |= 0x40; /* Last slot */
+
slots++;
}