📄 dosfs.c
字号:
return dest;
}
/*
Find the first unused FAT entry
You must provide a scratch buffer for one sector (SECTOR_SIZE) and a populated VOLINFO
Returns a FAT32 BAD_CLUSTER value for any error, otherwise the contents of the desired
FAT entry.
Returns FAT32 bad_sector (0x0ffffff7) if there is no free cluster available
*/
uint32_t DFS_GetFreeFAT(PVOLINFO volinfo, uint8_t *scratch)
{
uint32_t i, result = 0xffffffff, scratchcache = 0;
// Search starts at cluster 2, which is the first usable cluster
// NOTE: This search can't terminate at a bad cluster, because there might
// legitimately be bad clusters on the disk.
for (i=2; i < volinfo->numclusters; i++) {
result = DFS_GetFAT(volinfo, scratch, &scratchcache, i);
if (!result) {
return i;
}
}
return 0x0ffffff7; // Can't find a free cluster
}
/*
Open a directory for enumeration by DFS_GetNextDirEnt
You must supply a populated VOLINFO (see DFS_GetVolInfo)
The empty string or a string containing only the directory separator are
considered to be the root directory.
Returns 0 OK, nonzero for any error.
*/
uint32_t DFS_OpenDir(PVOLINFO volinfo, uint8_t *dirname, PDIRINFO dirinfo)
{
// Default behavior is a regular search for existing entries
dirinfo->flags = 0;
if (!strlen((char *) dirname) || (strlen((char *) dirname) == 1 && dirname[0] == DIR_SEPARATOR)) {
if (volinfo->filesystem == FAT32) {
dirinfo->currentcluster = volinfo->rootdir;
dirinfo->currentsector = 0;
dirinfo->currententry = 0;
// read first sector of directory
return DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->dataarea + ((volinfo->rootdir - 2) * volinfo->secperclus), 1);
}
else {
dirinfo->currentcluster = 0;
dirinfo->currentsector = 0;
dirinfo->currententry = 0;
// read first sector of directory
return DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->rootdir, 1);
}
}
// This is not the root directory. We need to find the start of this subdirectory.
// We do this by devious means, using our own companion function DFS_GetNext.
else {
uint8_t tmpfn[12];
uint8_t *ptr = dirname;
uint32_t result;
DIRENT de;
if (volinfo->filesystem == FAT32) {
dirinfo->currentcluster = volinfo->rootdir;
dirinfo->currentsector = 0;
dirinfo->currententry = 0;
// read first sector of directory
if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->dataarea + ((volinfo->rootdir - 2) * volinfo->secperclus), 1))
return DFS_ERRMISC;
}
else {
dirinfo->currentcluster = 0;
dirinfo->currentsector = 0;
dirinfo->currententry = 0;
// read first sector of directory
if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->rootdir, 1))
return DFS_ERRMISC;
}
// skip leading path separators
while (*ptr == DIR_SEPARATOR && *ptr)
ptr++;
// Scan the path from left to right, finding the start cluster of each entry
// Observe that this code is inelegant, but obviates the need for recursion.
while (*ptr) {
DFS_CanonicalToDir(tmpfn, ptr);
de.name[0] = 0;
do {
result = DFS_GetNext(volinfo, dirinfo, &de);
} while (!result && memcmp(de.name, tmpfn, 11));
if (!memcmp(de.name, tmpfn, 11) && ((de.attr & ATTR_DIRECTORY) == ATTR_DIRECTORY)) {
if (volinfo->filesystem == FAT32) {
dirinfo->currentcluster = (uint32_t) de.startclus_l_l |
((uint32_t) de.startclus_l_h) << 8 |
((uint32_t) de.startclus_h_l) << 16 |
((uint32_t) de.startclus_h_h) << 24;
}
else {
dirinfo->currentcluster = (uint32_t) de.startclus_l_l |
((uint32_t) de.startclus_l_h) << 8;
}
dirinfo->currentsector = 0;
dirinfo->currententry = 0;
if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->dataarea + ((dirinfo->currentcluster - 2) * volinfo->secperclus), 1))
return DFS_ERRMISC;
}
else if (!memcmp(de.name, tmpfn, 11) && !(de.attr & ATTR_DIRECTORY))
return DFS_NOTFOUND;
// seek to next item in list
while (*ptr != DIR_SEPARATOR && *ptr)
ptr++;
if (*ptr == DIR_SEPARATOR)
ptr++;
}
if (!dirinfo->currentcluster)
return DFS_NOTFOUND;
}
return DFS_OK;
}
/*
Get next entry in opened directory structure. Copies fields into the dirent
structure, updates dirinfo. Note that it is the _caller's_ responsibility to
handle the '.' and '..' entries.
A deleted file will be returned as a NULL entry (first char of filename=0)
by this code. Filenames beginning with 0x05 will be translated to 0xE5
automatically. Long file name entries will be returned as NULL.
returns DFS_EOF if there are no more entries, DFS_OK if this entry is valid,
or DFS_ERRMISC for a media error
*/
uint32_t DFS_GetNext(PVOLINFO volinfo, PDIRINFO dirinfo, PDIRENT dirent)
{
uint32_t tempint; // required by DFS_GetFAT
// Do we need to read the next sector of the directory?
if (dirinfo->currententry >= SECTOR_SIZE / sizeof(DIRENT)) {
dirinfo->currententry = 0;
dirinfo->currentsector++;
// Root directory; special case handling
// Note that currentcluster will only ever be zero if both:
// (a) this is the root directory, and
// (b) we are on a FAT12/16 volume, where the root dir can't be expanded
if (dirinfo->currentcluster == 0) {
// Trying to read past end of root directory?
if (dirinfo->currentsector * (SECTOR_SIZE / sizeof(DIRENT)) >= volinfo->rootentries)
return DFS_EOF;
// Otherwise try to read the next sector
if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->rootdir + dirinfo->currentsector, 1))
return DFS_ERRMISC;
}
// Normal handling
else {
if (dirinfo->currentsector >= volinfo->secperclus) {
dirinfo->currentsector = 0;
if ((dirinfo->currentcluster >= 0xff7 && volinfo->filesystem == FAT12) ||
(dirinfo->currentcluster >= 0xfff7 && volinfo->filesystem == FAT16) ||
(dirinfo->currentcluster >= 0x0ffffff7 && volinfo->filesystem == FAT32)) {
// We are at the end of the directory chain. If this is a normal
// find operation, we should indicate that there is nothing more
// to see.
if (!(dirinfo->flags & DFS_DI_BLANKENT))
return DFS_EOF;
// On the other hand, if this is a "find free entry" search,
// we need to tell the caller to allocate a new cluster
else
return DFS_ALLOCNEW;
}
dirinfo->currentcluster = DFS_GetFAT(volinfo, dirinfo->scratch, &tempint, dirinfo->currentcluster);
}
if (DFS_ReadSector(volinfo->unit, dirinfo->scratch, volinfo->dataarea + ((dirinfo->currentcluster - 2) * volinfo->secperclus) + dirinfo->currentsector, 1))
return DFS_ERRMISC;
}
}
memcpy(dirent, &(((PDIRENT) dirinfo->scratch)[dirinfo->currententry]), sizeof(DIRENT));
if (dirent->name[0] == 0) { // no more files in this directory
// If this is a "find blank" then we can reuse this name.
if (dirinfo->flags & DFS_DI_BLANKENT)
return DFS_OK;
else
return DFS_EOF;
}
if (dirent->name[0] == 0xe5) // handle deleted file entries
dirent->name[0] = 0;
else if ((dirent->attr & ATTR_LONG_NAME) == ATTR_LONG_NAME)
dirent->name[0] = 0;
else if (dirent->name[0] == 0x05) // handle kanji filenames beginning with 0xE5
dirent->name[0] = 0xe5;
dirinfo->currententry++;
return DFS_OK;
}
/*
INTERNAL
Find a free directory entry in the directory specified by path
This function MAY cause a disk write if it is necessary to extend the directory
size.
Note - di.scratch must be preinitialized to point to a sector scratch buffer
de is a scratch structure
Returns DFS_ERRMISC if a new entry could not be located or created
de is updated with the same return information you would expect from DFS_GetNext
*/
uint32_t DFS_GetFreeDirEnt(PVOLINFO volinfo, uint8_t *path, PDIRINFO di, PDIRENT de)
{
uint32_t tempclus,i;
if (DFS_OpenDir(volinfo, path, di))
return DFS_NOTFOUND;
// Set "search for empty" flag so DFS_GetNext knows what we're doing
di->flags |= DFS_DI_BLANKENT;
// We seek through the directory looking for an empty entry
// Note we are reusing tempclus as a temporary result holder.
tempclus = 0;
do {
tempclus = DFS_GetNext(volinfo, di, de);
// Empty entry found
if (tempclus == DFS_OK && (!de->name[0])) {
return DFS_OK;
}
// End of root directory reached
else if (tempclus == DFS_EOF)
return DFS_ERRMISC;
else if (tempclus == DFS_ALLOCNEW) {
tempclus = DFS_GetFreeFAT(volinfo, di->scratch);
if (tempclus == 0x0ffffff7)
return DFS_ERRMISC;
// write out zeroed sectors to the new cluster
memset(di->scratch, 0, SECTOR_SIZE);
for (i=0;i<volinfo->secperclus;i++) {
if (DFS_WriteSector(volinfo->unit, di->scratch, volinfo->dataarea + ((tempclus - 2) * volinfo->secperclus) + i, 1))
return DFS_ERRMISC;
}
// Point old end cluster to newly allocated cluster
i = 0;
DFS_SetFAT(volinfo, di->scratch, &i, di->currentcluster, tempclus);
// Update DIRINFO so caller knows where to place the new file
di->currentcluster = tempclus;
di->currentsector = 0;
di->currententry = 1; // since the code coming after this expects to subtract 1
// Mark newly allocated cluster as end of chain
switch(volinfo->filesystem) {
case FAT12: tempclus = 0xff8; break;
case FAT16: tempclus = 0xfff8; break;
case FAT32: tempclus = 0x0ffffff8; break;
default: return DFS_ERRMISC;
}
DFS_SetFAT(volinfo, di->scratch, &i, di->currentcluster, tempclus);
}
} while (!tempclus);
// We shouldn't get here
return DFS_ERRMISC;
}
/*
Open a file for reading or writing. You supply populated VOLINFO, a path to the file,
mode (DFS_READ or DFS_WRITE) and an empty fileinfo structure. You also need to
provide a pointer to a sector-sized scratch buffer.
Returns various DFS_* error states. If the result is DFS_OK, fileinfo can be used
to access the file from this point on.
*/
uint32_t DFS_OpenFile(PVOLINFO volinfo, uint8_t *path, uint8_t mode, uint8_t *scratch, PFILEINFO fileinfo)
{
uint8_t tmppath[MAX_PATH];
uint8_t filename[12];
uint8_t *p;
DIRINFO di;
DIRENT de;
// larwe 2006-09-16 +1 zero out file structure
memset(fileinfo, 0, sizeof(FILEINFO));
// save access mode
fileinfo->mode = mode;
// Get a local copy of the path. If it's longer than MAX_PATH, abort.
strncpy((char *) tmppath, (char *) path, MAX_PATH);
tmppath[MAX_PATH - 1] = 0;
if (strcmp((char *) path,(char *) tmppath)) {
return DFS_PATHLEN;
}
// strip leading path separators
while (tmppath[0] == DIR_SEPARATOR)
strcpy((char *) tmppath, (char *) tmppath + 1);
// Parse filename off the end of the supplied path
p = tmppath;
while (*(p++));
p--;
while (p > tmppath && *p != DIR_SEPARATOR) // larwe 9/16/06 ">=" to ">" bugfix
p--;
if (*p == DIR_SEPARATOR)
p++;
DFS_CanonicalToDir(filename, p);
if (p > tmppath)
p--;
if (*p == DIR_SEPARATOR || p == tmppath) // larwe 9/16/06 +"|| p == tmppath" bugfix
*p = 0;
// At this point, if our path was MYDIR/MYDIR2/FILE.EXT, filename = "FILE EXT" and
// tmppath = "MYDIR/MYDIR2".
di.scratch = scratch;
if (DFS_OpenDir(volinfo, tmppath, &di))
return DFS_NOTFOUND;
while (!DFS_GetNext(volinfo, &di, &de)) {
if (!memcmp(de.name, filename, 11)) {
// You can't use this function call to open a directory.
if (de.attr & ATTR_DIRECTORY)
return DFS_NOTFOUND;
fileinfo->volinfo = volinfo;
fileinfo->pointer = 0;
// The reason we store this extra info about the file is so that we can
// speedily update the file size, modification date, etc. on a file that is
// opened for writing.
if (di.currentcluster == 0)
fileinfo->dirsector = volinfo->rootdir + di.currentsector;
else
fileinfo->dirsector = volinfo->dataarea + ((di.currentcluster - 2) * volinfo->secperclus) + di.currentsector;
fileinfo->diroffset = di.currententry - 1;
if (volinfo->filesystem == FAT32) {
fileinfo->cluster = (uint32_t) de.startclus_l_l |
((uint32_t) de.startclus_l_h) << 8 |
((uint32_t) de.startclus_h_l) << 16 |
((uint32_t) de.startclus_h_h) << 24;
}
else {
fileinfo->cluster = (uint32_t) de.startclus_l_l |
((uint32_t) de.startclus_l_h) << 8;
}
fileinfo->firstcluster = fileinfo->cluster;
fileinfo->filelen = (uint32_t) de.filesize_0 |
((uint32_t) de.filesize_1) << 8 |
((uint32_t) de.filesize_2) << 16 |
((uint32_t) de.filesize_3) << 24;
return DFS_OK;
}
}
// At this point, we KNOW the file does not exist. If the file was opened
// with write access, we can create it.
if (mode & DFS_WRITE) {
uint32_t cluster, temp;
// Locate or create a directory entry for this file
if (DFS_OK != DFS_GetFreeDirEnt(volinfo, tmppath, &di, &de))
return DFS_ERRMISC;
// put sane values in the directory entry
memset(&de, 0, sizeof(de));
memcpy(de.name, filename, 11);
de.crttime_l = 0x20; // 01:01:00am, Jan 1, 2006.
de.crttime_h = 0x08;
de.crtdate_l = 0x11;
de.crtdate_h = 0x34;
de.lstaccdate_l = 0x11;
de.lstaccdate_h = 0x34;
de.wrttime_l = 0x20;
de.wrttime_h = 0x08;
de.wrtdate_l = 0x11;
de.wrtdate_h = 0x34;
// allocate a starting cluster for the directory entry
cluster = DFS_GetFreeFAT(volinfo, scratch);
de.startclus_l_l = cluster & 0xff;
de.startclus_l_h = (cluster & 0xff00) >> 8;
de.startclus_h_l = (cluster & 0xff0000) >> 16;
de.startclus_h_h = (cluster & 0xff000000) >> 24;
// update FILEINFO for our caller's sake
fileinfo->volinfo = volinfo;
fileinfo->pointer = 0;
// The reason we store this extra info about the file is so that we can
// speedily update the file size, modification date, etc. on a file that is
// opened for writing.
if (di.currentcluster == 0)
fileinfo->dirsector = volinfo->rootdir + di.currentsector;
else
fileinfo->dirsector = volinfo->dataarea + ((di.currentcluster - 2) * volinfo->secperclus) + di.currentsector;
fileinfo->diroffset = di.currententry - 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -