1
0
Fork 0
forked from len0rd/rockbox

FS#11118: reduce the number of cached sector in FAT code because some are useless after a rewrite of LFN entries handling. Also makes LFN handling more robust.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25290 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Amaury Pouly 2010-03-22 10:35:25 +00:00
parent 7a3822c8b0
commit a8137252a5
2 changed files with 98 additions and 118 deletions

View file

@ -31,6 +31,7 @@
#include "timefuncs.h" #include "timefuncs.h"
#include "kernel.h" #include "kernel.h"
#include "rbunicode.h" #include "rbunicode.h"
/*#define LOGF_ENABLE*/
#include "logf.h" #include "logf.h"
#define BYTES2INT16(array,pos) \ #define BYTES2INT16(array,pos) \
@ -110,6 +111,14 @@
#define FATLONG_ORDER 0 #define FATLONG_ORDER 0
#define FATLONG_TYPE 12 #define FATLONG_TYPE 12
#define FATLONG_CHKSUM 13 #define FATLONG_CHKSUM 13
#define FATLONG_LAST_LONG_ENTRY 0x40
#define FATLONG_NAME_BYTES_PER_ENTRY 26
/* at most 20 LFN entries, keep coherent with fat_dir->longname size ! */
#define FATLONG_MAX_ORDER 20
#define FATLONG_NAME_CHUNKS 3
static unsigned char FATLONG_NAME_POS[FATLONG_NAME_CHUNKS] = {1, 14, 28};
static unsigned char FATLONG_NAME_SIZE[FATLONG_NAME_CHUNKS] = {10, 12, 4};
#define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4) #define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4)
#define CLUSTERS_PER_FAT16_SECTOR (SECTOR_SIZE / 2) #define CLUSTERS_PER_FAT16_SECTOR (SECTOR_SIZE / 2)
@ -1173,7 +1182,7 @@ static int write_long_name(struct fat_file* file,
entry[FATLONG_ORDER] = numentries-i-1; entry[FATLONG_ORDER] = numentries-i-1;
if (i==0) { if (i==0) {
/* mark this as last long entry */ /* mark this as last long entry */
entry[FATLONG_ORDER] |= 0x40; entry[FATLONG_ORDER] |= FATLONG_LAST_LONG_ENTRY;
/* pad name with 0xffff */ /* pad name with 0xffff */
for (k=1; k<11; k++) entry[k] = FAT_LONGNAME_PAD_BYTE; for (k=1; k<11; k++) entry[k] = FAT_LONGNAME_PAD_BYTE;
@ -2323,47 +2332,20 @@ int fat_opendir(IF_MV2(int volume,)
return 0; return 0;
} }
/* Copies a segment of long file name (UTF-16 LE encoded) to the
* destination buffer (UTF-8 encoded). Copying is stopped when
* either 0x0000 or 0xffff (FAT pad char) is encountered.
* Trailing \0 is also appended at the end of the UTF8-encoded
* string.
*
* utf16src utf16 (little endian) segment to copy
* utf16count max number of the utf16-characters to copy
* utf8dst where to write UTF8-encoded string to
*
* returns the number of UTF-16 characters actually copied
*/
static int fat_copy_long_name_segment(unsigned char *utf16src,
int utf16count, unsigned char *utf8dst) {
int cnt = 0;
while ((utf16count--) > 0) {
unsigned short ucs = utf16src[0] | (utf16src[1] << 8);
if ((ucs == 0) || (ucs == FAT_LONGNAME_PAD_UCS)) {
break;
}
utf8dst = utf8encode(ucs, utf8dst);
utf16src += 2;
cnt++;
}
*utf8dst = 0;
return cnt;
}
int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry) int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
{ {
bool done = false; bool done = false;
int i; int i, j;
int rc; int rc;
int order;
unsigned char firstbyte; unsigned char firstbyte;
/* Long file names are stored in special entries. Each entry holds /* Long file names are stored in special entries. Each entry holds
up to 13 characters. Names can be max 255 chars (not bytes!) long up to 13 characters. Names can be max 255 chars (not bytes!) long */
hence max 20 entries are required. */ /* The number of long entries in the long name can be retrieve from the first
int longarray[20]; * long entry because there are stored in reverse order and have an ordinal */
int longs=0; int nb_longs = 0;
int sectoridx=0; /* The long entries are expected to be in order, so remember the last ordinal */
unsigned char* cached_buf = dir->sectorcache[0]; int last_long_ord = 0;
dir->entrycount = 0; dir->entrycount = 0;
@ -2371,7 +2353,7 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
{ {
if ( !(dir->entry % DIR_ENTRIES_PER_SECTOR) || !dir->sector ) if ( !(dir->entry % DIR_ENTRIES_PER_SECTOR) || !dir->sector )
{ {
rc = fat_readwrite(&dir->file, 1, cached_buf, false); rc = fat_readwrite(&dir->file, 1, dir->sectorcache, false);
if (rc == 0) { if (rc == 0) {
/* eof */ /* eof */
entry->name[0] = 0; entry->name[0] = 0;
@ -2386,16 +2368,14 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
} }
for (i = dir->entry % DIR_ENTRIES_PER_SECTOR; for (i = dir->entry % DIR_ENTRIES_PER_SECTOR;
i < DIR_ENTRIES_PER_SECTOR; i++) i < DIR_ENTRIES_PER_SECTOR; i++) {
{
unsigned int entrypos = i * DIR_ENTRY_SIZE; unsigned int entrypos = i * DIR_ENTRY_SIZE;
firstbyte = cached_buf[entrypos]; firstbyte = dir->sectorcache[entrypos];
dir->entry++; dir->entry++;
if (firstbyte == 0xe5) { if (firstbyte == 0xe5) {
/* free entry */ /* free entry */
sectoridx = 0;
dir->entrycount = 0; dir->entrycount = 0;
continue; continue;
} }
@ -2409,14 +2389,48 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
dir->entrycount++; dir->entrycount++;
/* longname entry? */ /* LFN entry? */
if ( ( cached_buf[entrypos + FATDIR_ATTR] & if ( ( dir->sectorcache[entrypos + FATDIR_ATTR] &
FAT_ATTR_LONG_NAME_MASK ) == FAT_ATTR_LONG_NAME ) { FAT_ATTR_LONG_NAME_MASK ) == FAT_ATTR_LONG_NAME ) {
longarray[longs++] = entrypos + sectoridx; /* extract ordinal */
order = dir->sectorcache[entrypos + FATLONG_ORDER] & ~FATLONG_LAST_LONG_ENTRY;
/* is this entry the first long entry ? (first in order but containing last part) */
if (dir->sectorcache[entrypos + FATLONG_ORDER] & FATLONG_LAST_LONG_ENTRY) {
/* check that order is not too big ! (and non-zero) */
if(order <= 0 || order > FATLONG_MAX_ORDER)
continue; /* ignore the whole LFN, will trigger lots of warnings */
nb_longs = order;
last_long_ord = order;
}
else {
/* check orphan entry */
if (nb_longs == 0) {
logf("fat warning: orphan LFN entry");
/* ignore */
continue;
}
/* check order */
if (order != (last_long_ord - 1)) {
logf("fat warning: wrong LFN ordinal");
/* ignore the whole LFN, will trigger lots of warnings */
nb_longs = 0;
}
last_long_ord = order;
}
/* copy part, reuse [order] for another purpose :) */
order = (order - 1) * FATLONG_NAME_BYTES_PER_ENTRY;
for(j = 0; j < FATLONG_NAME_CHUNKS; j++) {
memcpy(dir->longname + order,
dir->sectorcache + entrypos + FATLONG_NAME_POS[j],
FATLONG_NAME_SIZE[j]);
order += FATLONG_NAME_SIZE[j];
}
} }
else { else {
if ( parse_direntry(entry, if ( parse_direntry(entry, dir->sectorcache + entrypos) ) {
&cached_buf[entrypos]) ) {
/* don't return volume id entry */ /* don't return volume id entry */
if ( (entry->attr & if ( (entry->attr &
@ -2425,74 +2439,45 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
continue; continue;
/* replace shortname with longname? */ /* replace shortname with longname? */
if ( longs ) { /* check that the long name is complete */
int j; if (nb_longs != 0 && last_long_ord == 1) {
/* This should be enough to hold any name segment /* hold a copy of the shortname in case the long one is too long */
utf8-encoded */
unsigned char shortname[13]; /* 8+3+dot+\0 */ unsigned char shortname[13]; /* 8+3+dot+\0 */
/* Add 1 for trailing \0 */
unsigned char longname_utf8segm[6*4 + 1];
int longname_utf8len = 0; int longname_utf8len = 0;
/* Temporarily store it */ /* One character at a time, add 1 for trailing \0, 4 is the maximum size
* of a UTF8 encoded character in rockbox */
unsigned char longname_utf8segm[4 + 1];
unsigned short ucs;
int segm_utf8len;
/* Temporarily store short name */
strcpy(shortname, entry->name); strcpy(shortname, entry->name);
entry->name[0] = 0; entry->name[0] = 0;
/* iterate backwards through the dir entries */ /* Convert the FAT name to a utf8-encoded one.
for (j=longs-1; j>=0; j--) { * The name is not necessary NUL-terminated ! */
unsigned char* ptr = cached_buf; for (j = 0; j < nb_longs * FATLONG_NAME_BYTES_PER_ENTRY; j += 2) {
int index = longarray[j]; ucs = dir->longname[j] | (dir->longname[j + 1] << 8);
/* current or cached sector? */ if(ucs == 0 || ucs == FAT_LONGNAME_PAD_UCS)
if ( sectoridx >= SECTOR_SIZE ) { break;
if ( sectoridx >= SECTOR_SIZE*2 ) { /* utf8encode will return a pointer after the converted
if ( ( index >= SECTOR_SIZE ) && * string, subtract the pointer to the start to get the length of it */
( index < SECTOR_SIZE*2 )) segm_utf8len = utf8encode(ucs, longname_utf8segm) - longname_utf8segm;
ptr = dir->sectorcache[1];
else
ptr = dir->sectorcache[2];
}
else {
if ( index < SECTOR_SIZE )
ptr = dir->sectorcache[1];
}
index &= SECTOR_SIZE-1; /* warn the trailing zero ! (FAT_FILENAME_BYTES includes it) */
if (longname_utf8len + segm_utf8len >= FAT_FILENAME_BYTES) {
/* force use of short name */
longname_utf8len = FAT_FILENAME_BYTES + 1;
break; /* fallback later */
}
else {
longname_utf8segm[segm_utf8len] = 0;
strcat(entry->name + longname_utf8len, longname_utf8segm);
longname_utf8len += segm_utf8len;
} }
/* Try to append each segment of the long name.
Check if we'd exceed the buffer.
Also check for FAT padding characters 0xFFFF. */
if (fat_copy_long_name_segment(ptr + index + 1, 5,
longname_utf8segm) == 0) break;
/* logf("SG: %s, EN: %s", longname_utf8segm,
entry->name); */
longname_utf8len += strlen(longname_utf8segm);
if (longname_utf8len < FAT_FILENAME_BYTES)
strcat(entry->name, longname_utf8segm);
else
break;
if (fat_copy_long_name_segment(ptr + index + 14, 6,
longname_utf8segm) == 0) break;
/* logf("SG: %s, EN: %s", longname_utf8segm,
entry->name); */
longname_utf8len += strlen(longname_utf8segm);
if (longname_utf8len < FAT_FILENAME_BYTES)
strcat(entry->name, longname_utf8segm);
else
break;
if (fat_copy_long_name_segment(ptr + index + 28, 2,
longname_utf8segm) == 0) break;
/* logf("SG: %s, EN: %s", longname_utf8segm,
entry->name); */
longname_utf8len += strlen(longname_utf8segm);
if (longname_utf8len < FAT_FILENAME_BYTES)
strcat(entry->name, longname_utf8segm);
else
break;
} }
/* Does the utf8-encoded name fit into the entry? */ /* Does the utf8-encoded name fit into the entry? */
/* warn the trailing zero ! (FAT_FILENAME_BYTES includes it) */
if (longname_utf8len >= FAT_FILENAME_BYTES) { if (longname_utf8len >= FAT_FILENAME_BYTES) {
/* Take the short DOS name. Need to utf8-encode it /* Take the short DOS name. Need to utf8-encode it
since it may contain chars from the upper half of since it may contain chars from the upper half of
@ -2504,29 +2489,19 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
unsigned char *utf8; unsigned char *utf8;
utf8 = iso_decode(shortname, entry->name, -1, utf8 = iso_decode(shortname, entry->name, -1,
strlen(shortname)); strlen(shortname));
*utf8 = 0; *utf8 = 0;
logf("SN: %s", entry->name); logf("SN: %s", entry->name);
} else { } else {
/* logf("LN: %s", entry->name); logf("LN: %s", entry->name);
logf("LNLen: %d (%c)", longname_utf8len, logf("LNLen: %d", longname_utf8len);
entry->name[0]); */
} }
} }
done = true; done = true;
sectoridx = 0;
i++; i++;
break; break;
} }
} }
} }
/* save this sector, for longname use */
if ( sectoridx )
memcpy( dir->sectorcache[2], dir->sectorcache[0], SECTOR_SIZE );
else
memcpy( dir->sectorcache[1], dir->sectorcache[0], SECTOR_SIZE );
sectoridx += SECTOR_SIZE;
} }
return 0; return 0;
} }

View file

@ -85,7 +85,12 @@ struct fat_dir
unsigned int entrycount; unsigned int entrycount;
long sector; long sector;
struct fat_file file; struct fat_file file;
unsigned char sectorcache[3][SECTOR_SIZE]; unsigned char sectorcache[SECTOR_SIZE];
/* There are 2-bytes per characters. We don't want to bother too much, as LFN entries are
* at much 255 characters longs, that's at most 20 LFN entries. Each entry hold at most
* 13 characters, that a total of 260 characters. So we keep a buffer of that size.
* Keep coherent with fat.c code. */
unsigned char longname[260 * 2];
}; };
#ifdef HAVE_HOTSWAP #ifdef HAVE_HOTSWAP