summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjörn Stenberg <bjorn@haxx.se>2002-11-18 11:58:43 +0000
committerBjörn Stenberg <bjorn@haxx.se>2002-11-18 11:58:43 +0000
commiteee2c01697f90e3be4d7c1b0c57f6921c78e85b9 (patch)
tree071d638ff388222972a70f55997412cc23dcef29
parent7aabb1ab66b3126b987200528b62f4a3fb98d205 (diff)
downloadrockbox-eee2c01697f90e3be4d7c1b0c57f6921c78e85b9.zip
rockbox-eee2c01697f90e3be4d7c1b0c57f6921c78e85b9.tar.gz
rockbox-eee2c01697f90e3be4d7c1b0c57f6921c78e85b9.tar.bz2
rockbox-eee2c01697f90e3be4d7c1b0c57f6921c78e85b9.tar.xz
Added longname handling to fat_remove().
Added proper 0xffff padding of last longname entry. add_dir_entry() now makes sure shortname is unique. Changed update_file_size() to use dir-as-file handling. Simplified create_dos_name() since we never use shortnames. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@2853 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/drivers/fat.c373
-rw-r--r--firmware/drivers/fat.h6
2 files changed, 239 insertions, 140 deletions
diff --git a/firmware/drivers/fat.c b/firmware/drivers/fat.c
index 534d48b..a3ed62e 100644
--- a/firmware/drivers/fat.c
+++ b/firmware/drivers/fat.c
@@ -100,7 +100,6 @@
#define FATDIR_FILESIZE 28
#define FATLONG_ORDER 0
-#define FATLONG_ATTR 11
#define FATLONG_TYPE 12
#define FATLONG_CHKSUM 13
@@ -648,9 +647,9 @@ static int write_long_name(struct fat_file* file,
unsigned char* entry;
unsigned int idx = firstentry % DIR_ENTRIES_PER_SECTOR;
unsigned int sector = firstentry / DIR_ENTRIES_PER_SECTOR;
- unsigned int i, j=0, nameidx;
+ unsigned int i, j=0;
unsigned char chksum = 0;
- unsigned int namelen = strlen(name);
+ int nameidx=0, namelen = strlen(name);
int rc;
LDEBUGF("write_long_name(file:%x, first:%d, num:%d, name:%s)\n",
@@ -669,9 +668,10 @@ static int write_long_name(struct fat_file* file,
chksum = ((chksum & 1) ? 0x80 : 0) + (chksum >> 1) + shortname[j++];
/* calc position of last name segment */
- for (nameidx=0;
- nameidx < (namelen - NAME_BYTES_PER_ENTRY);
- nameidx += NAME_BYTES_PER_ENTRY);
+ if ( namelen > NAME_BYTES_PER_ENTRY )
+ for (nameidx=0;
+ nameidx < (namelen - NAME_BYTES_PER_ENTRY);
+ nameidx += NAME_BYTES_PER_ENTRY);
for (i=0; i < numentries; i++) {
/* new sector? */
@@ -685,10 +685,15 @@ static int write_long_name(struct fat_file* file,
if (rc<1)
return -4;
- /* grab next sector */
+ /* read next sector */
rc = fat_readwrite(file, 1, buf, false);
- if (rc<1)
+ if (rc<0) {
+ LDEBUGF("Failed writing new sector: %d\n",rc);
return -5;
+ }
+ if (rc==0)
+ /* end of dir */
+ memset(buf, 0, sizeof buf);
sector++;
idx = 0;
@@ -707,13 +712,34 @@ static int write_long_name(struct fat_file* file,
if ( i+1 < numentries ) {
/* longname entry */
int k, l = nameidx;
+
entry[FATLONG_ORDER] = numentries-i-1;
- if (i==0)
+ if (i==0) {
+ /* mark this as last long entry */
entry[FATLONG_ORDER] |= 0x40;
- for (k=0; k<5 && name[l]; k++) entry[k*2 + 1] = name[l++];
- for (k=0; k<6 && name[l]; k++) entry[k*2 + 14] = name[l++];
- for (k=0; k<2 && name[l]; k++) entry[k*2 + 28] = name[l++];
- entry[FATLONG_ATTR] = FAT_ATTR_LONG_NAME;
+
+ /* pad name with 0xffff */
+ for (k=1; k<12; k++) entry[k] = 0xff;
+ for (k=14; k<26; k++) entry[k] = 0xff;
+ for (k=28; k<32; k++) entry[k] = 0xff;
+ };
+ /* set name */
+ for (k=0; k<5 && l <= namelen; k++) {
+ entry[k*2 + 1] = name[l++];
+ entry[k*2 + 2] = 0;
+ }
+ for (k=0; k<6 && l <= namelen; k++) {
+ entry[k*2 + 14] = name[l++];
+ entry[k*2 + 15] = 0;
+ }
+ for (k=0; k<2 && l <= namelen; k++) {
+ entry[k*2 + 28] = name[l++];
+ entry[k*2 + 29] = 0;
+ }
+
+ entry[FATDIR_ATTR] = FAT_ATTR_LONG_NAME;
+ entry[FATDIR_FSTCLUSLO] = 0;
+ entry[FATLONG_TYPE] = 0;
entry[FATLONG_CHKSUM] = chksum;
LDEBUGF("Longname entry %d: %.13s\n", idx, name+nameidx);
}
@@ -726,14 +752,13 @@ static int write_long_name(struct fat_file* file,
}
idx++;
nameidx -= NAME_BYTES_PER_ENTRY;
- //dbg_dump_buffer(buf, SECTOR_SIZE, 0);
}
/* update last sector */
rc = fat_seek(file, sector);
if (rc<0)
return -5;
-
+
rc = fat_readwrite(file, 1, buf, true);
if (rc<1)
return -6;
@@ -751,8 +776,10 @@ static int add_dir_entry(struct fat_dir* dir,
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,%x)\n",
name, file->firstcluster);
@@ -767,6 +794,9 @@ static int add_dir_entry(struct fat_dir* dir,
if (namelen % NAME_BYTES_PER_ENTRY)
entries_needed++;
+ restart:
+ firstentry = 0;
+
err=fat_seek(&dir->file, 0);
if (err<0)
return -1;
@@ -803,7 +833,7 @@ static int add_dir_entry(struct fat_dir* dir,
}
/* look for free slots */
- for (i=0; i < DIR_ENTRIES_PER_SECTOR; i++)
+ for (i=0; i < DIR_ENTRIES_PER_SECTOR && !done; i++)
{
unsigned char firstbyte = buf[i * DIR_ENTRY_SIZE];
switch (firstbyte) {
@@ -811,6 +841,8 @@ static int add_dir_entry(struct fat_dir* dir,
entries_found = entries_needed;
LDEBUGF("Found last entry %d\n",
sector * DIR_ENTRIES_PER_SECTOR + i);
+ last = true;
+ done = true;
break;
case 0xe5: /* free entry */
@@ -822,55 +854,90 @@ static int add_dir_entry(struct fat_dir* dir,
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 */
+ snprintf(shortname+8, 4, "%03X", (unsigned)rand() & 0xfff);
+ LDEBUGF("Duplicate shortname, changing to %s\n",
+ shortname);
+
+ /* name has changed, we need to restart search */
+ goto restart;
+ }
break;
}
- if (entries_found == entries_needed)
- {
- int firstentry = sector * DIR_ENTRIES_PER_SECTOR + i;
+ if (!firstentry && (entries_found == entries_needed)) {
+ firstentry = sector * DIR_ENTRIES_PER_SECTOR + i;
- /* if we're not extending the dir, we must go back to first
- free entry */
- if (firstbyte)
+ /* if we're not extending the dir,
+ we must go back to first free entry */
+ if (!last)
firstentry -= (entries_needed - 1);
+ }
+ }
+ }
- LDEBUGF("Adding longname to entry %d in sector %d\n",
- i, sector);
+ sector = firstentry / DIR_ENTRIES_PER_SECTOR;
+ LDEBUGF("Adding longname to entry %d in sector %d\n",
+ firstentry, sector);
- err = write_long_name(&dir->file, firstentry,
- entries_needed, name, shortname);
- if (err < 0)
- return -5;
+ err = write_long_name(&dir->file, firstentry,
+ entries_needed, name, shortname);
+ if (err < 0)
+ return -5;
- /* remember where the shortname dir entry is located */
- file->dirsector = dir->file.lastsector;
- file->direntry =
- (i + entries_needed - 1) % DIR_ENTRIES_PER_SECTOR;
+ /* remember where the shortname dir entry is located */
+ file->direntry = firstentry + entries_needed - 1;
+ file->direntries = entries_needed;
+ file->dircluster = dir->file.firstcluster;
+ LDEBUGF("Added new dir entry %d, using %d slots.\n",
+ file->direntry, file->direntries);
- /* advance the last_empty_entry marker? */
- if (firstbyte == 0)
- {
- i++;
- if (i < DIR_ENTRIES_PER_SECTOR)
- {
- /* next entry is now last */
- buf[i*DIR_ENTRY_SIZE] = 0;
- }
- else
- {
- /* add a new sector/cluster for last entry */
- memset(buf, 0, sizeof buf);
- do {
- err = fat_readwrite(&dir->file, 1, buf, true);
- if (err<1)
- return -6;
- } while (dir->file.sectornum <
- (int)fat_bpb.bpb_secperclus);
- }
- }
- done = true;
- break;
- }
+ /* 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;
+
+ err=fat_seek(&dir->file, lastentry / DIR_ENTRIES_PER_SECTOR);
+ if (err<0)
+ return -6;
+
+ err = fat_readwrite(&dir->file, 1, buf, false);
+ if (err<1)
+ return -7;
+
+ /* clear last entry */
+ buf[idx] = 0;
+
+ err=fat_seek(&dir->file, lastentry / DIR_ENTRIES_PER_SECTOR);
+ if (err<0)
+ return -8;
+
+ /* we must clear entire last cluster */
+ do {
+ err = fat_readwrite(&dir->file, 1, buf, true);
+ if (err<1)
+ return -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 {
+ err = fat_readwrite(&dir->file, 1, buf, true);
+ if (err<1)
+ return -10;
+ } while (dir->file.sectornum < (int)fat_bpb.bpb_secperclus);
}
}
@@ -916,106 +983,70 @@ unsigned char char2dos(unsigned char c)
static int create_dos_name(unsigned char *name, unsigned char *newname)
{
- unsigned char n[12];
- unsigned char c;
- int i;
- char *ext;
+ int i,j;
- strncpy(n, name, sizeof n);
-
- ext = strchr(n, '.');
- if(ext)
- {
- *ext++ = 0;
- }
-
- /* The file name is either empty, or there was only an extension.
- In either case it is illegal. */
- if(n[0] == 0)
- {
- return -2;
- }
-
/* Name part */
- for(i = 0;n[i] && (i < 8);i++)
- {
- c = char2dos(n[i]);
- if(c)
- {
- newname[i] = toupper(c);
- }
- }
- while(i < 8)
+ for (i=0, j=0; name[i] && (j < 8); i++)
{
- newname[i++] = ' ';
+ unsigned char c = char2dos(name[i]);
+ if (c)
+ newname[j++] = toupper(c);
}
+ while (j < 8)
+ newname[j++] = ' ';
/* Extension part */
- for (i = 0;ext && ext[i] && (i < 3);i++)
- {
- c = char2dos(ext[i]);
- if (c)
- {
- newname[8+i] = toupper(c);
- }
- }
- while(i < 3)
- {
- newname[8+i++] = ' ';
- }
+ snprintf(newname+8, 4, "%03X", (unsigned)rand() & 0xfff);
- newname[11] = 0;
return 0;
}
static int update_file_size( struct fat_file* file, int size )
{
unsigned char buf[SECTOR_SIZE];
- int sector = file->dirsector + fat_bpb.startsector;
- unsigned char* entry = buf + file->direntry * DIR_ENTRY_SIZE;
+ int sector = file->direntry / DIR_ENTRIES_PER_SECTOR;
+ unsigned char* entry =
+ buf + DIR_ENTRY_SIZE * (file->direntry % DIR_ENTRIES_PER_SECTOR);
unsigned int* sizeptr;
unsigned short* clusptr;
+ struct fat_file dir;
int err;
- LDEBUGF("update_file_size(cluster:%x entry:%d sector:%x size:%d)\n",
- file->firstcluster, file->direntry, file->dirsector, size);
+ LDEBUGF("update_file_size(cluster:%x entry:%d size:%d)\n",
+ file->firstcluster, file->direntry, size);
- if ( file->direntry >= DIR_ENTRIES_PER_SECTOR )
- panicf("update_file_size(): Illegal entry %d!\n",file->direntry);
+ /* create a temporary file handle for the dir holding this file */
+ err = fat_open(file->dircluster, &dir, NULL);
+ if (err<0)
+ return -1;
- if ( file->direntry < 0 )
- panicf("update_file_size(): Illegal entry %d!\n",file->direntry);
+ err = fat_seek( &dir, sector );
+ if (err<0)
+ return -2;
- err = ata_read_sectors(sector, 1, buf);
- if (err)
- {
- DEBUGF( "update_file_size() - Couldn't read dir sector %d"
- " (error code %d)\n", sector, err);
- return -1;
- }
+ err = fat_readwrite(&dir, 1, buf, false);
+ if (err<1)
+ return -3;
- if ( size == -1 ) {
- /* mark entry deleted */
- entry[0] = 0xe5;
- }
- else {
- clusptr = (short*)(entry + FATDIR_FSTCLUSHI);
- *clusptr = SWAB16(file->firstcluster >> 16);
+ if (!entry[0] || entry[0] == 0xe5)
+ panicf("Updating size on empty dir entry %d\n", file->direntry);
- clusptr = (short*)(entry + FATDIR_FSTCLUSLO);
- *clusptr = SWAB16(file->firstcluster & 0xffff);
+ clusptr = (short*)(entry + FATDIR_FSTCLUSHI);
+ *clusptr = SWAB16(file->firstcluster >> 16);
+
+ clusptr = (short*)(entry + FATDIR_FSTCLUSLO);
+ *clusptr = SWAB16(file->firstcluster & 0xffff);
- sizeptr = (int*)(entry + FATDIR_FILESIZE);
- *sizeptr = SWAB32(size);
- }
+ sizeptr = (int*)(entry + FATDIR_FILESIZE);
+ *sizeptr = SWAB32(size);
- err = ata_write_sectors(sector, 1, buf);
- if (err)
- {
- DEBUGF( "update_file_size() - Couldn't write dir sector %d"
- " (error code %d)\n", sector, err);
- return -2;
- }
+ err = fat_seek( &dir, sector );
+ if (err<0)
+ return -4;
+
+ err = fat_readwrite(&dir, 1, buf, true);
+ if (err<1)
+ return -5;
return 0;
}
@@ -1057,8 +1088,9 @@ int fat_open(unsigned int startcluster,
/* remember where the file's dir entry is located */
if ( dir ) {
- file->dirsector = dir->sector;
file->direntry = dir->entry - 1;
+ file->direntries = dir->entrycount;
+ file->dircluster = dir->file.firstcluster;
}
LDEBUGF("fat_open(%x), entry %d\n",startcluster,file->direntry);
return 0;
@@ -1113,7 +1145,7 @@ int fat_closewrite(struct fat_file *file, int size)
}
}
- if (file->dirsector)
+ if (file->dircluster)
if (update_file_size(file, size) < 0)
return -1;
@@ -1152,9 +1184,69 @@ int fat_remove(struct fat_file* file)
update_fat_entry(last,0);
last = next;
}
- update_file_size(file, -1);
- file->dirsector = 0;
+
+ /* free all dir entries */
+ if ( file->dircluster ) {
+ unsigned char buf[SECTOR_SIZE];
+ struct fat_file dir;
+ unsigned int entry = file->direntry - file->direntries + 1;
+ unsigned int sector = entry / DIR_ENTRIES_PER_SECTOR;
+ unsigned int i;
+ int err;
+
+ /* create a temporary file handle for the dir holding this file */
+ err = fat_open(file->dircluster, &dir, NULL);
+ if (err<0)
+ return -1;
+
+ err = fat_seek( &dir, sector );
+ if (err<0)
+ return -2;
+
+ err = fat_readwrite(&dir, 1, buf, false);
+ if (err<1)
+ return -3;
+
+ for (i=0; i < file->direntries; i++) {
+ LDEBUGF("Clearing dir entry %d (%d/%d)\n",
+ entry, i+1, file->direntries);
+ buf[(entry % DIR_ENTRIES_PER_SECTOR) * DIR_ENTRY_SIZE] = 0xe5;
+ entry++;
+
+ if ( (entry % DIR_ENTRIES_PER_SECTOR) == 0 ) {
+ /* flush this sector */
+ err = fat_seek(&dir, sector);
+ if (err<0)
+ return -4;
+
+ err = fat_readwrite(&dir, 1, buf, true);
+ if (err<1)
+ return -5;
+
+ if ( i+1 < file->direntries ) {
+ /* read next sector */
+ err = fat_readwrite(&dir, 1, buf, false);
+ if (err<1)
+ return -6;
+ }
+ sector++;
+ }
+ }
+
+ if ( entry % DIR_ENTRIES_PER_SECTOR ) {
+ /* flush this sector */
+ err = fat_seek(&dir, sector);
+ if (err<0)
+ return -7;
+
+ err = fat_readwrite(&dir, 1, buf, true);
+ if (err<1)
+ return -8;
+ }
+ }
+
file->firstcluster = 0;
+ file->dircluster = 0;
return 0;
}
@@ -1384,9 +1476,11 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
int sectoridx=0;
static unsigned char cached_buf[SECTOR_SIZE];
+ dir->entrycount = 0;
+
while(!done)
{
- if ( dir->entry >= DIR_ENTRIES_PER_SECTOR || !dir->sector )
+ if ( !(dir->entry % DIR_ENTRIES_PER_SECTOR) || !dir->sector )
{
err = fat_readwrite(&dir->file, 1, cached_buf, false);
if (err==0) {
@@ -1400,7 +1494,6 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
return -1;
}
dir->sector = dir->file.lastsector;
- dir->entry = 0;
}
for (i = dir->entry % DIR_ENTRIES_PER_SECTOR;
@@ -1414,15 +1507,19 @@ int fat_getnext(struct fat_dir *dir, struct fat_direntry *entry)
if (firstbyte == 0xe5) {
/* free entry */
sectoridx = 0;
+ dir->entrycount = 0;
continue;
}
if (firstbyte == 0) {
/* last entry */
entry->name[0] = 0;
+ dir->entrycount = 0;
return 0;
}
+ dir->entrycount++;
+
/* longname entry? */
if ( ( cached_buf[entrypos + FATDIR_ATTR] &
FAT_ATTR_LONG_NAME_MASK ) == FAT_ATTR_LONG_NAME ) {
diff --git a/firmware/drivers/fat.h b/firmware/drivers/fat.h
index f05acf3..a5abfd5 100644
--- a/firmware/drivers/fat.h
+++ b/firmware/drivers/fat.h
@@ -52,14 +52,16 @@ struct fat_file
int lastcluster; /* cluster of last access */
int lastsector; /* sector of last access */
int sectornum; /* sector number in this cluster */
- int dirsector; /* sector where the dir entry is located */
- int direntry; /* dir entry index in sector */
+ unsigned int direntry; /* short dir entry index from start of dir */
+ unsigned int direntries; /* number of dir entries used by this file */
+ unsigned int dircluster; /* first cluster of dir */
bool eof;
};
struct fat_dir
{
unsigned int entry;
+ unsigned int entrycount;
int sector;
struct fat_file file;
};