diff options
| -rw-r--r-- | firmware/drivers/fat.c | 216 |
1 files changed, 95 insertions, 121 deletions
diff --git a/firmware/drivers/fat.c b/firmware/drivers/fat.c index 79b9dc0..88467f2 100644 --- a/firmware/drivers/fat.c +++ b/firmware/drivers/fat.c @@ -85,6 +85,10 @@ #define FAT_ATTR_LONG_NAME_MASK (FAT_ATTR_READ_ONLY | FAT_ATTR_HIDDEN | \ FAT_ATTR_SYSTEM | FAT_ATTR_VOLUME_ID | \ FAT_ATTR_DIRECTORY | FAT_ATTR_ARCHIVE ) + +/* NTRES flags */ +#define FAT_NTRES_LC_NAME 0x08 +#define FAT_NTRES_LC_EXT 0x10 #define FATDIR_NAME 0 #define FATDIR_ATTR 11 @@ -1235,14 +1239,11 @@ static int add_dir_entry(struct fat_dir* dir, struct bpb* fat_bpb = &fat_bpbs[0]; #endif unsigned char buf[SECTOR_SIZE]; - unsigned char shortname[16]; + unsigned char shortname[12]; int rc; unsigned int sector; bool done = false; - bool eof = false; - bool last = false; int entries_needed, entries_found = 0; - int namelen = strlen(name); int firstentry; LDEBUGF( "add_dir_entry(%s,%lx)\n", @@ -1255,87 +1256,71 @@ static int add_dir_entry(struct fat_dir* dir, /* The "." and ".." directory entries must not be long names */ if(dotdir) { int i; - strncpy(shortname, name, 16); - for(i = strlen(shortname);i < 12;i++) + strncpy(shortname, name, 12); + for(i = strlen(shortname); i < 12; i++) shortname[i] = ' '; entries_needed = 1; } else { - /* create dos name */ rc = create_dos_name(name, shortname); if (rc < 0) return rc * 10 - 0; /* one dir entry needed for every 13 bytes of filename, plus one entry for the short name */ - entries_needed = namelen / NAME_BYTES_PER_ENTRY + 1; - if (namelen % NAME_BYTES_PER_ENTRY) - entries_needed++; + entries_needed = (strlen(name) + (NAME_BYTES_PER_ENTRY-1)) + / NAME_BYTES_PER_ENTRY + 1; } - restart: - firstentry = 0; - + restart: + firstentry = -1; + rc = fat_seek(&dir->file, 0); if (rc < 0) return rc * 10 - 1; - for (sector=0; !done; sector++) + /* step 1: search for free entries and check for duplicate shortname */ + for (sector = 0; !done; sector++) { unsigned int i; - rc = 0; - if (!eof) { - rc = fat_readwrite(&dir->file, 1, buf, false); - } - if (rc == 0) { - /* eof: add new sector */ - eof = true; - - memset(buf, 0, sizeof buf); - LDEBUGF("Adding new sector to dir\n"); - rc = fat_seek(&dir->file, sector); - if (rc < 0) - return rc * 10 - 2; - - /* add sectors (we must clear the whole cluster) */ - do { - rc = fat_readwrite(&dir->file, 1, buf, true); - if (rc < 1) - return rc * 10 - 3; - } while (dir->file.sectornum < (int)fat_bpb->bpb_secperclus); - } + rc = fat_readwrite(&dir->file, 1, buf, false); if (rc < 0) { DEBUGF( "add_dir_entry() - Couldn't read dir" " (error code %d)\n", rc); - return rc * 10 - 4; + return rc * 10 - 2; + } + + if (rc == 0) { /* current end of dir reached */ + LDEBUGF("End of dir on cluster boundary\n"); + break; } /* look for free slots */ - for (i=0; i < DIR_ENTRIES_PER_SECTOR && !done; i++) + for (i = 0; i < DIR_ENTRIES_PER_SECTOR; i++) { - unsigned char firstbyte = buf[i * DIR_ENTRY_SIZE]; - switch (firstbyte) { - case 0: /* end of dir */ - entries_found = entries_needed; - LDEBUGF("Found last entry %d\n", + switch (buf[i * DIR_ENTRY_SIZE]) { + case 0: + entries_found += DIR_ENTRIES_PER_SECTOR - i; + LDEBUGF("Found end of dir %d\n", sector * DIR_ENTRIES_PER_SECTOR + i); + i = DIR_ENTRIES_PER_SECTOR - 1; done = true; break; - case 0xe5: /* free entry */ + case 0xe5: entries_found++; LDEBUGF("Found free entry %d (%d/%d)\n", sector * DIR_ENTRIES_PER_SECTOR + i, entries_found, entries_needed); break; - default: + default: entries_found = 0; /* check that our intended shortname doesn't already exist */ if (!strncmp(shortname, buf + i * DIR_ENTRY_SIZE, 12)) { - /* filename exists already. make a new one */ + /* shortname exists already, make a new one */ randomize_dos_name(shortname); LDEBUGF("Duplicate shortname, changing to %s\n", shortname); @@ -1345,28 +1330,53 @@ static int add_dir_entry(struct fat_dir* dir, } break; } + if (firstentry < 0 && (entries_found >= entries_needed)) + firstentry = sector * DIR_ENTRIES_PER_SECTOR + i + 1 + - entries_found; + } + } - if (!firstentry && (entries_found == entries_needed)) { - firstentry = sector * DIR_ENTRIES_PER_SECTOR + i; + /* step 2: extend the dir if necessary */ + if (firstentry < 0) + { +#ifdef HAVE_FAT16SUPPORT + if (dir->file.firstcluster < 0) { + LDEBUGF("FAT16 root dir isn't extendable\n"); + return -3; + } +#endif + LDEBUGF("Adding new sector(s) to dir\n"); + rc = fat_seek(&dir->file, sector); + if (rc < 0) + return rc * 10 - 4; + memset(buf, 0, sizeof buf); - /* if we're not extending the dir, - we must go back to first free entry */ - if (done) - last = true; - else - firstentry -= (entries_needed - 1); - } + /* we must clear whole clusters */ + for (; (entries_found < entries_needed) || + (dir->file.sectornum < (int)fat_bpb->bpb_secperclus); sector++) + { + if (sector >= (65536/DIR_ENTRIES_PER_SECTOR)) + return -5; /* dir too large -- FAT specification */ + + rc = fat_readwrite(&dir->file, 1, buf, true); + if (rc < 1) /* No more room or something went wrong */ + return rc * 10 - 6; + + entries_found += DIR_ENTRIES_PER_SECTOR; } + + firstentry = sector * DIR_ENTRIES_PER_SECTOR - entries_found; } + /* step 3: add entry */ sector = firstentry / DIR_ENTRIES_PER_SECTOR; LDEBUGF("Adding longname to entry %d in sector %d\n", firstentry, sector); - - rc = write_long_name(&dir->file, firstentry, + + rc = write_long_name(&dir->file, firstentry, entries_needed, name, shortname, is_directory); if (rc < 0) - return rc * 10 - 5; + return rc * 10 - 7; /* remember where the shortname dir entry is located */ file->direntry = firstentry + entries_needed - 1; @@ -1375,53 +1385,6 @@ static int add_dir_entry(struct fat_dir* dir, LDEBUGF("Added new dir entry %d, using %d slots.\n", file->direntry, file->direntries); - /* advance the end-of-dir marker? */ - if (last) - { - unsigned int lastentry = firstentry + entries_needed; - - LDEBUGF("Updating end-of-dir entry %d\n",lastentry); - - if (lastentry % DIR_ENTRIES_PER_SECTOR) - { - int idx = (lastentry % DIR_ENTRIES_PER_SECTOR) * DIR_ENTRY_SIZE; - - rc = fat_seek(&dir->file, lastentry / DIR_ENTRIES_PER_SECTOR); - if (rc < 0) - return rc * 10 - 6; - - rc = fat_readwrite(&dir->file, 1, buf, false); - if (rc < 1) - return rc * 10 - 7; - - /* clear last entry */ - buf[idx] = 0; - - rc = fat_seek(&dir->file, lastentry / DIR_ENTRIES_PER_SECTOR); - if (rc < 0) - return rc * 10 - 8; - - /* we must clear entire last cluster */ - do { - rc = fat_readwrite(&dir->file, 1, buf, true); - if (rc < 1) - return rc * 10 - 9; - memset(buf, 0, sizeof buf); - } while (dir->file.sectornum < (int)fat_bpb->bpb_secperclus); - } - else - { - /* add a new sector/cluster for last entry */ - memset(buf, 0, sizeof buf); - do { - rc = fat_readwrite(&dir->file, 1, buf, true); - if (rc < 1) - return rc * 10 - 9; /* Same error code as above, can't be - more than -9 */ - } while (dir->file.sectornum < (int)fat_bpb->bpb_secperclus); - } - } - return 0; } @@ -1429,10 +1392,6 @@ unsigned char char2dos(unsigned char c) { switch(c) { - case 0xe5: /* Special kanji character */ - c = 0x05; - break; - case 0x22: case 0x2a: case 0x2b: @@ -1448,16 +1407,15 @@ unsigned char char2dos(unsigned char c) case 0x5c: case 0x5d: case 0x7c: - /* Illegal name, replace */ + /* Illegal char, replace */ c = '_'; break; default: if(c <= 0x20) - { - /* Illegal name, remove */ - c = 0; - } + c = 0; /* Illegal char, remove */ + else + c = toupper(c); break; } return c; @@ -1478,13 +1436,16 @@ static int create_dos_name(const unsigned char *name, unsigned char *newname) { unsigned char c = char2dos(*name); if (c) - newname[i++] = toupper(c); + newname[i++] = c; } /* Pad both name and extension */ while (i < 11) newname[i++] = ' '; + if (newname[0] == 0xe5) /* Special kanji character */ + newname[0] = 0x05; + if (ext) { /* Extension part */ ext++; @@ -1492,7 +1453,7 @@ static int create_dos_name(const unsigned char *name, unsigned char *newname) { unsigned char c = char2dos(*ext); if (c) - newname[i++] = toupper(c); + newname[i++] = c; } } return 0; @@ -1580,6 +1541,9 @@ static int update_short_entry( struct fat_file* file, long size, int attr ) static int parse_direntry(struct fat_direntry *de, const unsigned char *buf) { int i=0,j=0; + unsigned char c; + bool lowercase; + memset(de, 0, sizeof(struct fat_direntry)); de->attr = buf[FATDIR_ATTR]; de->crttimetenth = buf[FATDIR_CRTTIMETENTH]; @@ -1592,14 +1556,24 @@ static int parse_direntry(struct fat_direntry *de, const unsigned char *buf) ((long)(unsigned)BYTES2INT16(buf,FATDIR_FSTCLUSHI) << 16); /* The double cast is to prevent a sign-extension to be done on CalmRISC16. (the result of the shift is always considered signed) */ - + /* fix the name */ - for (i=0; (i<8) && (buf[FATDIR_NAME+i] != ' '); i++) - de->name[j++] = buf[FATDIR_NAME+i]; - if ( buf[FATDIR_NAME+8] != ' ' ) { + lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_NAME); + c = buf[FATDIR_NAME]; + if (c == 0x05) /* special kanji char */ + c = 0xe5; + i = 0; + while (c != ' ') { + de->name[j++] = lowercase ? tolower(c) : c; + if (++i >= 8) + break; + c = buf[FATDIR_NAME+i]; + } + if (buf[FATDIR_NAME+8] != ' ') { + lowercase = (buf[FATDIR_NTRES] & FAT_NTRES_LC_EXT); de->name[j++] = '.'; - for (i=8; (i<11) && (buf[FATDIR_NAME+i] != ' '); i++) - de->name[j++] = buf[FATDIR_NAME+i]; + for (i = 8; (i < 11) && ((c = buf[FATDIR_NAME+i]) != ' '); i++) + de->name[j++] = lowercase ? tolower(c) : c; } return 1; } |