mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-25 09:16:07 +03:00
Retain SD card log data when power is lost in flight
This commit is contained in:
parent
6055ab2432
commit
536579e83a
4 changed files with 303 additions and 129 deletions
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
* This file is part of Cleanflight.
|
||||
*
|
||||
* Cleanflight is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Cleanflight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is a FAT16/FAT32 filesystem for SD cards which uses asynchronous operations: The caller need never wait
|
||||
* for the SD card to be ready.
|
||||
|
@ -27,6 +44,12 @@
|
|||
#include "fat_standard.h"
|
||||
#include "drivers/sdcard.h"
|
||||
|
||||
#ifdef AFATFS_DEBUG
|
||||
#define ONLY_EXPOSE_FOR_TESTING
|
||||
#else
|
||||
#define ONLY_EXPOSE_FOR_TESTING static
|
||||
#endif
|
||||
|
||||
#define AFATFS_NUM_CACHE_SECTORS 8
|
||||
|
||||
// FAT filesystems are allowed to differ from these parameters, but we choose not to support those weird filesystems:
|
||||
|
@ -35,6 +58,9 @@
|
|||
|
||||
#define AFATFS_MAX_OPEN_FILES 3
|
||||
|
||||
#define AFATFS_DEFAULT_FILE_DATE FAT_MAKE_DATE(2015, 12, 01)
|
||||
#define AFATFS_DEFAULT_FILE_TIME FAT_MAKE_TIME(00, 00, 00)
|
||||
|
||||
#define AFATFS_FILES_PER_DIRECTORY_SECTOR (AFATFS_SECTOR_SIZE / sizeof(fatDirectoryEntry_t))
|
||||
|
||||
#define AFATFS_FAT32_FAT_ENTRIES_PER_SECTOR (AFATFS_SECTOR_SIZE / sizeof(uint32_t))
|
||||
|
@ -78,6 +104,12 @@
|
|||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
typedef enum {
|
||||
AFATFS_SAVE_DIRECTORY_NORMAL,
|
||||
AFATFS_SAVE_DIRECTORY_FOR_CLOSE,
|
||||
AFATFS_SAVE_DIRECTORY_DELETED,
|
||||
} afatfsSaveDirectoryEntryMode_e;
|
||||
|
||||
typedef enum {
|
||||
AFATFS_CACHE_STATE_EMPTY,
|
||||
AFATFS_CACHE_STATE_IN_SYNC,
|
||||
|
@ -190,6 +222,7 @@ typedef struct afatfsCreateFile_t {
|
|||
afatfsFileCallback_t callback;
|
||||
|
||||
uint8_t phase;
|
||||
uint8_t filename[FAT_FILENAME_LENGTH];
|
||||
} afatfsCreateFile_t;
|
||||
|
||||
typedef struct afatfsSeek_t {
|
||||
|
@ -200,16 +233,16 @@ typedef struct afatfsSeek_t {
|
|||
|
||||
typedef enum {
|
||||
AFATFS_APPEND_SUPERCLUSTER_PHASE_INIT = 0,
|
||||
AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FAT,
|
||||
AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FREEFILE_DIRECTORY,
|
||||
AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FAT,
|
||||
AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FILE_DIRECTORY,
|
||||
} afatfsAppendSuperclusterPhase_e;
|
||||
|
||||
typedef struct afatfsAppendSupercluster_t {
|
||||
afatfsAppendSuperclusterPhase_e phase;
|
||||
uint32_t previousCluster;
|
||||
uint32_t fatRewriteStartCluster;
|
||||
uint32_t fatRewriteEndCluster;
|
||||
afatfsAppendSuperclusterPhase_e phase;
|
||||
} afatfsAppendSupercluster_t;
|
||||
|
||||
typedef enum {
|
||||
|
@ -223,9 +256,9 @@ typedef enum {
|
|||
} afatfsAppendFreeClusterPhase_e;
|
||||
|
||||
typedef struct afatfsAppendFreeCluster_t {
|
||||
afatfsAppendFreeClusterPhase_e phase;
|
||||
uint32_t previousCluster;
|
||||
uint32_t searchCluster;
|
||||
afatfsAppendFreeClusterPhase_e phase;
|
||||
} afatfsAppendFreeCluster_t;
|
||||
|
||||
typedef enum {
|
||||
|
@ -241,6 +274,7 @@ typedef struct afatfsExtendSubdirectory_t {
|
|||
afatfsAppendFreeCluster_t appendFreeCluster;
|
||||
|
||||
afatfsExtendSubdirectoryPhase_e phase;
|
||||
|
||||
uint32_t parentDirectoryCluster;
|
||||
afatfsFileCallback_t callback;
|
||||
} afatfsExtendSubdirectory_t;
|
||||
|
@ -311,6 +345,30 @@ typedef struct afatfsFile_t {
|
|||
// The byte offset of the cursor within the file
|
||||
uint32_t cursorOffset;
|
||||
|
||||
/* The file size in bytes as seen by users of the filesystem (the exact length of the file they've written).
|
||||
*
|
||||
* This is only used by users of the filesystem, not us, so it only needs to be up to date for fseek() (to clip
|
||||
* seeks to the EOF), fread(), feof(), and fclose() (which writes the logicalSize to the directory).
|
||||
*
|
||||
* It becomes out of date when we fwrite() to extend the length of the file. In this situation, feof() is properly
|
||||
* true, so we don't have to update the logicalSize for fread() or feof() to get the correct result. We only need
|
||||
* to update it when we seek backwards (so we don't forget the logical EOF position), or fclose().
|
||||
*/
|
||||
uint32_t logicalSize;
|
||||
|
||||
/* The allocated size in bytes based on how many clusters have been assigned to the file. Always a multiple of
|
||||
* the cluster size.
|
||||
*
|
||||
* This is an underestimate for existing files, because we don't bother to check precisely how long the chain is
|
||||
* at the time the file is opened (it might be longer than needed to contain the logical size), but assuming the
|
||||
* filesystem metadata is correct, it should always be at least as many clusters as needed to contain logicalSize.
|
||||
*
|
||||
* Since this is an estimate, we only use it to exaggerate the filesize in the directory entry of a file that is
|
||||
* currently being written (so that the final cluster of the file will be entirely readable if power is lost before
|
||||
* we can could update the directory entry with a new logicalSize).
|
||||
*/
|
||||
uint32_t physicalSize;
|
||||
|
||||
/*
|
||||
* The cluster that the file pointer is currently within. When seeking to the end of the file, this will be
|
||||
* set to zero.
|
||||
|
@ -323,6 +381,7 @@ typedef struct afatfsFile_t {
|
|||
uint32_t cursorPreviousCluster;
|
||||
|
||||
uint8_t mode; // A combination of AFATFS_FILE_MODE_* flags
|
||||
uint8_t attrib; // FAT directory entry attributes for this file
|
||||
|
||||
/* We hold on to one sector entry in the cache and remember its index here. The cache is invalidated when we
|
||||
* seek across a sector boundary. This allows fwrite() to complete faster because it doesn't need to check the
|
||||
|
@ -334,8 +393,9 @@ typedef struct afatfsFile_t {
|
|||
|
||||
// The position of our directory entry on the disk (so we can update it without consulting a parent directory file)
|
||||
afatfsDirEntryPointer_t directoryEntryPos;
|
||||
// A copy of the file's FAT directory entry (especially fileSize and first cluster details)
|
||||
fatDirectoryEntry_t directoryEntry;
|
||||
|
||||
// The first cluster number of the file, or 0 if this file is empty
|
||||
uint32_t firstCluster;
|
||||
|
||||
// State for a queued operation on the file
|
||||
struct afatfsOperation_t operation;
|
||||
|
@ -430,12 +490,6 @@ static bool isPowerOfTwo(unsigned int x)
|
|||
return ((x != 0) && ((x & (~x + 1)) == x));
|
||||
}
|
||||
|
||||
static uint32_t afatfs_firstCluster(afatfsFilePtr_t file)
|
||||
{
|
||||
// Take care that the uint16_t doesn't get promoted to signed int...
|
||||
return ((uint32_t) file->directoryEntry.firstClusterHigh << 16) | (file->directoryEntry.firstClusterLow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for conditions that should always be true (and if otherwise mean a bug or a corrupt filesystem).
|
||||
*
|
||||
|
@ -471,7 +525,8 @@ static uint32_t afatfs_fatEntriesPerSector()
|
|||
/**
|
||||
* Size of a FAT cluster in bytes
|
||||
*/
|
||||
static uint32_t afatfs_clusterSize()
|
||||
ONLY_EXPOSE_FOR_TESTING
|
||||
uint32_t afatfs_clusterSize()
|
||||
{
|
||||
return afatfs.sectorsPerCluster * AFATFS_SECTOR_SIZE;
|
||||
}
|
||||
|
@ -1028,6 +1083,14 @@ static afatfsOperationStatus_e afatfs_FATSetNextCluster(uint32_t startCluster, u
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bring the logical filesize up to date with the current cursor position.
|
||||
*/
|
||||
static void afatfs_fileUpdateFilesize(afatfsFile_t *file)
|
||||
{
|
||||
file->logicalSize = MAX(file->logicalSize, file->cursorOffset);
|
||||
}
|
||||
|
||||
static void afatfs_fileUnlockCacheSector(afatfsFilePtr_t file)
|
||||
{
|
||||
if (file->writeLockedCacheIndex != -1) {
|
||||
|
@ -1095,8 +1158,8 @@ static afatfsFindClusterStatus_e afatfs_findClusterWithCondition(afatfsClusterSe
|
|||
|
||||
#ifdef AFATFS_USE_FREEFILE
|
||||
// If we're looking inside the freefile, we won't find any free clusters! Skip it!
|
||||
if (afatfs.freeFile.directoryEntry.fileSize > 0 && *cluster == afatfs_firstCluster(&afatfs.freeFile)) {
|
||||
*cluster += (afatfs.freeFile.directoryEntry.fileSize + afatfs_clusterSize() - 1) / afatfs_clusterSize();
|
||||
if (afatfs.freeFile.logicalSize > 0 && *cluster == afatfs.freeFile.firstCluster) {
|
||||
*cluster += (afatfs.freeFile.logicalSize + afatfs_clusterSize() - 1) / afatfs_clusterSize();
|
||||
|
||||
// Maintain alignment
|
||||
*cluster = roundUpTo(*cluster, jump);
|
||||
|
@ -1170,7 +1233,7 @@ static afatfsOperationStatus_e afatfs_fileGetNextCluster(afatfsFilePtr_t file, u
|
|||
(void) file;
|
||||
#else
|
||||
if ((file->mode & AFATFS_FILE_MODE_CONTIGUOUS) != 0) {
|
||||
uint32_t freeFileStart = afatfs_firstCluster(&afatfs.freeFile);
|
||||
uint32_t freeFileStart = afatfs.freeFile.firstCluster;
|
||||
|
||||
afatfs_assert(currentCluster + 1 <= freeFileStart);
|
||||
|
||||
|
@ -1289,18 +1352,24 @@ static afatfsOperationStatus_e afatfs_FATFillWithPattern(afatfsFATPattern_e patt
|
|||
/**
|
||||
* Write the directory entry for the file into its `directoryEntryPos` position in its containing directory.
|
||||
*
|
||||
* mode:
|
||||
* AFATFS_SAVE_DIRECTORY_NORMAL - Store the file's physical size, not the logical size, in the directory entry
|
||||
* AFATFS_SAVE_DIRECTORY_FOR_CLOSE - We're done extending the file so we can write the logical size now.
|
||||
* AFATFS_SAVE_DIRECTORY_DELETED - Mark the directory entry as deleted
|
||||
*
|
||||
* Returns:
|
||||
* AFATFS_OPERATION_SUCCESS - The directory entry has been stored into the directory sector in cache.
|
||||
* AFATFS_OPERATION_IN_PROGRESS - Cache is too busy, retry later
|
||||
* AFATFS_OPERATION_FAILURE - If the filesystem enters the fatal state
|
||||
*/
|
||||
static afatfsOperationStatus_e afatfs_saveDirectoryEntry(afatfsFilePtr_t file)
|
||||
static afatfsOperationStatus_e afatfs_saveDirectoryEntry(afatfsFilePtr_t file, afatfsSaveDirectoryEntryMode_e mode)
|
||||
{
|
||||
uint8_t *sector;
|
||||
afatfsOperationStatus_e result;
|
||||
|
||||
if (file->directoryEntryPos.sectorNumberPhysical == 0)
|
||||
if (file->directoryEntryPos.sectorNumberPhysical == 0) {
|
||||
return AFATFS_OPERATION_SUCCESS; // Root directories don't have a directory entry
|
||||
}
|
||||
|
||||
result = afatfs_cacheSector(file->directoryEntryPos.sectorNumberPhysical, §or, AFATFS_CACHE_READ | AFATFS_CACHE_WRITE);
|
||||
|
||||
|
@ -1311,11 +1380,32 @@ static afatfsOperationStatus_e afatfs_saveDirectoryEntry(afatfsFilePtr_t file)
|
|||
if (result == AFATFS_OPERATION_SUCCESS) {
|
||||
// (sub)directories don't store a filesize in their directory entry:
|
||||
if (file->type == AFATFS_FILE_TYPE_DIRECTORY) {
|
||||
file->directoryEntry.fileSize = 0;
|
||||
file->logicalSize = 0;
|
||||
}
|
||||
|
||||
if (afatfs_assert(file->directoryEntryPos.entryIndex >= 0)) {
|
||||
memcpy(sector + file->directoryEntryPos.entryIndex * FAT_DIRECTORY_ENTRY_SIZE, &file->directoryEntry, FAT_DIRECTORY_ENTRY_SIZE);
|
||||
fatDirectoryEntry_t *entry = (fatDirectoryEntry_t *) sector + file->directoryEntryPos.entryIndex;
|
||||
|
||||
switch (mode) {
|
||||
case AFATFS_SAVE_DIRECTORY_NORMAL:
|
||||
/* We exaggerate the length of the written file so that if power is lost, the end of the file will
|
||||
* still be readable (though the very tail of the file will be uninitialized data).
|
||||
*
|
||||
* This way we can avoid updating the directory entry too many times during fwrites() on the file.
|
||||
*/
|
||||
entry->fileSize = file->physicalSize;
|
||||
break;
|
||||
case AFATFS_SAVE_DIRECTORY_DELETED:
|
||||
entry->filename[0] = FAT_DELETED_FILE_MARKER;
|
||||
//Fall through
|
||||
|
||||
case AFATFS_SAVE_DIRECTORY_FOR_CLOSE:
|
||||
// We write the true length of the file on close.
|
||||
entry->fileSize = file->logicalSize;
|
||||
}
|
||||
|
||||
entry->firstClusterHigh = file->firstCluster >> 16;
|
||||
entry->firstClusterLow = file->firstCluster & 0xFFFF;
|
||||
} else {
|
||||
return AFATFS_OPERATION_FAILURE;
|
||||
}
|
||||
|
@ -1359,12 +1449,11 @@ static afatfsOperationStatus_e afatfs_appendRegularFreeClusterContinue(afatfsFil
|
|||
|
||||
// Make the cluster available for us to write in
|
||||
file->cursorCluster = opState->searchCluster;
|
||||
file->physicalSize += afatfs_clusterSize();
|
||||
|
||||
if (opState->previousCluster == 0) {
|
||||
// This is the new first cluster in the file so we also need to update the directory entry
|
||||
file->directoryEntry.firstClusterHigh = opState->searchCluster >> 16;
|
||||
file->directoryEntry.firstClusterLow = opState->searchCluster & 0xFFFF;
|
||||
|
||||
// This is the new first cluster in the file
|
||||
file->firstCluster = opState->searchCluster;
|
||||
}
|
||||
|
||||
opState->phase = AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FAT1;
|
||||
|
@ -1385,18 +1474,7 @@ static afatfsOperationStatus_e afatfs_appendRegularFreeClusterContinue(afatfsFil
|
|||
status = afatfs_FATSetNextCluster(opState->searchCluster, 0xFFFFFFFF);
|
||||
|
||||
if (status == AFATFS_OPERATION_SUCCESS) {
|
||||
// Is the new cluster the first cluster in the file?
|
||||
if (opState->previousCluster == 0) {
|
||||
opState->phase = AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FILE_DIRECTORY;
|
||||
} else {
|
||||
opState->phase = AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FAT2;
|
||||
}
|
||||
goto doMore;
|
||||
}
|
||||
break;
|
||||
case AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FILE_DIRECTORY:
|
||||
if (afatfs_saveDirectoryEntry(file) == AFATFS_OPERATION_SUCCESS) {
|
||||
opState->phase = AFATFS_APPEND_FREE_CLUSTER_PHASE_COMPLETE;
|
||||
opState->phase = AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FAT2;
|
||||
goto doMore;
|
||||
}
|
||||
break;
|
||||
|
@ -1405,6 +1483,12 @@ static afatfsOperationStatus_e afatfs_appendRegularFreeClusterContinue(afatfsFil
|
|||
status = afatfs_FATSetNextCluster(opState->previousCluster, opState->searchCluster);
|
||||
|
||||
if (status == AFATFS_OPERATION_SUCCESS) {
|
||||
opState->phase = AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FILE_DIRECTORY;
|
||||
goto doMore;
|
||||
}
|
||||
break;
|
||||
case AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FILE_DIRECTORY:
|
||||
if (afatfs_saveDirectoryEntry(file, AFATFS_SAVE_DIRECTORY_NORMAL) == AFATFS_OPERATION_SUCCESS) {
|
||||
opState->phase = AFATFS_APPEND_FREE_CLUSTER_PHASE_COMPLETE;
|
||||
goto doMore;
|
||||
}
|
||||
|
@ -1469,7 +1553,8 @@ static afatfsOperationStatus_e afatfs_appendRegularFreeCluster(afatfsFilePtr_t f
|
|||
/**
|
||||
* Size of a AFATFS supercluster in bytes
|
||||
*/
|
||||
static uint32_t afatfs_superClusterSize()
|
||||
ONLY_EXPOSE_FOR_TESTING
|
||||
uint32_t afatfs_superClusterSize()
|
||||
{
|
||||
return afatfs_fatEntriesPerSector() * afatfs_clusterSize();
|
||||
}
|
||||
|
@ -1493,7 +1578,7 @@ static afatfsOperationStatus_e afatfs_appendSuperclusterContinue(afatfsFile_t *f
|
|||
switch (opState->phase) {
|
||||
case AFATFS_APPEND_SUPERCLUSTER_PHASE_INIT:
|
||||
// Our file steals the first cluster of the freefile
|
||||
freeFileStartCluster = afatfs_firstCluster(&afatfs.freeFile);
|
||||
freeFileStartCluster = afatfs.freeFile.firstCluster;
|
||||
|
||||
// The new supercluster needs to have its clusters chained contiguously and marked with a terminator at the end
|
||||
opState->fatRewriteStartCluster = freeFileStartCluster;
|
||||
|
@ -1501,8 +1586,7 @@ static afatfsOperationStatus_e afatfs_appendSuperclusterContinue(afatfsFile_t *f
|
|||
|
||||
if (opState->previousCluster == 0) {
|
||||
// This is the new first cluster in the file so we need to update the directory entry
|
||||
file->directoryEntry.firstClusterHigh = afatfs.freeFile.directoryEntry.firstClusterHigh;
|
||||
file->directoryEntry.firstClusterLow = afatfs.freeFile.directoryEntry.firstClusterLow;
|
||||
file->firstCluster = afatfs.freeFile.firstCluster;
|
||||
} else {
|
||||
/*
|
||||
* We also need to update the FAT of the supercluster that used to end the file so that it no longer
|
||||
|
@ -1512,7 +1596,8 @@ static afatfsOperationStatus_e afatfs_appendSuperclusterContinue(afatfsFile_t *f
|
|||
}
|
||||
|
||||
// Remove the first supercluster from the freefile
|
||||
afatfs.freeFile.directoryEntry.fileSize -= afatfs_superClusterSize();
|
||||
afatfs.freeFile.logicalSize -= afatfs_superClusterSize();
|
||||
afatfs.freeFile.physicalSize -= afatfs_superClusterSize();
|
||||
|
||||
uint32_t newFreeFileStartCluster;
|
||||
|
||||
|
@ -1524,35 +1609,34 @@ static afatfsOperationStatus_e afatfs_appendSuperclusterContinue(afatfsFile_t *f
|
|||
*/
|
||||
newFreeFileStartCluster = freeFileStartCluster + afatfs_fatEntriesPerSector();
|
||||
|
||||
afatfs.freeFile.directoryEntry.firstClusterHigh = newFreeFileStartCluster >> 16;
|
||||
afatfs.freeFile.directoryEntry.firstClusterLow = newFreeFileStartCluster & 0xFFFF;
|
||||
afatfs.freeFile.firstCluster = newFreeFileStartCluster;
|
||||
|
||||
opState->phase = AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FAT;
|
||||
// Update the file's allocated size
|
||||
file->physicalSize += afatfs_superClusterSize();
|
||||
|
||||
opState->phase = AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FREEFILE_DIRECTORY;
|
||||
goto doMore;
|
||||
break;
|
||||
case AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FREEFILE_DIRECTORY:
|
||||
// First update the freefile's directory entry to remove the first supercluster so we don't risk cross-linking the file
|
||||
status = afatfs_saveDirectoryEntry(&afatfs.freeFile, AFATFS_SAVE_DIRECTORY_NORMAL);
|
||||
|
||||
if (status == AFATFS_OPERATION_SUCCESS) {
|
||||
opState->phase = AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FAT;
|
||||
goto doMore;
|
||||
}
|
||||
break;
|
||||
case AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FAT:
|
||||
status = afatfs_FATFillWithPattern(AFATFS_FAT_PATTERN_TERMINATED_CHAIN, &opState->fatRewriteStartCluster, opState->fatRewriteEndCluster);
|
||||
|
||||
if (status == AFATFS_OPERATION_SUCCESS) {
|
||||
opState->phase = AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FREEFILE_DIRECTORY;
|
||||
opState->phase = AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FILE_DIRECTORY;
|
||||
goto doMore;
|
||||
}
|
||||
break;
|
||||
case AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FREEFILE_DIRECTORY:
|
||||
status = afatfs_saveDirectoryEntry(&afatfs.freeFile);
|
||||
|
||||
if (status == AFATFS_OPERATION_SUCCESS) {
|
||||
if (opState->previousCluster == 0) {
|
||||
// Need to write the new first-cluster to the file's directory entry
|
||||
opState->phase = AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FILE_DIRECTORY;
|
||||
goto doMore;
|
||||
} else {
|
||||
return AFATFS_OPERATION_SUCCESS;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FILE_DIRECTORY:
|
||||
status = afatfs_saveDirectoryEntry(file);
|
||||
// Update the fileSize/firstCluster in the directory entry for the file
|
||||
status = afatfs_saveDirectoryEntry(file, AFATFS_SAVE_DIRECTORY_NORMAL);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1578,7 +1662,7 @@ static afatfsOperationStatus_e afatfs_appendSupercluster(afatfsFilePtr_t file, u
|
|||
if (file->operation.operation == AFATFS_FILE_OPERATION_APPEND_SUPERCLUSTER)
|
||||
return AFATFS_OPERATION_IN_PROGRESS;
|
||||
|
||||
if (afatfs.freeFile.directoryEntry.fileSize < superClusterSize) {
|
||||
if (afatfs.freeFile.logicalSize < superClusterSize) {
|
||||
afatfs.filesystemFull = true;
|
||||
}
|
||||
|
||||
|
@ -1596,7 +1680,7 @@ static afatfsOperationStatus_e afatfs_appendSupercluster(afatfsFilePtr_t file, u
|
|||
* We can go ahead and write to that space before the FAT and directory are updated by the
|
||||
* queued operation:
|
||||
*/
|
||||
file->cursorCluster = afatfs_firstCluster(&afatfs.freeFile);
|
||||
file->cursorCluster = afatfs.freeFile.firstCluster;
|
||||
|
||||
status = afatfs_appendSuperclusterContinue(file);
|
||||
|
||||
|
@ -1727,7 +1811,7 @@ static uint8_t* afatfs_fileLockCursorSectorForWrite(afatfsFilePtr_t file)
|
|||
*/
|
||||
if (
|
||||
cursorOffsetInSector > 0
|
||||
|| (file->cursorOffset & ~(AFATFS_SECTOR_SIZE - 1)) + AFATFS_SECTOR_SIZE < file->directoryEntry.fileSize
|
||||
|| (file->cursorOffset & ~(AFATFS_SECTOR_SIZE - 1)) + AFATFS_SECTOR_SIZE < file->logicalSize
|
||||
) {
|
||||
cacheFlags |= AFATFS_CACHE_READ;
|
||||
}
|
||||
|
@ -1859,7 +1943,7 @@ static bool afatfs_fseekInternalContinue(afatfsFile_t *file)
|
|||
file->cursorOffset += opState->seekOffset;
|
||||
}
|
||||
|
||||
file->directoryEntry.fileSize = MAX(file->directoryEntry.fileSize, file->cursorOffset);
|
||||
afatfs_fileUpdateFilesize(file); // TODO do we need this?
|
||||
|
||||
file->operation.operation = AFATFS_FILE_OPERATION_NONE;
|
||||
|
||||
|
@ -1873,6 +1957,8 @@ static bool afatfs_fseekInternalContinue(afatfsFile_t *file)
|
|||
/**
|
||||
* Seek the file pointer forwards by offset bytes. Calls the callback when the seek is complete.
|
||||
*
|
||||
* Will happily seek beyond the logical end of the file.
|
||||
*
|
||||
* Returns:
|
||||
* AFATFS_OPERATION_SUCCESS - The seek was completed immediately
|
||||
* AFATFS_OPERATION_IN_PROGRESS - The seek was queued and will complete later
|
||||
|
@ -1911,17 +1997,21 @@ static afatfsOperationStatus_e afatfs_fseekInternal(afatfsFilePtr_t file, uint32
|
|||
*
|
||||
* Returns:
|
||||
* AFATFS_OPERATION_SUCCESS - The seek was completed immediately
|
||||
* AFATFS_OPERATION_IN_PROGRESS - The seek was queued and will complete later
|
||||
* AFATFS_OPERATION_IN_PROGRESS - The seek was queued and will complete later. Feel free to attempt read/write
|
||||
* operations on the file, they'll fail until the seek is complete.
|
||||
* AFATFS_OPERATION_FAILURE - The seek could not be queued because the file was busy with another operation,
|
||||
* try again later.
|
||||
*/
|
||||
afatfsOperationStatus_e afatfs_fseek(afatfsFilePtr_t file, int32_t offset, afatfsSeek_e whence)
|
||||
{
|
||||
// We need an up-to-date logical filesize so we can clamp seeks to the EOF
|
||||
afatfs_fileUpdateFilesize(file);
|
||||
|
||||
switch (whence) {
|
||||
case AFATFS_SEEK_CUR:
|
||||
if (offset >= 0) {
|
||||
// Only forwards seeks are supported by this routine:
|
||||
return afatfs_fseekInternal(file, offset, NULL);
|
||||
return afatfs_fseekInternal(file, MIN(file->cursorOffset + offset, file->logicalSize), NULL);
|
||||
}
|
||||
|
||||
// Convert a backwards relative seek into a SEEK_SET. TODO considerable room for improvement if within the same cluster
|
||||
|
@ -1929,8 +2019,13 @@ afatfsOperationStatus_e afatfs_fseek(afatfsFilePtr_t file, int32_t offset, afatf
|
|||
break;
|
||||
|
||||
case AFATFS_SEEK_END:
|
||||
// Are we already at the right position?
|
||||
if (file->logicalSize + offset == file->cursorOffset) {
|
||||
return AFATFS_OPERATION_SUCCESS;
|
||||
}
|
||||
|
||||
// Convert into a SEEK_SET
|
||||
offset += file->directoryEntry.fileSize;
|
||||
offset += file->logicalSize;
|
||||
break;
|
||||
|
||||
case AFATFS_SEEK_SET:
|
||||
|
@ -1942,11 +2037,11 @@ afatfsOperationStatus_e afatfs_fseek(afatfsFilePtr_t file, int32_t offset, afatf
|
|||
afatfs_fileUnlockCacheSector(file);
|
||||
|
||||
file->cursorPreviousCluster = 0;
|
||||
file->cursorCluster = afatfs_firstCluster(file);
|
||||
file->cursorCluster = file->firstCluster;
|
||||
file->cursorOffset = 0;
|
||||
|
||||
// Then seek forwards by the offset
|
||||
return afatfs_fseekInternal(file, offset, NULL);
|
||||
return afatfs_fseekInternal(file, MIN((uint32_t) offset, file->logicalSize), NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2065,8 +2160,8 @@ static afatfsOperationStatus_e afatfs_extendSubdirectoryContinue(afatfsFile_t *d
|
|||
|
||||
memset(dirEntries[0].filename, ' ', sizeof(dirEntries[0].filename));
|
||||
dirEntries[0].filename[0] = '.';
|
||||
dirEntries[0].firstClusterHigh = directory->directoryEntry.firstClusterHigh;
|
||||
dirEntries[0].firstClusterLow = directory->directoryEntry.firstClusterLow;
|
||||
dirEntries[0].firstClusterHigh = directory->firstCluster >> 16;
|
||||
dirEntries[0].firstClusterLow = directory->firstCluster & 0xFFFF;
|
||||
dirEntries[0].attrib = FAT_FILE_ATTRIBUTE_DIRECTORY;
|
||||
|
||||
memset(dirEntries[1].filename, ' ', sizeof(dirEntries[1].filename));
|
||||
|
@ -2140,7 +2235,7 @@ static afatfsOperationStatus_e afatfs_extendSubdirectory(afatfsFile_t *directory
|
|||
directory->operation.operation = AFATFS_FILE_OPERATION_EXTEND_SUBDIRECTORY;
|
||||
|
||||
opState->phase = AFATFS_EXTEND_SUBDIRECTORY_PHASE_INITIAL;
|
||||
opState->parentDirectoryCluster = parentDirectory ? afatfs_firstCluster(parentDirectory) : 0;
|
||||
opState->parentDirectoryCluster = parentDirectory ? parentDirectory->firstCluster : 0;
|
||||
opState->callback = callback;
|
||||
|
||||
afatfs_appendRegularFreeClusterInitOperationState(&opState->appendFreeCluster, directory->cursorPreviousCluster);
|
||||
|
@ -2218,17 +2313,17 @@ static afatfsFilePtr_t afatfs_allocateFileHandle()
|
|||
* was a truncate), then the truncate operation's callback is called. This allows the truncation to be called as a
|
||||
* sub-operation without it clearing the parent file operation.
|
||||
*/
|
||||
static afatfsOperationStatus_e afatfs_ftruncateContinue(afatfsFilePtr_t file)
|
||||
static afatfsOperationStatus_e afatfs_ftruncateContinue(afatfsFilePtr_t file, bool markDeleted)
|
||||
{
|
||||
afatfsTruncateFile_t *opState = &file->operation.state.truncateFile;
|
||||
afatfsOperationStatus_e status;
|
||||
uint32_t oldFreeFileStart;
|
||||
uint32_t oldFreeFileStart, freeFileGrow;
|
||||
|
||||
doMore:
|
||||
|
||||
switch (opState->phase) {
|
||||
case AFATFS_TRUNCATE_FILE_UPDATE_DIRECTORY:
|
||||
status = afatfs_saveDirectoryEntry(file);
|
||||
status = afatfs_saveDirectoryEntry(file, markDeleted ? AFATFS_SAVE_DIRECTORY_DELETED : AFATFS_SAVE_DIRECTORY_NORMAL);
|
||||
|
||||
if (status == AFATFS_OPERATION_SUCCESS) {
|
||||
if (opState->endCluster) {
|
||||
|
@ -2250,13 +2345,16 @@ static afatfsOperationStatus_e afatfs_ftruncateContinue(afatfsFilePtr_t file)
|
|||
break;
|
||||
case AFATFS_TRUNCATE_FILE_PREPEND_TO_FREEFILE:
|
||||
// Note, it's okay to run this code several times:
|
||||
oldFreeFileStart = afatfs_firstCluster(&afatfs.freeFile);
|
||||
oldFreeFileStart = afatfs.freeFile.firstCluster;
|
||||
|
||||
afatfs.freeFile.directoryEntry.firstClusterHigh = opState->startCluster >> 16;
|
||||
afatfs.freeFile.directoryEntry.firstClusterLow = opState->startCluster & 0xFFFF;
|
||||
afatfs.freeFile.directoryEntry.fileSize += (oldFreeFileStart - opState->startCluster) * afatfs_clusterSize();
|
||||
afatfs.freeFile.firstCluster = opState->startCluster;
|
||||
|
||||
status = afatfs_saveDirectoryEntry(&afatfs.freeFile);
|
||||
freeFileGrow = (oldFreeFileStart - opState->startCluster) * afatfs_clusterSize();
|
||||
|
||||
afatfs.freeFile.logicalSize += freeFileGrow;
|
||||
afatfs.freeFile.physicalSize += freeFileGrow;
|
||||
|
||||
status = afatfs_saveDirectoryEntry(&afatfs.freeFile, AFATFS_SAVE_DIRECTORY_NORMAL);
|
||||
if (status == AFATFS_OPERATION_SUCCESS) {
|
||||
opState->phase = AFATFS_TRUNCATE_FILE_SUCCESS;
|
||||
goto doMore;
|
||||
|
@ -2308,7 +2406,7 @@ static afatfsOperationStatus_e afatfs_ftruncateContinue(afatfsFilePtr_t file)
|
|||
}
|
||||
|
||||
/**
|
||||
* Truncate the file to zero bytes in length.
|
||||
* Queue an operation to truncate the file to zero bytes in length.
|
||||
*
|
||||
* Returns true if the operation was successfully queued or false if the file is busy (try again later).
|
||||
*
|
||||
|
@ -2326,27 +2424,38 @@ bool afatfs_ftruncate(afatfsFilePtr_t file, afatfsFileCallback_t callback)
|
|||
opState = &file->operation.state.truncateFile;
|
||||
opState->callback = callback;
|
||||
opState->phase = AFATFS_TRUNCATE_FILE_INITIAL;
|
||||
opState->startCluster = afatfs_firstCluster(file);
|
||||
opState->startCluster = file->firstCluster;
|
||||
opState->currentCluster = opState->startCluster;
|
||||
|
||||
if ((file->mode & AFATFS_FILE_MODE_CONTIGUOUS) != 0) {
|
||||
// The file is contiguous and ends where the freefile begins
|
||||
opState->endCluster = afatfs_firstCluster(&afatfs.freeFile);
|
||||
opState->endCluster = afatfs.freeFile.firstCluster;
|
||||
} else {
|
||||
// The range of clusters to delete is not contiguous, so follow it as a linked-list instead
|
||||
opState->endCluster = 0;
|
||||
}
|
||||
|
||||
// We'll drop the cluster chain from the directory entry immediately
|
||||
file->directoryEntry.firstClusterHigh = 0;
|
||||
file->directoryEntry.firstClusterLow = 0;
|
||||
file->directoryEntry.fileSize = 0;
|
||||
file->firstCluster = 0;
|
||||
file->logicalSize = 0;
|
||||
file->physicalSize = 0;
|
||||
|
||||
afatfs_fseek(file, 0, AFATFS_SEEK_SET);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load details from the given FAT directory entry into the file.
|
||||
*/
|
||||
static void afatfs_fileLoadDirectoryEntry(afatfsFile_t *file, fatDirectoryEntry_t *entry)
|
||||
{
|
||||
file->firstCluster = (uint32_t) (entry->firstClusterHigh << 16) | entry->firstClusterLow;
|
||||
file->logicalSize = entry->fileSize;
|
||||
file->physicalSize = roundUpTo(entry->fileSize, afatfs_clusterSize());
|
||||
file->attrib = entry->attrib;
|
||||
}
|
||||
|
||||
static void afatfs_createFileContinue(afatfsFile_t *file)
|
||||
{
|
||||
afatfsCreateFile_t *opState = &file->operation.state.createFile;
|
||||
|
@ -2383,9 +2492,9 @@ static void afatfs_createFileContinue(afatfsFile_t *file)
|
|||
opState->phase = AFATFS_CREATEFILE_PHASE_FAILURE;
|
||||
goto doMore;
|
||||
}
|
||||
} else if (strncmp(entry->filename, (char*) file->directoryEntry.filename, FAT_FILENAME_LENGTH) == 0) {
|
||||
} else if (strncmp(entry->filename, (char*) opState->filename, FAT_FILENAME_LENGTH) == 0) {
|
||||
// We found a file with this name!
|
||||
memcpy(&file->directoryEntry, entry, sizeof(file->directoryEntry));
|
||||
afatfs_fileLoadDirectoryEntry(file, entry);
|
||||
|
||||
afatfs_findLast(&afatfs.currentDirectory);
|
||||
|
||||
|
@ -2407,10 +2516,17 @@ static void afatfs_createFileContinue(afatfsFile_t *file)
|
|||
status = afatfs_allocateDirectoryEntry(&afatfs.currentDirectory, &entry, &file->directoryEntryPos);
|
||||
|
||||
if (status == AFATFS_OPERATION_SUCCESS) {
|
||||
memcpy(entry, &file->directoryEntry, sizeof(file->directoryEntry));
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
|
||||
memcpy(entry->filename, opState->filename, FAT_FILENAME_LENGTH);
|
||||
entry->attrib = file->attrib;
|
||||
entry->creationDate = AFATFS_DEFAULT_FILE_DATE;
|
||||
entry->creationTime = AFATFS_DEFAULT_FILE_TIME;
|
||||
entry->lastWriteDate = AFATFS_DEFAULT_FILE_DATE;
|
||||
entry->lastWriteTime = AFATFS_DEFAULT_FILE_TIME;
|
||||
|
||||
#ifdef AFATFS_DEBUG_VERBOSE
|
||||
fprintf(stderr, "Adding directory entry for %.*s to sector %u\n", FAT_FILENAME_LENGTH, entry->filename, file->directoryEntryPos.sectorNumberPhysical);
|
||||
fprintf(stderr, "Adding directory entry for %.*s to sector %u\n", FAT_FILENAME_LENGTH, opState->filename, file->directoryEntryPos.sectorNumberPhysical);
|
||||
#endif
|
||||
|
||||
opState->phase = AFATFS_CREATEFILE_PHASE_SUCCESS;
|
||||
|
@ -2468,7 +2584,7 @@ static void afatfs_createFileContinue(afatfsFile_t *file)
|
|||
if ((file->mode & AFATFS_FILE_MODE_APPEND) != 0) {
|
||||
// This replaces our open file operation
|
||||
file->operation.operation = AFATFS_FILE_OPERATION_NONE;
|
||||
afatfs_fseekInternal(file, file->directoryEntry.fileSize, opState->callback);
|
||||
afatfs_fseekInternal(file, file->logicalSize, opState->callback);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2506,7 +2622,7 @@ static void afatfs_funlinkContinue(afatfsFilePtr_t file)
|
|||
afatfsUnlinkFile_t *opState = &file->operation.state.unlinkFile;
|
||||
afatfsOperationStatus_e status;
|
||||
|
||||
status = afatfs_ftruncateContinue(file);
|
||||
status = afatfs_ftruncateContinue(file, true);
|
||||
|
||||
if (status == AFATFS_OPERATION_SUCCESS) {
|
||||
// Once the truncation is completed, we can close the file handle
|
||||
|
@ -2538,11 +2654,6 @@ bool afatfs_funlink(afatfsFilePtr_t file, afatfsCallback_t callback)
|
|||
if (!afatfs_ftruncate(file, NULL))
|
||||
return false;
|
||||
|
||||
/* The truncate operation will flush the directory entry for us, so go ahead and mark that as deleted in memory
|
||||
* for it.
|
||||
*/
|
||||
file->directoryEntry.filename[0] = FAT_DELETED_FILE_MARKER;
|
||||
|
||||
/*
|
||||
* The unlink operation has its own private callback field so that the truncate suboperation doesn't end up
|
||||
* calling back early when it completes:
|
||||
|
@ -2567,19 +2678,24 @@ bool afatfs_funlink(afatfsFilePtr_t file, afatfsCallback_t callback)
|
|||
static afatfsFilePtr_t afatfs_createFile(afatfsFilePtr_t file, const char *name, uint8_t attrib, uint8_t fileMode,
|
||||
afatfsFileCallback_t callback)
|
||||
{
|
||||
afatfsCreateFile_t *opState;
|
||||
afatfsCreateFile_t *opState = &file->operation.state.createFile;
|
||||
|
||||
afatfs_initFileHandle(file);
|
||||
|
||||
// Queue the operation to finish the file creation
|
||||
file->operation.operation = AFATFS_FILE_OPERATION_CREATE_FILE;
|
||||
|
||||
file->mode = fileMode;
|
||||
|
||||
if (strcmp(name, ".") == 0) {
|
||||
memcpy(&file->directoryEntry, &afatfs.currentDirectory.directoryEntry, sizeof(afatfs.currentDirectory.directoryEntry));
|
||||
|
||||
file->firstCluster = afatfs.currentDirectory.firstCluster;
|
||||
file->physicalSize = afatfs.currentDirectory.physicalSize;
|
||||
file->logicalSize = afatfs.currentDirectory.logicalSize;
|
||||
file->attrib = afatfs.currentDirectory.attrib;
|
||||
file->type = afatfs.currentDirectory.type;
|
||||
} else {
|
||||
fat_convertFilenameToFATStyle(name, (uint8_t*)file->directoryEntry.filename);
|
||||
file->directoryEntry.attrib = attrib;
|
||||
fat_convertFilenameToFATStyle(name, opState->filename);
|
||||
file->attrib = attrib;
|
||||
|
||||
if ((attrib & FAT_FILE_ATTRIBUTE_DIRECTORY) != 0) {
|
||||
file->type = AFATFS_FILE_TYPE_DIRECTORY;
|
||||
|
@ -2588,10 +2704,6 @@ static afatfsFilePtr_t afatfs_createFile(afatfsFilePtr_t file, const char *name,
|
|||
}
|
||||
}
|
||||
|
||||
// Queue the operation to finish the file creation
|
||||
file->operation.operation = AFATFS_FILE_OPERATION_CREATE_FILE;
|
||||
|
||||
opState = &file->operation.state.createFile;
|
||||
opState->callback = callback;
|
||||
|
||||
if (strcmp(name, ".") == 0) {
|
||||
|
@ -2620,7 +2732,7 @@ static void afatfs_fcloseContinue(afatfsFilePtr_t file)
|
|||
*/
|
||||
if (file->type != AFATFS_FILE_TYPE_DIRECTORY && file->type != AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY
|
||||
&& (file->mode & (AFATFS_FILE_MODE_APPEND | AFATFS_FILE_MODE_WRITE | AFATFS_FILE_MODE_CREATE)) != 0) {
|
||||
if (afatfs_saveDirectoryEntry(file) != AFATFS_OPERATION_SUCCESS) {
|
||||
if (afatfs_saveDirectoryEntry(file, AFATFS_SAVE_DIRECTORY_FOR_CLOSE) != AFATFS_OPERATION_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2667,6 +2779,8 @@ bool afatfs_fclose(afatfsFilePtr_t file, afatfsCallback_t callback)
|
|||
} else if (afatfs_fileIsBusy(file)) {
|
||||
return false;
|
||||
} else {
|
||||
afatfs_fileUpdateFilesize(file);
|
||||
|
||||
file->operation.operation = AFATFS_FILE_OPERATION_CLOSE;
|
||||
file->operation.state.closeFile.callback = callback;
|
||||
afatfs_fcloseContinue(file);
|
||||
|
@ -2720,9 +2834,8 @@ bool afatfs_chdir(afatfsFilePtr_t directory)
|
|||
else
|
||||
afatfs.currentDirectory.type = AFATFS_FILE_TYPE_DIRECTORY;
|
||||
|
||||
afatfs.currentDirectory.directoryEntry.firstClusterHigh = afatfs.rootDirectoryCluster >> 16;
|
||||
afatfs.currentDirectory.directoryEntry.firstClusterLow = afatfs.rootDirectoryCluster & 0xFFFF;
|
||||
afatfs.currentDirectory.directoryEntry.attrib = FAT_FILE_ATTRIBUTE_DIRECTORY;
|
||||
afatfs.currentDirectory.firstCluster = afatfs.rootDirectoryCluster;
|
||||
afatfs.currentDirectory.attrib = FAT_FILE_ATTRIBUTE_DIRECTORY;
|
||||
|
||||
// Root directories don't have a directory entry to represent themselves:
|
||||
afatfs.currentDirectory.directoryEntryPos.sectorNumberPhysical = 0;
|
||||
|
@ -2808,12 +2921,12 @@ void afatfs_fputc(afatfsFilePtr_t file, uint8_t c)
|
|||
|
||||
int cacheIndex = file->writeLockedCacheIndex;
|
||||
|
||||
/* If we've already locked the current sector in the cache, and we won't be completing the sector, we won't
|
||||
* be caching/uncaching/seeking, so we can just run this simpler fast case.
|
||||
*/
|
||||
if (cacheIndex != -1 && cursorOffsetInSector != AFATFS_SECTOR_SIZE - 1) {
|
||||
afatfs_cacheSectorGetMemory(cacheIndex)[cursorOffsetInSector] = c;
|
||||
file->cursorOffset++;
|
||||
|
||||
// TODO we could probably just defer this to the next seek, file close, or slow-path fwrite() call?
|
||||
file->directoryEntry.fileSize = MAX(file->directoryEntry.fileSize, file->cursorOffset);
|
||||
} else {
|
||||
// Slow path
|
||||
afatfs_fwrite(file, &c, sizeof(c));
|
||||
|
@ -2869,12 +2982,12 @@ uint32_t afatfs_fwrite(afatfsFilePtr_t file, const uint8_t *buffer, uint32_t len
|
|||
*
|
||||
* If the seek has to queue, when the seek completes, it'll update the fileSize for us to contain the cursor.
|
||||
*/
|
||||
if (afatfs_fseek(file, bytesToWriteThisSector, AFATFS_SEEK_CUR) == AFATFS_OPERATION_IN_PROGRESS) {
|
||||
if (afatfs_fseekInternal(file, bytesToWriteThisSector, NULL) == AFATFS_OPERATION_IN_PROGRESS) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ((file->mode & AFATFS_FILE_MODE_CONTIGUOUS) != 0) {
|
||||
afatfs_assert(file->cursorCluster < afatfs_firstCluster(&afatfs.freeFile));
|
||||
afatfs_assert(file->cursorCluster < afatfs.freeFile.firstCluster);
|
||||
}
|
||||
|
||||
len -= bytesToWriteThisSector;
|
||||
|
@ -2882,8 +2995,6 @@ uint32_t afatfs_fwrite(afatfsFilePtr_t file, const uint8_t *buffer, uint32_t len
|
|||
cursorOffsetInSector = 0;
|
||||
}
|
||||
|
||||
file->directoryEntry.fileSize = MAX(file->directoryEntry.fileSize, file->cursorOffset);
|
||||
|
||||
return writtenBytes;
|
||||
}
|
||||
|
||||
|
@ -2910,7 +3021,15 @@ uint32_t afatfs_fread(afatfsFilePtr_t file, uint8_t *buffer, uint32_t len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
len = MIN(file->directoryEntry.fileSize - file->cursorOffset, len);
|
||||
/*
|
||||
* If we've just previously fwritten() to extend the file, the logical filesize will be out of date and the cursor
|
||||
* will appear to be beyond the end of the file (but actually it's precisely at the end of the file, because if
|
||||
* we had seeked backwards to where we could read something with fseek(), we would have updated the filesize).
|
||||
*/
|
||||
if (file->cursorOffset >= file->logicalSize)
|
||||
return 0;
|
||||
|
||||
len = MIN(file->logicalSize - file->cursorOffset, len);
|
||||
|
||||
uint32_t readBytes = 0;
|
||||
uint32_t cursorOffsetInSector = file->cursorOffset % AFATFS_SECTOR_SIZE;
|
||||
|
@ -2936,7 +3055,7 @@ uint32_t afatfs_fread(afatfsFilePtr_t file, uint8_t *buffer, uint32_t len)
|
|||
* A seek operation should always be able to queue on the file since we have checked that the file wasn't busy
|
||||
* on entry (fseek will never return AFATFS_OPERATION_FAILURE).
|
||||
*/
|
||||
if (afatfs_fseek(file, bytesToReadThisSector, AFATFS_SEEK_CUR) == AFATFS_OPERATION_IN_PROGRESS) {
|
||||
if (afatfs_fseekInternal(file, bytesToReadThisSector, NULL) == AFATFS_OPERATION_IN_PROGRESS) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2950,7 +3069,7 @@ uint32_t afatfs_fread(afatfsFilePtr_t file, uint8_t *buffer, uint32_t len)
|
|||
|
||||
bool afatfs_feof(afatfsFilePtr_t file)
|
||||
{
|
||||
return file->cursorOffset >= file->directoryEntry.fileSize;
|
||||
return file->cursorOffset >= file->logicalSize;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2975,7 +3094,7 @@ static void afatfs_fileOperationContinue(afatfsFile_t *file)
|
|||
afatfs_funlinkContinue(file);
|
||||
break;
|
||||
case AFATFS_FILE_OPERATION_TRUNCATE:
|
||||
afatfs_ftruncateContinue(file);
|
||||
afatfs_ftruncateContinue(file, false);
|
||||
break;
|
||||
#ifdef AFATFS_USE_FREEFILE
|
||||
case AFATFS_FILE_OPERATION_APPEND_SUPERCLUSTER:
|
||||
|
@ -3018,7 +3137,7 @@ static void afatfs_fileOperationsPoll()
|
|||
*/
|
||||
uint32_t afatfs_getContiguousFreeSpace()
|
||||
{
|
||||
return afatfs.freeFile.directoryEntry.fileSize;
|
||||
return afatfs.freeFile.logicalSize;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3117,7 +3236,7 @@ static void afatfs_freeFileCreated(afatfsFile_t *file)
|
|||
{
|
||||
if (file) {
|
||||
// Did the freefile already have allocated space?
|
||||
if (file->directoryEntry.fileSize > 0) {
|
||||
if (file->logicalSize > 0) {
|
||||
afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_READY;
|
||||
} else {
|
||||
// Allocate clusters for the freefile
|
||||
|
@ -3199,10 +3318,10 @@ static void afatfs_initContinue()
|
|||
afatfs.initState.freeSpaceFAT.startCluster = startCluster;
|
||||
afatfs.initState.freeSpaceFAT.endCluster = endCluster;
|
||||
|
||||
afatfs.freeFile.directoryEntry.firstClusterHigh = startCluster >> 16;
|
||||
afatfs.freeFile.directoryEntry.firstClusterLow = startCluster & 0xFFFF;
|
||||
afatfs.freeFile.firstCluster = startCluster;
|
||||
|
||||
afatfs.freeFile.directoryEntry.fileSize = afatfs.initState.freeSpaceSearch.bestGapLength * afatfs_clusterSize();
|
||||
afatfs.freeFile.logicalSize = afatfs.initState.freeSpaceSearch.bestGapLength * afatfs_clusterSize();
|
||||
afatfs.freeFile.physicalSize = afatfs.freeFile.logicalSize;
|
||||
|
||||
// We can write the FAT table for the freefile now
|
||||
afatfs.initPhase = AFATFS_INITIALIZATION_FREEFILE_UPDATE_FAT;
|
||||
|
@ -3224,7 +3343,7 @@ static void afatfs_initContinue()
|
|||
}
|
||||
break;
|
||||
case AFATFS_INITIALIZATION_FREEFILE_SAVE_DIR_ENTRY:
|
||||
status = afatfs_saveDirectoryEntry(&afatfs.freeFile);
|
||||
status = afatfs_saveDirectoryEntry(&afatfs.freeFile, AFATFS_SAVE_DIRECTORY_NORMAL);
|
||||
|
||||
if (status == AFATFS_OPERATION_SUCCESS) {
|
||||
afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_READY;
|
||||
|
@ -3272,11 +3391,13 @@ void afatfs_init()
|
|||
|
||||
/**
|
||||
* Shut down the filesystem, flushing all data to the disk. Keep calling until it returns true.
|
||||
*
|
||||
* dirty - Set to true to skip the flush operation and terminate immediately (buffered data will be lost!)
|
||||
*/
|
||||
bool afatfs_destroy()
|
||||
bool afatfs_destroy(bool dirty)
|
||||
{
|
||||
// Only attempt detailed cleanup if the filesystem is in reasonable looking state
|
||||
if (afatfs.filesystemState == AFATFS_FILESYSTEM_STATE_READY) {
|
||||
if (!dirty && afatfs.filesystemState == AFATFS_FILESYSTEM_STATE_READY) {
|
||||
int openFileCount = 0;
|
||||
|
||||
for (int i = 0; i < AFATFS_MAX_OPEN_FILES; i++) {
|
||||
|
@ -3325,7 +3446,6 @@ bool afatfs_destroy()
|
|||
memset(&afatfs, 0, sizeof(afatfs));
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
* This file is part of Cleanflight.
|
||||
*
|
||||
* Cleanflight is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Cleanflight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
@ -57,7 +74,7 @@ void afatfs_findLast(afatfsFilePtr_t directory);
|
|||
|
||||
bool afatfs_flush();
|
||||
void afatfs_init();
|
||||
bool afatfs_destroy();
|
||||
bool afatfs_destroy(bool dirty);
|
||||
void afatfs_poll();
|
||||
|
||||
uint32_t afatfs_getFreeBufferSpace();
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
* This file is part of Cleanflight.
|
||||
*
|
||||
* Cleanflight is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Cleanflight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "fat_standard.h"
|
||||
|
|
|
@ -1,3 +1,20 @@
|
|||
/*
|
||||
* This file is part of Cleanflight.
|
||||
*
|
||||
* Cleanflight is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Cleanflight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
@ -30,6 +47,9 @@
|
|||
#define FAT_FILENAME_LENGTH 11
|
||||
#define FAT_DELETED_FILE_MARKER 0xE5
|
||||
|
||||
#define FAT_MAKE_DATE(year, month, day) (day | (month << 5) | ((year - 1980) << 9))
|
||||
#define FAT_MAKE_TIME(hour, minute, second) ((second / 2) | (minute << 5) | (hour << 11))
|
||||
|
||||
typedef enum {
|
||||
FAT_FILESYSTEM_TYPE_INVALID,
|
||||
FAT_FILESYSTEM_TYPE_FAT12,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue