2 * This is a FAT16/FAT32 filesystem for SD cards which uses asynchronous operations: The caller need never wait
3 * for the SD card to be ready.
5 * On top of the regular FAT32 concepts, we add the idea of a "super cluster". Given one FAT sector, a super cluster is
6 * the series of clusters which corresponds to all of the cluster entries in that FAT sector. If files are allocated
7 * on super-cluster boundaries, they will have FAT sectors which are dedicated to them and independent of all other
10 * We can pre-allocate a "freefile" which is a file on disk made up of contiguous superclusters. Then when we want
11 * to allocate a file on disk, we can carve it out of the freefile, and know that the clusters will be contiguous
12 * without needing to read the FAT at all (the freefile's FAT is completely determined from its start cluster and file
13 * size, which we get from the directory entry). This allows for extremely fast append-only logging.
22 #include "common/time.h"
23 #include "common/utils.h"
30 #include "asyncfatfs.h"
32 #include "fat_standard.h"
33 #include "drivers/sdcard/sdcard.h"
36 #define ONLY_EXPOSE_FOR_TESTING
38 #define ONLY_EXPOSE_FOR_TESTING static
41 #define AFATFS_NUM_CACHE_SECTORS 8
43 // FAT filesystems are allowed to differ from these parameters, but we choose not to support those weird filesystems:
44 #define AFATFS_SECTOR_SIZE 512
45 #define AFATFS_NUM_FATS 2
47 #define AFATFS_MAX_OPEN_FILES 3
49 #define AFATFS_DEFAULT_FILE_DATE FAT_MAKE_DATE(2015, 12, 01)
50 #define AFATFS_DEFAULT_FILE_TIME FAT_MAKE_TIME(00, 00, 00)
53 * How many blocks will we write in a row before we bother using the SDcard's multiple block write method?
54 * If this define is omitted, this disables multi-block write.
56 #define AFATFS_MIN_MULTIPLE_BLOCK_WRITE_COUNT 4
58 #define AFATFS_FILES_PER_DIRECTORY_SECTOR (AFATFS_SECTOR_SIZE / sizeof(fatDirectoryEntry_t))
60 #define AFATFS_FAT32_FAT_ENTRIES_PER_SECTOR (AFATFS_SECTOR_SIZE / sizeof(uint32_t))
61 #define AFATFS_FAT16_FAT_ENTRIES_PER_SECTOR (AFATFS_SECTOR_SIZE / sizeof(uint16_t))
63 // We will read from the file
64 #define AFATFS_FILE_MODE_READ 1
65 // We will write to the file
66 #define AFATFS_FILE_MODE_WRITE 2
67 // We will append to the file, may not be combined with the write flag
68 #define AFATFS_FILE_MODE_APPEND 4
69 // File will occupy a series of superclusters (only valid for creating new files):
70 #define AFATFS_FILE_MODE_CONTIGUOUS 8
71 // File should be created if it doesn't exist:
72 #define AFATFS_FILE_MODE_CREATE 16
73 // The file's directory entry should be locked in cache so we can read it with no latency:
74 #define AFATFS_FILE_MODE_RETAIN_DIRECTORY 32
76 // Open the cache sector for read access (it will be read from disk)
77 #define AFATFS_CACHE_READ 1
78 // Open the cache sector for write access (it will be marked dirty)
79 #define AFATFS_CACHE_WRITE 2
80 // Lock this sector to prevent its state from transitioning (prevent flushes to disk)
81 #define AFATFS_CACHE_LOCK 4
82 // Discard this sector in preference to other sectors when it is in the in-sync state
83 #define AFATFS_CACHE_DISCARDABLE 8
84 // Increase the retain counter of the cache sector to prevent it from being discarded when in the in-sync state
85 #define AFATFS_CACHE_RETAIN 16
87 // Turn the largest free block on the disk into one contiguous file for efficient fragment-free allocation
88 #define AFATFS_USE_FREEFILE
90 // When allocating a freefile, leave this many clusters un-allocated for regular files to use
91 #define AFATFS_FREEFILE_LEAVE_CLUSTERS 100
93 // Filename in 8.3 format:
94 #define AFATFS_FREESPACE_FILENAME "FREESPAC.E"
96 #define AFATFS_INTROSPEC_LOG_FILENAME "ASYNCFAT.LOG"
98 #define MIN(a, b) ((a) < (b) ? (a) : (b))
99 #define MAX(a, b) ((a) > (b) ? (a) : (b))
102 AFATFS_SAVE_DIRECTORY_NORMAL
,
103 AFATFS_SAVE_DIRECTORY_FOR_CLOSE
,
104 AFATFS_SAVE_DIRECTORY_DELETED
,
105 } afatfsSaveDirectoryEntryMode_e
;
108 AFATFS_CACHE_STATE_EMPTY
,
109 AFATFS_CACHE_STATE_IN_SYNC
,
110 AFATFS_CACHE_STATE_READING
,
111 AFATFS_CACHE_STATE_WRITING
,
112 AFATFS_CACHE_STATE_DIRTY
113 } afatfsCacheBlockState_e
;
116 AFATFS_FILE_TYPE_NONE
,
117 AFATFS_FILE_TYPE_NORMAL
,
118 AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY
,
119 AFATFS_FILE_TYPE_DIRECTORY
123 CLUSTER_SEARCH_FREE_AT_BEGINNING_OF_FAT_SECTOR
,
125 CLUSTER_SEARCH_OCCUPIED
,
126 } afatfsClusterSearchCondition_e
;
129 AFATFS_CREATEFILE_PHASE_INITIAL
= 0,
130 AFATFS_CREATEFILE_PHASE_FIND_FILE
,
131 AFATFS_CREATEFILE_PHASE_CREATE_NEW_FILE
,
132 AFATFS_CREATEFILE_PHASE_SUCCESS
,
133 AFATFS_CREATEFILE_PHASE_FAILURE
,
137 AFATFS_FIND_CLUSTER_IN_PROGRESS
,
138 AFATFS_FIND_CLUSTER_FOUND
,
139 AFATFS_FIND_CLUSTER_FATAL
,
140 AFATFS_FIND_CLUSTER_NOT_FOUND
,
141 } afatfsFindClusterStatus_e
;
143 struct afatfsFileOperation_t
;
145 typedef union afatfsFATSector_t
{
151 typedef struct afatfsCacheBlockDescriptor_t
{
153 * The physical sector index on disk that this cached block corresponds to
155 uint32_t sectorIndex
;
157 // We use an increasing timestamp to identify cache access times.
159 // This is the timestamp that this sector was first marked dirty at (so we can flush sectors in write-order).
160 uint32_t writeTimestamp
;
162 // This is the last time the sector was accessed
163 uint32_t accessTimestamp
;
165 /* This is set to non-zero when we expect to write a consecutive series of this many blocks (including this block),
166 * so we will tell the SD-card to pre-erase those blocks.
168 * This counter only needs to be set on the first block of a consecutive write (though setting it, appropriately
169 * decreased, on the subsequent blocks won't hurt).
171 uint16_t consecutiveEraseBlockCount
;
173 afatfsCacheBlockState_e state
;
176 * The state of this block must not transition (do not flush to disk, do not discard). This is useful for a sector
177 * which is currently being written to by the application (so flushing it would be a waste of time).
179 * This is a binary state rather than a counter because we assume that only one party will be responsible for and
180 * so consider locking a given sector.
185 * A counter for how many parties want this sector to be retained in memory (not discarded). If this value is
186 * non-zero, the sector may be flushed to disk if dirty but must remain in the cache. This is useful if we require
187 * a directory sector to be cached in order to meet our response time requirements.
189 unsigned retainCount
:6;
192 * If this block is in the In Sync state, it should be discarded from the cache in preference to other blocks.
193 * This is useful for data that we don't expect to read again, e.g. data written to an append-only file. This hint
194 * is overridden by the locked and retainCount flags.
196 unsigned discardable
:1;
197 } afatfsCacheBlockDescriptor_t
;
200 AFATFS_FAT_PATTERN_UNTERMINATED_CHAIN
,
201 AFATFS_FAT_PATTERN_TERMINATED_CHAIN
,
202 AFATFS_FAT_PATTERN_FREE
203 } afatfsFATPattern_e
;
206 AFATFS_FREE_SPACE_SEARCH_PHASE_FIND_HOLE
,
207 AFATFS_FREE_SPACE_SEARCH_PHASE_GROW_HOLE
208 } afatfsFreeSpaceSearchPhase_e
;
210 typedef struct afatfsFreeSpaceSearch_t
{
211 uint32_t candidateStart
;
212 uint32_t candidateEnd
;
213 uint32_t bestGapStart
;
214 uint32_t bestGapLength
;
215 afatfsFreeSpaceSearchPhase_e phase
;
216 } afatfsFreeSpaceSearch_t
;
218 typedef struct afatfsFreeSpaceFAT_t
{
219 uint32_t startCluster
;
221 } afatfsFreeSpaceFAT_t
;
223 typedef struct afatfsCreateFile_t
{
224 afatfsFileCallback_t callback
;
227 uint8_t filename
[FAT_FILENAME_LENGTH
];
228 } afatfsCreateFile_t
;
230 typedef struct afatfsSeek_t
{
231 afatfsFileCallback_t callback
;
237 AFATFS_APPEND_SUPERCLUSTER_PHASE_INIT
= 0,
238 AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FREEFILE_DIRECTORY
,
239 AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FAT
,
240 AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FILE_DIRECTORY
,
241 } afatfsAppendSuperclusterPhase_e
;
243 typedef struct afatfsAppendSupercluster_t
{
244 uint32_t previousCluster
;
245 uint32_t fatRewriteStartCluster
;
246 uint32_t fatRewriteEndCluster
;
247 afatfsAppendSuperclusterPhase_e phase
;
248 } afatfsAppendSupercluster_t
;
251 AFATFS_APPEND_FREE_CLUSTER_PHASE_INITIAL
= 0,
252 AFATFS_APPEND_FREE_CLUSTER_PHASE_FIND_FREESPACE
= 0,
253 AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FAT1
,
254 AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FAT2
,
255 AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FILE_DIRECTORY
,
256 AFATFS_APPEND_FREE_CLUSTER_PHASE_COMPLETE
,
257 AFATFS_APPEND_FREE_CLUSTER_PHASE_FAILURE
,
258 } afatfsAppendFreeClusterPhase_e
;
260 typedef struct afatfsAppendFreeCluster_t
{
261 uint32_t previousCluster
;
262 uint32_t searchCluster
;
263 afatfsAppendFreeClusterPhase_e phase
;
264 } afatfsAppendFreeCluster_t
;
267 AFATFS_EXTEND_SUBDIRECTORY_PHASE_INITIAL
= 0,
268 AFATFS_EXTEND_SUBDIRECTORY_PHASE_ADD_FREE_CLUSTER
= 0,
269 AFATFS_EXTEND_SUBDIRECTORY_PHASE_WRITE_SECTORS
,
270 AFATFS_EXTEND_SUBDIRECTORY_PHASE_SUCCESS
,
271 AFATFS_EXTEND_SUBDIRECTORY_PHASE_FAILURE
272 } afatfsExtendSubdirectoryPhase_e
;
274 typedef struct afatfsExtendSubdirectory_t
{
275 // We need to call this as a sub-operation so we have it as our first member to be compatible with its memory layout:
276 afatfsAppendFreeCluster_t appendFreeCluster
;
278 afatfsExtendSubdirectoryPhase_e phase
;
280 uint32_t parentDirectoryCluster
;
281 afatfsFileCallback_t callback
;
282 } afatfsExtendSubdirectory_t
;
285 AFATFS_TRUNCATE_FILE_INITIAL
= 0,
286 AFATFS_TRUNCATE_FILE_UPDATE_DIRECTORY
= 0,
287 AFATFS_TRUNCATE_FILE_ERASE_FAT_CHAIN_NORMAL
,
288 #ifdef AFATFS_USE_FREEFILE
289 AFATFS_TRUNCATE_FILE_ERASE_FAT_CHAIN_CONTIGUOUS
,
290 AFATFS_TRUNCATE_FILE_PREPEND_TO_FREEFILE
,
292 AFATFS_TRUNCATE_FILE_SUCCESS
,
293 } afatfsTruncateFilePhase_e
;
295 typedef struct afatfsTruncateFile_t
{
296 uint32_t startCluster
; // First cluster to erase
297 uint32_t currentCluster
; // Used to mark progress
298 uint32_t endCluster
; // Optional, for contiguous files set to 1 past the end cluster of the file, otherwise set to 0
299 afatfsFileCallback_t callback
;
300 afatfsTruncateFilePhase_e phase
;
301 } afatfsTruncateFile_t
;
304 AFATFS_DELETE_FILE_DELETE_DIRECTORY_ENTRY
,
305 AFATFS_DELETE_FILE_DEALLOCATE_CLUSTERS
,
306 } afatfsDeleteFilePhase_e
;
308 typedef struct afatfsDeleteFile_t
{
309 afatfsTruncateFile_t truncateFile
;
310 afatfsCallback_t callback
;
311 } afatfsUnlinkFile_t
;
313 typedef struct afatfsCloseFile_t
{
314 afatfsCallback_t callback
;
318 AFATFS_FILE_OPERATION_NONE
,
319 AFATFS_FILE_OPERATION_CREATE_FILE
,
320 AFATFS_FILE_OPERATION_SEEK
, // Seek the file's cursorCluster forwards by seekOffset bytes
321 AFATFS_FILE_OPERATION_CLOSE
,
322 AFATFS_FILE_OPERATION_TRUNCATE
,
323 AFATFS_FILE_OPERATION_UNLINK
,
324 #ifdef AFATFS_USE_FREEFILE
325 AFATFS_FILE_OPERATION_APPEND_SUPERCLUSTER
,
326 AFATFS_FILE_OPERATION_LOCKED
,
328 AFATFS_FILE_OPERATION_APPEND_FREE_CLUSTER
,
329 AFATFS_FILE_OPERATION_EXTEND_SUBDIRECTORY
,
330 } afatfsFileOperation_e
;
332 typedef struct afatfsFileOperation_t
{
333 afatfsFileOperation_e operation
;
335 afatfsCreateFile_t createFile
;
337 afatfsAppendSupercluster_t appendSupercluster
;
338 afatfsAppendFreeCluster_t appendFreeCluster
;
339 afatfsExtendSubdirectory_t extendSubdirectory
;
340 afatfsUnlinkFile_t unlinkFile
;
341 afatfsTruncateFile_t truncateFile
;
342 afatfsCloseFile_t closeFile
;
344 } afatfsFileOperation_t
;
346 typedef struct afatfsFile_t
{
347 afatfsFileType_e type
;
349 // The byte offset of the cursor within the file
350 uint32_t cursorOffset
;
352 /* The file size in bytes as seen by users of the filesystem (the exact length of the file they've written).
354 * This is only used by users of the filesystem, not us, so it only needs to be up to date for fseek() (to clip
355 * seeks to the EOF), fread(), feof(), and fclose() (which writes the logicalSize to the directory).
357 * It becomes out of date when we fwrite() to extend the length of the file. In this situation, feof() is properly
358 * true, so we don't have to update the logicalSize for fread() or feof() to get the correct result. We only need
359 * to update it when we seek backwards (so we don't forget the logical EOF position), or fclose().
361 uint32_t logicalSize
;
363 /* The allocated size in bytes based on how many clusters have been assigned to the file. Always a multiple of
366 * This is an underestimate for existing files, because we don't bother to check precisely how long the chain is
367 * at the time the file is opened (it might be longer than needed to contain the logical size), but assuming the
368 * filesystem metadata is correct, it should always be at least as many clusters as needed to contain logicalSize.
370 * Since this is an estimate, we only use it to exaggerate the filesize in the directory entry of a file that is
371 * currently being written (so that the final cluster of the file will be entirely readable if power is lost before
372 * we can could update the directory entry with a new logicalSize).
374 uint32_t physicalSize
;
377 * The cluster that the file pointer is currently within. When seeking to the end of the file, this will be
380 uint32_t cursorCluster
;
383 * The cluster before the one the file pointer is inside. This is set to zero when at the start of the file.
385 uint32_t cursorPreviousCluster
;
387 uint8_t mode
; // A combination of AFATFS_FILE_MODE_* flags
388 uint8_t attrib
; // Combination of FAT_FILE_ATTRIBUTE_* flags for the directory entry of this file
390 /* We hold on to one sector entry in the cache and remember its index here. The cache is invalidated when we
391 * seek across a sector boundary. This allows fwrite() to complete faster because it doesn't need to check the
392 * cache on every call.
394 int8_t writeLockedCacheIndex
;
395 // Ditto for fread():
396 int8_t readRetainCacheIndex
;
398 // The position of our directory entry on the disk (so we can update it without consulting a parent directory file)
399 afatfsDirEntryPointer_t directoryEntryPos
;
401 // The first cluster number of the file, or 0 if this file is empty
402 uint32_t firstCluster
;
404 // State for a queued operation on the file
405 struct afatfsFileOperation_t operation
;
409 AFATFS_INITIALIZATION_READ_MBR
,
410 AFATFS_INITIALIZATION_READ_VOLUME_ID
,
412 #ifdef AFATFS_USE_FREEFILE
413 AFATFS_INITIALIZATION_FREEFILE_CREATE
,
414 AFATFS_INITIALIZATION_FREEFILE_CREATING
,
415 AFATFS_INITIALIZATION_FREEFILE_FAT_SEARCH
,
416 AFATFS_INITIALIZATION_FREEFILE_UPDATE_FAT
,
417 AFATFS_INITIALIZATION_FREEFILE_SAVE_DIR_ENTRY
,
418 AFATFS_INITIALIZATION_FREEFILE_LAST
= AFATFS_INITIALIZATION_FREEFILE_SAVE_DIR_ENTRY
,
421 AFATFS_INITIALIZATION_DONE
422 } afatfsInitializationPhase_e
;
424 typedef struct afatfs_t
{
425 fatFilesystemType_e filesystemType
;
427 afatfsFilesystemState_e filesystemState
;
428 afatfsInitializationPhase_e initPhase
;
430 // State used during FS initialisation where only one member of the union is used at a time
431 #ifdef AFATFS_USE_FREEFILE
433 afatfsFreeSpaceSearch_t freeSpaceSearch
;
434 afatfsFreeSpaceFAT_t freeSpaceFAT
;
441 uint8_t cache
[AFATFS_SECTOR_SIZE
* AFATFS_NUM_CACHE_SECTORS
];
443 afatfsCacheBlockDescriptor_t cacheDescriptor
[AFATFS_NUM_CACHE_SECTORS
];
446 int cacheDirtyEntries
; // The number of cache entries in the AFATFS_CACHE_STATE_DIRTY state
447 bool cacheFlushInProgress
;
449 afatfsFile_t openFiles
[AFATFS_MAX_OPEN_FILES
];
451 #ifdef AFATFS_USE_FREEFILE
452 afatfsFile_t freeFile
;
455 afatfsError_e lastError
;
459 // The current working directory:
460 afatfsFile_t currentDirectory
;
462 uint32_t partitionStartSector
; // The physical sector that the first partition on the device begins at
464 uint32_t fatStartSector
; // The first sector of the first FAT
465 uint32_t fatSectors
; // The size in sectors of a single FAT
468 * Number of clusters available for storing user data. Note that clusters are numbered starting from 2, so the
469 * index of the last cluster on the volume is numClusters + 1 !!!
471 uint32_t numClusters
;
472 uint32_t clusterStartSector
; // The physical sector that the clusters area begins at
473 uint32_t sectorsPerCluster
;
476 * Number of the cluster we last allocated (i.e. free->occupied). Searches for a free cluster will begin after this
479 uint32_t lastClusterAllocated
;
481 /* Mask to be ANDed with a byte offset within a file to give the offset within the cluster */
482 uint32_t byteInClusterMask
;
484 uint32_t rootDirectoryCluster
; // Present on FAT32 and set to zero for FAT16
485 uint32_t rootDirectorySectors
; // Zero on FAT32, for FAT16 the number of sectors that the root directory occupies
489 static uint8_t afatfs_cache
[AFATFS_SECTOR_SIZE
* AFATFS_NUM_CACHE_SECTORS
] __attribute__((aligned(32)));
492 static afatfs_t afatfs
;
494 static void afatfs_fileOperationContinue(afatfsFile_t
*file
);
495 static uint8_t* afatfs_fileLockCursorSectorForWrite(afatfsFilePtr_t file
);
496 static uint8_t* afatfs_fileRetainCursorSectorForRead(afatfsFilePtr_t file
);
498 static uint32_t roundUpTo(uint32_t value
, uint32_t rounding
)
500 uint32_t remainder
= value
% rounding
;
503 value
+= rounding
- remainder
;
509 static bool isPowerOfTwo(unsigned int x
)
511 return ((x
!= 0) && ((x
& (~x
+ 1)) == x
));
515 * Check for conditions that should always be true (and if otherwise mean a bug or a corrupt filesystem).
517 * If the condition is false, the filesystem is marked as being in a fatal state.
519 * Returns the value of the condition.
521 static bool afatfs_assert(bool condition
)
524 if (afatfs
.lastError
== AFATFS_ERROR_NONE
) {
525 afatfs
.lastError
= AFATFS_ERROR_GENERIC
;
527 afatfs
.filesystemState
= AFATFS_FILESYSTEM_STATE_FATAL
;
536 static bool afatfs_fileIsBusy(afatfsFilePtr_t file
)
538 return file
->operation
.operation
!= AFATFS_FILE_OPERATION_NONE
;
542 * The number of FAT table entries that fit within one AFATFS sector size.
544 * Note that this is the same as the number of clusters in an AFATFS supercluster.
546 static uint32_t afatfs_fatEntriesPerSector(void)
548 return afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT32
? AFATFS_FAT32_FAT_ENTRIES_PER_SECTOR
: AFATFS_FAT16_FAT_ENTRIES_PER_SECTOR
;
552 * Size of a FAT cluster in bytes
554 ONLY_EXPOSE_FOR_TESTING
555 uint32_t afatfs_clusterSize(void)
557 return afatfs
.sectorsPerCluster
* AFATFS_SECTOR_SIZE
;
561 * Given a byte offset within a file, return the byte offset of that position within the cluster it belongs to.
563 static uint32_t afatfs_byteIndexInCluster(uint32_t byteOffset
)
565 return afatfs
.byteInClusterMask
& byteOffset
;
569 * Given a byte offset within a file, return the index of the sector within the cluster it belongs to.
571 static uint32_t afatfs_sectorIndexInCluster(uint32_t byteOffset
)
573 return afatfs_byteIndexInCluster(byteOffset
) / AFATFS_SECTOR_SIZE
;
576 // Get the buffer memory for the cache entry of the given index.
577 static uint8_t *afatfs_cacheSectorGetMemory(int cacheEntryIndex
)
579 return afatfs
.cache
+ cacheEntryIndex
* AFATFS_SECTOR_SIZE
;
582 static int afatfs_getCacheDescriptorIndexForBuffer(uint8_t *memory
)
584 int index
= (memory
- afatfs
.cache
) / AFATFS_SECTOR_SIZE
;
586 if (afatfs_assert(index
>= 0 && index
< AFATFS_NUM_CACHE_SECTORS
)) {
593 static afatfsCacheBlockDescriptor_t
* afatfs_getCacheDescriptorForBuffer(uint8_t *memory
)
595 return afatfs
.cacheDescriptor
+ afatfs_getCacheDescriptorIndexForBuffer(memory
);
598 static void afatfs_cacheSectorMarkDirty(afatfsCacheBlockDescriptor_t
*descriptor
)
600 if (descriptor
->state
!= AFATFS_CACHE_STATE_DIRTY
) {
601 descriptor
->writeTimestamp
= ++afatfs
.cacheTimer
;
602 descriptor
->state
= AFATFS_CACHE_STATE_DIRTY
;
603 afatfs
.cacheDirtyEntries
++;
607 static void afatfs_cacheSectorInit(afatfsCacheBlockDescriptor_t
*descriptor
, uint32_t sectorIndex
, bool locked
)
609 descriptor
->sectorIndex
= sectorIndex
;
611 descriptor
->accessTimestamp
= descriptor
->writeTimestamp
= ++afatfs
.cacheTimer
;
613 descriptor
->consecutiveEraseBlockCount
= 0;
615 descriptor
->state
= AFATFS_CACHE_STATE_EMPTY
;
617 descriptor
->locked
= locked
;
618 descriptor
->retainCount
= 0;
619 descriptor
->discardable
= 0;
623 * Called by the SD card driver when one of our read operations completes.
625 static void afatfs_sdcardReadComplete(sdcardBlockOperation_e operation
, uint32_t sectorIndex
, uint8_t *buffer
, uint32_t callbackData
)
630 for (int i
= 0; i
< AFATFS_NUM_CACHE_SECTORS
; i
++) {
631 if (afatfs
.cacheDescriptor
[i
].state
!= AFATFS_CACHE_STATE_EMPTY
632 && afatfs
.cacheDescriptor
[i
].sectorIndex
== sectorIndex
634 if (buffer
== NULL
) {
635 // Read failed, mark the sector as empty and whoever asked for it will ask for it again later to retry
636 afatfs
.cacheDescriptor
[i
].state
= AFATFS_CACHE_STATE_EMPTY
;
638 afatfs_assert(afatfs_cacheSectorGetMemory(i
) == buffer
&& afatfs
.cacheDescriptor
[i
].state
== AFATFS_CACHE_STATE_READING
);
640 afatfs
.cacheDescriptor
[i
].state
= AFATFS_CACHE_STATE_IN_SYNC
;
649 * Called by the SD card driver when one of our write operations completes.
651 static void afatfs_sdcardWriteComplete(sdcardBlockOperation_e operation
, uint32_t sectorIndex
, uint8_t *buffer
, uint32_t callbackData
)
656 afatfs
.cacheFlushInProgress
= false;
658 for (int i
= 0; i
< AFATFS_NUM_CACHE_SECTORS
; i
++) {
659 /* Keep in mind that someone may have marked the sector as dirty after writing had already begun. In this case we must leave
660 * it marked as dirty because those modifications may have been made too late to make it to the disk!
662 if (afatfs
.cacheDescriptor
[i
].sectorIndex
== sectorIndex
663 && afatfs
.cacheDescriptor
[i
].state
== AFATFS_CACHE_STATE_WRITING
665 if (buffer
== NULL
) {
666 // Write failed, remark the sector as dirty
667 afatfs
.cacheDescriptor
[i
].state
= AFATFS_CACHE_STATE_DIRTY
;
668 afatfs
.cacheDirtyEntries
++;
670 afatfs_assert(afatfs_cacheSectorGetMemory(i
) == buffer
);
672 afatfs
.cacheDescriptor
[i
].state
= AFATFS_CACHE_STATE_IN_SYNC
;
680 * Attempt to flush the dirty cache entry with the given index to the SDcard.
682 static void afatfs_cacheFlushSector(int cacheIndex
)
684 afatfsCacheBlockDescriptor_t
*cacheDescriptor
= &afatfs
.cacheDescriptor
[cacheIndex
];
686 #ifdef AFATFS_MIN_MULTIPLE_BLOCK_WRITE_COUNT
687 if (cacheDescriptor
->consecutiveEraseBlockCount
) {
688 sdcard_beginWriteBlocks(cacheDescriptor
->sectorIndex
, cacheDescriptor
->consecutiveEraseBlockCount
);
692 switch (sdcard_writeBlock(cacheDescriptor
->sectorIndex
, afatfs_cacheSectorGetMemory(cacheIndex
), afatfs_sdcardWriteComplete
, 0)) {
693 case SDCARD_OPERATION_IN_PROGRESS
:
694 // The card will call us back later when the buffer transmission finishes
695 afatfs
.cacheDirtyEntries
--;
696 cacheDescriptor
->state
= AFATFS_CACHE_STATE_WRITING
;
697 afatfs
.cacheFlushInProgress
= true;
700 case SDCARD_OPERATION_SUCCESS
:
701 // Buffer is already transmitted
702 afatfs
.cacheDirtyEntries
--;
703 cacheDescriptor
->state
= AFATFS_CACHE_STATE_IN_SYNC
;
706 case SDCARD_OPERATION_BUSY
:
707 case SDCARD_OPERATION_FAILURE
:
714 * Find a sector in the cache which corresponds to the given physical sector index, or NULL if the sector isn't
715 * cached. Note that the cached sector could be in any state including completely empty.
717 static afatfsCacheBlockDescriptor_t
* afatfs_findCacheSector(uint32_t sectorIndex
)
719 for (int i
= 0; i
< AFATFS_NUM_CACHE_SECTORS
; i
++) {
720 if (afatfs
.cacheDescriptor
[i
].sectorIndex
== sectorIndex
) {
721 return &afatfs
.cacheDescriptor
[i
];
729 * Find or allocate a cache sector for the given sector index on disk. Returns a block which matches one of these
730 * conditions (in descending order of preference):
732 * - The requested sector that already exists in the cache
733 * - The index of an empty sector
734 * - The index of a synced discardable sector
735 * - The index of the oldest synced sector
737 * Otherwise it returns -1 to signal failure (cache is full!)
739 static int afatfs_allocateCacheSector(uint32_t sectorIndex
)
742 int emptyIndex
= -1, discardableIndex
= -1;
744 uint32_t oldestSyncedSectorLastUse
= 0xFFFFFFFF;
745 int oldestSyncedSectorIndex
= -1;
749 afatfs
.numClusters
== 0 // We're unable to check sector bounds during startup since we haven't read volume label yet
750 || sectorIndex
< afatfs
.clusterStartSector
+ afatfs
.numClusters
* afatfs
.sectorsPerCluster
756 for (int i
= 0; i
< AFATFS_NUM_CACHE_SECTORS
; i
++) {
757 if (afatfs
.cacheDescriptor
[i
].sectorIndex
== sectorIndex
) {
759 * If the sector is actually empty then do a complete re-init of it just like the standard
760 * empty case. (Sectors marked as empty should be treated as if they don't have a block index assigned)
762 if (afatfs
.cacheDescriptor
[i
].state
== AFATFS_CACHE_STATE_EMPTY
) {
767 // Bump the last access time
768 afatfs
.cacheDescriptor
[i
].accessTimestamp
= ++afatfs
.cacheTimer
;
772 switch (afatfs
.cacheDescriptor
[i
].state
) {
773 case AFATFS_CACHE_STATE_EMPTY
:
776 case AFATFS_CACHE_STATE_IN_SYNC
:
777 // Is this a synced sector that we could evict from the cache?
778 if (!afatfs
.cacheDescriptor
[i
].locked
&& afatfs
.cacheDescriptor
[i
].retainCount
== 0) {
779 if (afatfs
.cacheDescriptor
[i
].discardable
) {
780 discardableIndex
= i
;
781 } else if (afatfs
.cacheDescriptor
[i
].accessTimestamp
< oldestSyncedSectorLastUse
) {
782 // This is older than last block we decided to evict, so evict this one in preference
783 oldestSyncedSectorLastUse
= afatfs
.cacheDescriptor
[i
].accessTimestamp
;
784 oldestSyncedSectorIndex
= i
;
793 if (emptyIndex
> -1) {
794 allocateIndex
= emptyIndex
;
795 } else if (discardableIndex
> -1) {
796 allocateIndex
= discardableIndex
;
797 } else if (oldestSyncedSectorIndex
> -1) {
798 allocateIndex
= oldestSyncedSectorIndex
;
803 if (allocateIndex
> -1) {
804 afatfs_cacheSectorInit(&afatfs
.cacheDescriptor
[allocateIndex
], sectorIndex
, false);
807 return allocateIndex
;
811 * Attempt to flush dirty cache pages out to the sdcard, returning true if all flushable data has been flushed.
813 bool afatfs_flush(void)
815 if (afatfs
.cacheDirtyEntries
> 0) {
816 // Flush the oldest flushable sector
817 uint32_t earliestSectorTime
= 0xFFFFFFFF;
818 int earliestSectorIndex
= -1;
820 for (int i
= 0; i
< AFATFS_NUM_CACHE_SECTORS
; i
++) {
821 if (afatfs
.cacheDescriptor
[i
].state
== AFATFS_CACHE_STATE_DIRTY
&& !afatfs
.cacheDescriptor
[i
].locked
822 && (earliestSectorIndex
== -1 || afatfs
.cacheDescriptor
[i
].writeTimestamp
< earliestSectorTime
)
824 earliestSectorIndex
= i
;
825 earliestSectorTime
= afatfs
.cacheDescriptor
[i
].writeTimestamp
;
829 if (earliestSectorIndex
> -1) {
830 afatfs_cacheFlushSector(earliestSectorIndex
);
832 // That flush will take time to complete so we may as well tell caller to come back later
841 * Returns true if either the freefile or the regular cluster pool has been exhausted during a previous write operation.
843 bool afatfs_isFull(void)
845 return afatfs
.filesystemFull
;
849 * Get the physical sector number that corresponds to the FAT sector of the given fatSectorIndex within the given
850 * FAT (fatIndex may be 0 or 1). (0, 0) gives the first sector of the first FAT.
852 static uint32_t afatfs_fatSectorToPhysical(int fatIndex
, uint32_t fatSectorIndex
)
854 return afatfs
.fatStartSector
+ (fatIndex
? afatfs
.fatSectors
: 0) + fatSectorIndex
;
857 static uint32_t afatfs_fileClusterToPhysical(uint32_t clusterNumber
, uint32_t sectorIndex
)
859 return afatfs
.clusterStartSector
+ (clusterNumber
- 2) * afatfs
.sectorsPerCluster
+ sectorIndex
;
862 static uint32_t afatfs_fileGetCursorPhysicalSector(afatfsFilePtr_t file
)
864 if (file
->type
== AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY
) {
865 return afatfs
.fatStartSector
+ AFATFS_NUM_FATS
* afatfs
.fatSectors
+ file
->cursorOffset
/ AFATFS_SECTOR_SIZE
;
867 uint32_t cursorSectorInCluster
= afatfs_sectorIndexInCluster(file
->cursorOffset
);
868 return afatfs_fileClusterToPhysical(file
->cursorCluster
, cursorSectorInCluster
);
873 * Sector here is the sector index within the cluster.
875 static void afatfs_fileGetCursorClusterAndSector(afatfsFilePtr_t file
, uint32_t *cluster
, uint16_t *sector
)
877 *cluster
= file
->cursorCluster
;
879 if (file
->type
== AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY
) {
880 *sector
= file
->cursorOffset
/ AFATFS_SECTOR_SIZE
;
882 *sector
= afatfs_sectorIndexInCluster(file
->cursorOffset
);
887 * Get a cache entry for the given sector and store a pointer to the cached memory in *buffer.
889 * physicalSectorIndex - The index of the sector in the SD card to cache
890 * sectorflags - A union of AFATFS_CACHE_* constants that says which operations the sector will be cached for.
891 * buffer - A pointer to the 512-byte memory buffer for the sector will be stored here upon success
892 * eraseCount - For write operations, set to a non-zero number to hint that we plan to write that many sectors
893 * consecutively (including this sector)
896 * AFATFS_OPERATION_SUCCESS - On success
897 * AFATFS_OPERATION_IN_PROGRESS - Card is busy, call again later
898 * AFATFS_OPERATION_FAILURE - When the filesystem encounters a fatal error
900 static afatfsOperationStatus_e
afatfs_cacheSector(uint32_t physicalSectorIndex
, uint8_t **buffer
, uint8_t sectorFlags
, uint32_t eraseCount
)
902 // We never write to the MBR, so any attempt to write there is an asyncfatfs bug
903 if (!afatfs_assert((sectorFlags
& AFATFS_CACHE_WRITE
) == 0 || physicalSectorIndex
!= 0)) {
904 return AFATFS_OPERATION_FAILURE
;
907 int cacheSectorIndex
= afatfs_allocateCacheSector(physicalSectorIndex
);
909 if (cacheSectorIndex
== -1) {
910 // We don't have enough free cache to service this request right now, try again later
911 return AFATFS_OPERATION_IN_PROGRESS
;
914 switch (afatfs
.cacheDescriptor
[cacheSectorIndex
].state
) {
915 case AFATFS_CACHE_STATE_READING
:
916 return AFATFS_OPERATION_IN_PROGRESS
;
919 case AFATFS_CACHE_STATE_EMPTY
:
920 if ((sectorFlags
& AFATFS_CACHE_READ
) != 0) {
921 if (sdcard_readBlock(physicalSectorIndex
, afatfs_cacheSectorGetMemory(cacheSectorIndex
), afatfs_sdcardReadComplete
, 0)) {
922 afatfs
.cacheDescriptor
[cacheSectorIndex
].state
= AFATFS_CACHE_STATE_READING
;
924 return AFATFS_OPERATION_IN_PROGRESS
;
927 // We only get to decide these fields if we're the first ones to cache the sector:
928 afatfs
.cacheDescriptor
[cacheSectorIndex
].discardable
= (sectorFlags
& AFATFS_CACHE_DISCARDABLE
) != 0 ? 1 : 0;
930 #ifdef AFATFS_MIN_MULTIPLE_BLOCK_WRITE_COUNT
931 // Don't bother pre-erasing for small block sequences
932 if (eraseCount
< AFATFS_MIN_MULTIPLE_BLOCK_WRITE_COUNT
) {
935 eraseCount
= MIN(eraseCount
, UINT16_MAX
); // If caller asked for a longer chain of sectors we silently truncate that here
938 afatfs
.cacheDescriptor
[cacheSectorIndex
].consecutiveEraseBlockCount
= eraseCount
;
942 case AFATFS_CACHE_STATE_WRITING
:
943 case AFATFS_CACHE_STATE_IN_SYNC
:
944 if ((sectorFlags
& AFATFS_CACHE_WRITE
) != 0) {
945 afatfs_cacheSectorMarkDirty(&afatfs
.cacheDescriptor
[cacheSectorIndex
]);
949 case AFATFS_CACHE_STATE_DIRTY
:
950 if ((sectorFlags
& AFATFS_CACHE_LOCK
) != 0) {
951 afatfs
.cacheDescriptor
[cacheSectorIndex
].locked
= 1;
953 if ((sectorFlags
& AFATFS_CACHE_RETAIN
) != 0) {
954 afatfs
.cacheDescriptor
[cacheSectorIndex
].retainCount
++;
957 *buffer
= afatfs_cacheSectorGetMemory(cacheSectorIndex
);
959 return AFATFS_OPERATION_SUCCESS
;
963 // Cache block in unknown state, should never happen
964 afatfs_assert(false);
965 return AFATFS_OPERATION_FAILURE
;
970 * Parse the details out of the given MBR sector (512 bytes long). Return true if a compatible filesystem was found.
972 static bool afatfs_parseMBR(const uint8_t *sector
)
974 // Check MBR signature
975 if (sector
[AFATFS_SECTOR_SIZE
- 2] != 0x55 || sector
[AFATFS_SECTOR_SIZE
- 1] != 0xAA)
978 mbrPartitionEntry_t
*partition
= (mbrPartitionEntry_t
*) (sector
+ 446);
980 for (int i
= 0; i
< 4; i
++) {
982 partition
[i
].lbaBegin
> 0
984 partition
[i
].type
== MBR_PARTITION_TYPE_FAT32
985 || partition
[i
].type
== MBR_PARTITION_TYPE_FAT32_LBA
986 || partition
[i
].type
== MBR_PARTITION_TYPE_FAT16
987 || partition
[i
].type
== MBR_PARTITION_TYPE_FAT16_LBA
990 afatfs
.partitionStartSector
= partition
[i
].lbaBegin
;
999 static bool afatfs_parseVolumeID(const uint8_t *sector
)
1001 fatVolumeID_t
*volume
= (fatVolumeID_t
*) sector
;
1003 afatfs
.filesystemType
= FAT_FILESYSTEM_TYPE_INVALID
;
1005 if (volume
->bytesPerSector
!= AFATFS_SECTOR_SIZE
|| volume
->numFATs
!= AFATFS_NUM_FATS
1006 || sector
[510] != FAT_VOLUME_ID_SIGNATURE_1
|| sector
[511] != FAT_VOLUME_ID_SIGNATURE_2
) {
1010 afatfs
.fatStartSector
= afatfs
.partitionStartSector
+ volume
->reservedSectorCount
;
1012 afatfs
.sectorsPerCluster
= volume
->sectorsPerCluster
;
1013 if (afatfs
.sectorsPerCluster
< 1 || afatfs
.sectorsPerCluster
> 128 || !isPowerOfTwo(afatfs
.sectorsPerCluster
)) {
1017 afatfs
.byteInClusterMask
= AFATFS_SECTOR_SIZE
* afatfs
.sectorsPerCluster
- 1;
1019 afatfs
.fatSectors
= volume
->FATSize16
!= 0 ? volume
->FATSize16
: volume
->fatDescriptor
.fat32
.FATSize32
;
1021 // Always zero on FAT32 since rootEntryCount is always zero (this is non-zero on FAT16)
1022 afatfs
.rootDirectorySectors
= ((volume
->rootEntryCount
* FAT_DIRECTORY_ENTRY_SIZE
) + (volume
->bytesPerSector
- 1)) / volume
->bytesPerSector
;
1023 uint32_t totalSectors
= volume
->totalSectors16
!= 0 ? volume
->totalSectors16
: volume
->totalSectors32
;
1024 uint32_t dataSectors
= totalSectors
- (volume
->reservedSectorCount
+ (AFATFS_NUM_FATS
* afatfs
.fatSectors
) + afatfs
.rootDirectorySectors
);
1026 afatfs
.numClusters
= dataSectors
/ volume
->sectorsPerCluster
;
1028 if (afatfs
.numClusters
<= FAT12_MAX_CLUSTERS
) {
1029 afatfs
.filesystemType
= FAT_FILESYSTEM_TYPE_FAT12
;
1031 return false; // FAT12 is not a supported filesystem
1032 } else if (afatfs
.numClusters
<= FAT16_MAX_CLUSTERS
) {
1033 afatfs
.filesystemType
= FAT_FILESYSTEM_TYPE_FAT16
;
1035 afatfs
.filesystemType
= FAT_FILESYSTEM_TYPE_FAT32
;
1038 if (afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT32
) {
1039 afatfs
.rootDirectoryCluster
= volume
->fatDescriptor
.fat32
.rootCluster
;
1041 // FAT16 doesn't store the root directory in clusters
1042 afatfs
.rootDirectoryCluster
= 0;
1045 uint32_t endOfFATs
= afatfs
.fatStartSector
+ AFATFS_NUM_FATS
* afatfs
.fatSectors
;
1047 afatfs
.clusterStartSector
= endOfFATs
+ afatfs
.rootDirectorySectors
;
1053 * Get the position of the FAT entry for the cluster with the given number.
1055 static void afatfs_getFATPositionForCluster(uint32_t cluster
, uint32_t *fatSectorIndex
, uint32_t *fatSectorEntryIndex
)
1057 if (afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT16
) {
1058 uint32_t entriesPerFATSector
= AFATFS_SECTOR_SIZE
/ sizeof(uint16_t);
1060 *fatSectorIndex
= cluster
/ entriesPerFATSector
;
1061 *fatSectorEntryIndex
= cluster
& (entriesPerFATSector
- 1);
1063 uint32_t entriesPerFATSector
= AFATFS_SECTOR_SIZE
/ sizeof(uint32_t);
1065 *fatSectorIndex
= fat32_decodeClusterNumber(cluster
) / entriesPerFATSector
;
1066 *fatSectorEntryIndex
= cluster
& (entriesPerFATSector
- 1);
1070 static bool afatfs_FATIsEndOfChainMarker(uint32_t clusterNumber
)
1072 if (afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT32
) {
1073 return fat32_isEndOfChainMarker(clusterNumber
);
1075 return fat16_isEndOfChainMarker(clusterNumber
);
1080 * Look up the FAT to find out which cluster follows the one with the given number and store it into *nextCluster.
1082 * Use fat_isFreeSpace() and fat_isEndOfChainMarker() on nextCluster to distinguish those special values from regular
1085 * Note that if you're trying to find the next cluster of a file, you should be calling afatfs_fileGetNextCluster()
1086 * instead, as that one supports contiguous freefile-based files (which needn't consult the FAT).
1089 * AFATFS_OPERATION_IN_PROGRESS - FS is busy right now, call again later
1090 * AFATFS_OPERATION_SUCCESS - *nextCluster is set to the next cluster number
1092 static afatfsOperationStatus_e
afatfs_FATGetNextCluster(int fatIndex
, uint32_t cluster
, uint32_t *nextCluster
)
1094 uint32_t fatSectorIndex
, fatSectorEntryIndex
;
1095 afatfsFATSector_t sector
;
1097 afatfs_getFATPositionForCluster(cluster
, &fatSectorIndex
, &fatSectorEntryIndex
);
1099 afatfsOperationStatus_e result
= afatfs_cacheSector(afatfs_fatSectorToPhysical(fatIndex
, fatSectorIndex
), §or
.bytes
, AFATFS_CACHE_READ
, 0);
1101 if (result
== AFATFS_OPERATION_SUCCESS
) {
1102 if (afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT16
) {
1103 *nextCluster
= sector
.fat16
[fatSectorEntryIndex
];
1105 *nextCluster
= fat32_decodeClusterNumber(sector
.fat32
[fatSectorEntryIndex
]);
1113 * Set the cluster number that follows the given cluster. Pass 0xFFFFFFFF for nextCluster to terminate the FAT chain.
1116 * AFATFS_OPERATION_SUCCESS - On success
1117 * AFATFS_OPERATION_IN_PROGRESS - Card is busy, call again later
1118 * AFATFS_OPERATION_FAILURE - When the filesystem encounters a fatal error
1120 static afatfsOperationStatus_e
afatfs_FATSetNextCluster(uint32_t startCluster
, uint32_t nextCluster
)
1122 afatfsFATSector_t sector
;
1123 uint32_t fatSectorIndex
, fatSectorEntryIndex
, fatPhysicalSector
;
1124 afatfsOperationStatus_e result
;
1127 afatfs_assert(startCluster
>= FAT_SMALLEST_LEGAL_CLUSTER_NUMBER
);
1130 afatfs_getFATPositionForCluster(startCluster
, &fatSectorIndex
, &fatSectorEntryIndex
);
1132 fatPhysicalSector
= afatfs_fatSectorToPhysical(0, fatSectorIndex
);
1134 result
= afatfs_cacheSector(fatPhysicalSector
, §or
.bytes
, AFATFS_CACHE_READ
| AFATFS_CACHE_WRITE
, 0);
1136 if (result
== AFATFS_OPERATION_SUCCESS
) {
1137 if (afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT16
) {
1138 sector
.fat16
[fatSectorEntryIndex
] = nextCluster
;
1140 sector
.fat32
[fatSectorEntryIndex
] = nextCluster
;
1148 * Bring the logical filesize up to date with the current cursor position.
1150 static void afatfs_fileUpdateFilesize(afatfsFile_t
*file
)
1152 file
->logicalSize
= MAX(file
->logicalSize
, file
->cursorOffset
);
1155 static void afatfs_fileUnlockCacheSector(afatfsFilePtr_t file
)
1157 if (file
->writeLockedCacheIndex
!= -1) {
1158 afatfs
.cacheDescriptor
[file
->writeLockedCacheIndex
].locked
= 0;
1159 file
->writeLockedCacheIndex
= -1;
1161 if (file
->readRetainCacheIndex
!= -1) {
1162 afatfs
.cacheDescriptor
[file
->readRetainCacheIndex
].retainCount
= MAX((int) afatfs
.cacheDescriptor
[file
->readRetainCacheIndex
].retainCount
- 1, 0);
1163 file
->readRetainCacheIndex
= -1;
1168 * Starting from and including the given cluster number, find the number of the first cluster which matches the given
1171 * searchLimit - Last cluster to examine (exclusive). To search the entire volume, pass:
1172 * afatfs.numClusters + FAT_SMALLEST_LEGAL_CLUSTER_NUMBER
1175 * CLUSTER_SEARCH_FREE_AT_BEGINNING_OF_FAT_SECTOR - Find a cluster marked as free in the FAT which lies at the
1176 * beginning of its FAT sector. The passed initial search 'cluster' must correspond to the first entry of a FAT sector.
1177 * CLUSTER_SEARCH_FREE - Find a cluster marked as free in the FAT
1178 * CLUSTER_SEARCH_OCCUPIED - Find a cluster marked as occupied in the FAT.
1181 * AFATFS_FIND_CLUSTER_FOUND - A cluster matching the criteria was found and stored in *cluster
1182 * AFATFS_FIND_CLUSTER_IN_PROGRESS - The search is not over, call this routine again later with the updated *cluster value to resume
1183 * AFATFS_FIND_CLUSTER_FATAL - An unexpected read error occurred, the volume should be abandoned
1184 * AFATFS_FIND_CLUSTER_NOT_FOUND - The entire device was searched without finding a suitable cluster (the
1185 * *cluster points to just beyond the final cluster).
1187 static afatfsFindClusterStatus_e
afatfs_findClusterWithCondition(afatfsClusterSearchCondition_e condition
, uint32_t *cluster
, uint32_t searchLimit
)
1189 afatfsFATSector_t sector
;
1190 uint32_t fatSectorIndex
, fatSectorEntryIndex
;
1192 uint32_t fatEntriesPerSector
= afatfs_fatEntriesPerSector();
1193 bool lookingForFree
= condition
== CLUSTER_SEARCH_FREE_AT_BEGINNING_OF_FAT_SECTOR
|| condition
== CLUSTER_SEARCH_FREE
;
1197 // Get the FAT entry which corresponds to this cluster so we can begin our search there
1198 afatfs_getFATPositionForCluster(*cluster
, &fatSectorIndex
, &fatSectorEntryIndex
);
1200 switch (condition
) {
1201 case CLUSTER_SEARCH_FREE_AT_BEGINNING_OF_FAT_SECTOR
:
1202 jump
= fatEntriesPerSector
;
1204 // We're supposed to call this routine with the cluster properly aligned
1205 if (!afatfs_assert(fatSectorEntryIndex
== 0)) {
1206 return AFATFS_FIND_CLUSTER_FATAL
;
1209 case CLUSTER_SEARCH_OCCUPIED
:
1210 case CLUSTER_SEARCH_FREE
:
1214 afatfs_assert(false);
1215 return AFATFS_FIND_CLUSTER_FATAL
;
1218 while (*cluster
< searchLimit
) {
1220 #ifdef AFATFS_USE_FREEFILE
1221 // If we're looking inside the freefile, we won't find any free clusters! Skip it!
1222 if (afatfs
.freeFile
.logicalSize
> 0 && *cluster
== afatfs
.freeFile
.firstCluster
) {
1223 *cluster
+= (afatfs
.freeFile
.logicalSize
+ afatfs_clusterSize() - 1) / afatfs_clusterSize();
1225 // Maintain alignment
1226 *cluster
= roundUpTo(*cluster
, jump
);
1227 continue; // Go back to check that the new cluster number is within the volume
1231 afatfsOperationStatus_e status
= afatfs_cacheSector(afatfs_fatSectorToPhysical(0, fatSectorIndex
), §or
.bytes
, AFATFS_CACHE_READ
| AFATFS_CACHE_DISCARDABLE
, 0);
1234 case AFATFS_OPERATION_SUCCESS
:
1236 uint32_t clusterNumber
;
1238 switch (afatfs
.filesystemType
) {
1239 case FAT_FILESYSTEM_TYPE_FAT16
:
1240 clusterNumber
= sector
.fat16
[fatSectorEntryIndex
];
1242 case FAT_FILESYSTEM_TYPE_FAT32
:
1243 clusterNumber
= fat32_decodeClusterNumber(sector
.fat32
[fatSectorEntryIndex
]);
1246 return AFATFS_FIND_CLUSTER_FATAL
;
1249 if (fat_isFreeSpace(clusterNumber
) == lookingForFree
) {
1251 * The final FAT sector may have fewer than fatEntriesPerSector entries in it, so we need to
1252 * check the cluster number is valid here before we report a bogus success!
1254 if (*cluster
< searchLimit
) {
1255 return AFATFS_FIND_CLUSTER_FOUND
;
1257 *cluster
= searchLimit
;
1258 return AFATFS_FIND_CLUSTER_NOT_FOUND
;
1263 fatSectorEntryIndex
+= jump
;
1264 } while (fatSectorEntryIndex
< fatEntriesPerSector
);
1266 // Move on to the next FAT sector
1268 fatSectorEntryIndex
= 0;
1270 case AFATFS_OPERATION_FAILURE
:
1271 return AFATFS_FIND_CLUSTER_FATAL
;
1273 case AFATFS_OPERATION_IN_PROGRESS
:
1274 return AFATFS_FIND_CLUSTER_IN_PROGRESS
;
1279 // We looked at every available cluster and didn't find one matching the condition
1280 *cluster
= searchLimit
;
1281 return AFATFS_FIND_CLUSTER_NOT_FOUND
;
1285 * Get the cluster that follows the currentCluster in the FAT chain for the given file.
1288 * AFATFS_OPERATION_IN_PROGRESS - FS is busy right now, call again later
1289 * AFATFS_OPERATION_SUCCESS - *nextCluster is set to the next cluster number
1291 static afatfsOperationStatus_e
afatfs_fileGetNextCluster(afatfsFilePtr_t file
, uint32_t currentCluster
, uint32_t *nextCluster
)
1293 #ifndef AFATFS_USE_FREEFILE
1296 if ((file
->mode
& AFATFS_FILE_MODE_CONTIGUOUS
) != 0) {
1297 uint32_t freeFileStart
= afatfs
.freeFile
.firstCluster
;
1299 afatfs_assert(currentCluster
+ 1 <= freeFileStart
);
1301 // Would the next cluster lie outside the allocated file? (i.e. beyond the end of the file into the start of the freefile)
1302 if (currentCluster
+ 1 == freeFileStart
) {
1305 *nextCluster
= currentCluster
+ 1;
1308 return AFATFS_OPERATION_SUCCESS
;
1312 return afatfs_FATGetNextCluster(0, currentCluster
, nextCluster
);
1316 #ifdef AFATFS_USE_FREEFILE
1319 * Update the FAT to fill the contiguous series of clusters with indexes [*startCluster...endCluster) with the
1320 * specified pattern.
1322 * AFATFS_FAT_PATTERN_TERMINATED_CHAIN - Chain the clusters together in linear sequence and terminate the final cluster
1323 * AFATFS_FAT_PATTERN_CHAIN - Chain the clusters together without terminating the final entry
1324 * AFATFS_FAT_PATTERN_FREE - Mark the clusters as free space
1327 * AFATFS_OPERATION_SUCCESS - When the entire chain has been written
1328 * AFATFS_OPERATION_IN_PROGRESS - Call again later with the updated *startCluster value in order to resume writing.
1330 static afatfsOperationStatus_e
afatfs_FATFillWithPattern(afatfsFATPattern_e pattern
, uint32_t *startCluster
, uint32_t endCluster
)
1332 afatfsFATSector_t sector
;
1333 uint32_t fatSectorIndex
, firstEntryIndex
, fatPhysicalSector
;
1334 uint8_t fatEntrySize
;
1335 uint32_t nextCluster
;
1336 afatfsOperationStatus_e result
;
1337 uint32_t eraseSectorCount
;
1339 // Find the position of the initial cluster to begin our fill
1340 afatfs_getFATPositionForCluster(*startCluster
, &fatSectorIndex
, &firstEntryIndex
);
1342 fatPhysicalSector
= afatfs_fatSectorToPhysical(0, fatSectorIndex
);
1344 // How many consecutive FAT sectors will we be overwriting?
1345 eraseSectorCount
= (endCluster
- *startCluster
+ firstEntryIndex
+ afatfs_fatEntriesPerSector() - 1) / afatfs_fatEntriesPerSector();
1347 while (*startCluster
< endCluster
) {
1348 // The last entry we will fill inside this sector (exclusive):
1349 uint32_t lastEntryIndex
= MIN(firstEntryIndex
+ (endCluster
- *startCluster
), afatfs_fatEntriesPerSector());
1351 uint8_t cacheFlags
= AFATFS_CACHE_WRITE
| AFATFS_CACHE_DISCARDABLE
;
1353 if (firstEntryIndex
> 0 || lastEntryIndex
< afatfs_fatEntriesPerSector()) {
1354 // We're not overwriting the entire FAT sector so we must read the existing contents
1355 cacheFlags
|= AFATFS_CACHE_READ
;
1358 result
= afatfs_cacheSector(fatPhysicalSector
, §or
.bytes
, cacheFlags
, eraseSectorCount
);
1360 if (result
!= AFATFS_OPERATION_SUCCESS
) {
1364 #ifdef AFATFS_DEBUG_VERBOSE
1365 if (pattern
== AFATFS_FAT_PATTERN_FREE
) {
1366 fprintf(stderr
, "Marking cluster %u to %u as free in FAT sector %u...\n", *startCluster
, endCluster
, fatPhysicalSector
);
1368 fprintf(stderr
, "Writing FAT chain from cluster %u to %u in FAT sector %u...\n", *startCluster
, endCluster
, fatPhysicalSector
);
1373 case AFATFS_FAT_PATTERN_TERMINATED_CHAIN
:
1374 case AFATFS_FAT_PATTERN_UNTERMINATED_CHAIN
:
1375 nextCluster
= *startCluster
+ 1;
1376 // Write all the "next cluster" pointers
1377 if (afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT16
) {
1378 for (uint32_t i
= firstEntryIndex
; i
< lastEntryIndex
; i
++, nextCluster
++) {
1379 sector
.fat16
[i
] = nextCluster
;
1382 for (uint32_t i
= firstEntryIndex
; i
< lastEntryIndex
; i
++, nextCluster
++) {
1383 sector
.fat32
[i
] = nextCluster
;
1387 *startCluster
+= lastEntryIndex
- firstEntryIndex
;
1389 if (pattern
== AFATFS_FAT_PATTERN_TERMINATED_CHAIN
&& *startCluster
== endCluster
) {
1390 // We completed the chain! Overwrite the last entry we wrote with the terminator for the end of the chain
1391 if (afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT16
) {
1392 sector
.fat16
[lastEntryIndex
- 1] = 0xFFFF;
1394 sector
.fat32
[lastEntryIndex
- 1] = 0xFFFFFFFF;
1399 case AFATFS_FAT_PATTERN_FREE
:
1400 fatEntrySize
= afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT16
? sizeof(uint16_t) : sizeof(uint32_t);
1402 memset(sector
.bytes
+ firstEntryIndex
* fatEntrySize
, 0, (lastEntryIndex
- firstEntryIndex
) * fatEntrySize
);
1404 *startCluster
+= lastEntryIndex
- firstEntryIndex
;
1408 fatPhysicalSector
++;
1410 firstEntryIndex
= 0;
1413 return AFATFS_OPERATION_SUCCESS
;
1419 * Write the directory entry for the file into its `directoryEntryPos` position in its containing directory.
1422 * AFATFS_SAVE_DIRECTORY_NORMAL - Store the file's physical size, not the logical size, in the directory entry
1423 * AFATFS_SAVE_DIRECTORY_FOR_CLOSE - We're done extending the file so we can write the logical size now.
1424 * AFATFS_SAVE_DIRECTORY_DELETED - Mark the directory entry as deleted
1427 * AFATFS_OPERATION_SUCCESS - The directory entry has been stored into the directory sector in cache.
1428 * AFATFS_OPERATION_IN_PROGRESS - Cache is too busy, retry later
1429 * AFATFS_OPERATION_FAILURE - If the filesystem enters the fatal state
1431 static afatfsOperationStatus_e
afatfs_saveDirectoryEntry(afatfsFilePtr_t file
, afatfsSaveDirectoryEntryMode_e mode
)
1434 afatfsOperationStatus_e result
;
1436 if (file
->directoryEntryPos
.sectorNumberPhysical
== 0) {
1437 return AFATFS_OPERATION_SUCCESS
; // Root directories don't have a directory entry
1440 result
= afatfs_cacheSector(file
->directoryEntryPos
.sectorNumberPhysical
, §or
, AFATFS_CACHE_READ
| AFATFS_CACHE_WRITE
, 0);
1442 #ifdef AFATFS_DEBUG_VERBOSE
1443 fprintf(stderr
, "Saving directory entry to sector %u...\n", file
->directoryEntryPos
.sectorNumberPhysical
);
1446 if (result
== AFATFS_OPERATION_SUCCESS
) {
1447 if (afatfs_assert(file
->directoryEntryPos
.entryIndex
>= 0)) {
1448 fatDirectoryEntry_t
*entry
= (fatDirectoryEntry_t
*) sector
+ file
->directoryEntryPos
.entryIndex
;
1451 case AFATFS_SAVE_DIRECTORY_NORMAL
:
1452 /* We exaggerate the length of the written file so that if power is lost, the end of the file will
1453 * still be readable (though the very tail of the file will be uninitialized data).
1455 * This way we can avoid updating the directory entry too many times during fwrites() on the file.
1457 entry
->fileSize
= file
->physicalSize
;
1459 case AFATFS_SAVE_DIRECTORY_DELETED
:
1460 entry
->filename
[0] = FAT_DELETED_FILE_MARKER
;
1463 case AFATFS_SAVE_DIRECTORY_FOR_CLOSE
:
1464 // We write the true length of the file on close.
1465 entry
->fileSize
= file
->logicalSize
;
1468 // (sub)directories don't store a filesize in their directory entry:
1469 if (file
->type
== AFATFS_FILE_TYPE_DIRECTORY
) {
1470 entry
->fileSize
= 0;
1473 entry
->firstClusterHigh
= file
->firstCluster
>> 16;
1474 entry
->firstClusterLow
= file
->firstCluster
& 0xFFFF;
1476 return AFATFS_OPERATION_FAILURE
;
1484 * Attempt to add a free cluster to the end of the given file. If the file was previously empty, the directory entry
1485 * is updated to point to the new cluster.
1488 * AFATFS_OPERATION_SUCCESS - The cluster has been appended
1489 * AFATFS_OPERATION_IN_PROGRESS - Cache was busy, so call again later to continue
1490 * AFATFS_OPERATION_FAILURE - Cluster could not be appended because the filesystem ran out of space
1491 * (afatfs.filesystemFull is set to true)
1493 * If the file's operation was AFATFS_FILE_OPERATION_APPEND_FREE_CLUSTER, the file operation is cleared upon completion,
1494 * otherwise it is left alone so that this operation can be called as a sub-operation of some other operation on the
1497 static afatfsOperationStatus_e
afatfs_appendRegularFreeClusterContinue(afatfsFile_t
*file
)
1499 afatfsAppendFreeCluster_t
*opState
= &file
->operation
.state
.appendFreeCluster
;
1500 afatfsOperationStatus_e status
;
1504 switch (opState
->phase
) {
1505 case AFATFS_APPEND_FREE_CLUSTER_PHASE_FIND_FREESPACE
:
1506 switch (afatfs_findClusterWithCondition(CLUSTER_SEARCH_FREE
, &opState
->searchCluster
, afatfs
.numClusters
+ FAT_SMALLEST_LEGAL_CLUSTER_NUMBER
)) {
1507 case AFATFS_FIND_CLUSTER_FOUND
:
1508 afatfs
.lastClusterAllocated
= opState
->searchCluster
;
1510 // Make the cluster available for us to write in
1511 file
->cursorCluster
= opState
->searchCluster
;
1512 file
->physicalSize
+= afatfs_clusterSize();
1514 if (opState
->previousCluster
== 0) {
1515 // This is the new first cluster in the file
1516 file
->firstCluster
= opState
->searchCluster
;
1519 opState
->phase
= AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FAT1
;
1522 case AFATFS_FIND_CLUSTER_FATAL
:
1523 case AFATFS_FIND_CLUSTER_NOT_FOUND
:
1524 // We couldn't find an empty cluster to append to the file
1525 opState
->phase
= AFATFS_APPEND_FREE_CLUSTER_PHASE_FAILURE
;
1528 case AFATFS_FIND_CLUSTER_IN_PROGRESS
:
1532 case AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FAT1
:
1533 // Terminate the new cluster
1534 status
= afatfs_FATSetNextCluster(opState
->searchCluster
, 0xFFFFFFFF);
1536 if (status
== AFATFS_OPERATION_SUCCESS
) {
1537 if (opState
->previousCluster
) {
1538 opState
->phase
= AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FAT2
;
1540 opState
->phase
= AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FILE_DIRECTORY
;
1546 case AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FAT2
:
1547 // Add the new cluster to the pre-existing chain
1548 status
= afatfs_FATSetNextCluster(opState
->previousCluster
, opState
->searchCluster
);
1550 if (status
== AFATFS_OPERATION_SUCCESS
) {
1551 opState
->phase
= AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FILE_DIRECTORY
;
1555 case AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FILE_DIRECTORY
:
1556 if (afatfs_saveDirectoryEntry(file
, AFATFS_SAVE_DIRECTORY_NORMAL
) == AFATFS_OPERATION_SUCCESS
) {
1557 opState
->phase
= AFATFS_APPEND_FREE_CLUSTER_PHASE_COMPLETE
;
1561 case AFATFS_APPEND_FREE_CLUSTER_PHASE_COMPLETE
:
1562 if (file
->operation
.operation
== AFATFS_FILE_OPERATION_APPEND_FREE_CLUSTER
) {
1563 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
1566 return AFATFS_OPERATION_SUCCESS
;
1568 case AFATFS_APPEND_FREE_CLUSTER_PHASE_FAILURE
:
1569 if (file
->operation
.operation
== AFATFS_FILE_OPERATION_APPEND_FREE_CLUSTER
) {
1570 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
1573 afatfs
.filesystemFull
= true;
1574 return AFATFS_OPERATION_FAILURE
;
1578 return AFATFS_OPERATION_IN_PROGRESS
;
1581 static void afatfs_appendRegularFreeClusterInitOperationState(afatfsAppendFreeCluster_t
*state
, uint32_t previousCluster
)
1583 state
->phase
= AFATFS_APPEND_FREE_CLUSTER_PHASE_INITIAL
;
1584 state
->previousCluster
= previousCluster
;
1585 state
->searchCluster
= afatfs
.lastClusterAllocated
;
1589 * Queue up an operation to append a free cluster to the file and update the file's cursorCluster to point to it.
1591 * You must seek to the end of the file first, so file.cursorCluster will be 0 for the first call, and
1592 * `file.cursorPreviousCluster` will be the cluster to append after.
1594 * Note that the cursorCluster will be updated before this operation is completely finished (i.e. before the FAT is
1595 * updated) but you can go ahead and write to it before the operation succeeds.
1598 * AFATFS_OPERATION_SUCCESS - The append completed successfully
1599 * AFATFS_OPERATION_IN_PROGRESS - The operation was queued on the file and will complete later
1600 * AFATFS_OPERATION_FAILURE - Operation could not be queued or append failed, check afatfs.fileSystemFull
1602 static afatfsOperationStatus_e
afatfs_appendRegularFreeCluster(afatfsFilePtr_t file
)
1604 if (file
->operation
.operation
== AFATFS_FILE_OPERATION_APPEND_FREE_CLUSTER
)
1605 return AFATFS_OPERATION_IN_PROGRESS
;
1607 if (afatfs
.filesystemFull
|| afatfs_fileIsBusy(file
)) {
1608 return AFATFS_OPERATION_FAILURE
;
1611 file
->operation
.operation
= AFATFS_FILE_OPERATION_APPEND_FREE_CLUSTER
;
1613 afatfs_appendRegularFreeClusterInitOperationState(&file
->operation
.state
.appendFreeCluster
, file
->cursorPreviousCluster
);
1615 return afatfs_appendRegularFreeClusterContinue(file
);
1618 #ifdef AFATFS_USE_FREEFILE
1621 * Size of a AFATFS supercluster in bytes
1623 ONLY_EXPOSE_FOR_TESTING
1624 uint32_t afatfs_superClusterSize(void)
1626 return afatfs_fatEntriesPerSector() * afatfs_clusterSize();
1630 * Continue to attempt to add a supercluster to the end of the given file.
1632 * If the file operation was set to AFATFS_FILE_OPERATION_APPEND_SUPERCLUSTER and the operation completes, the file's
1633 * operation is cleared.
1636 * AFATFS_OPERATION_SUCCESS - On completion
1637 * AFATFS_OPERATION_IN_PROGRESS - Operation still in progress
1639 static afatfsOperationStatus_e
afatfs_appendSuperclusterContinue(afatfsFile_t
*file
)
1641 afatfsAppendSupercluster_t
*opState
= &file
->operation
.state
.appendSupercluster
;
1643 afatfsOperationStatus_e status
;
1646 switch (opState
->phase
) {
1647 case AFATFS_APPEND_SUPERCLUSTER_PHASE_INIT
:
1648 // Our file steals the first cluster of the freefile
1650 // We can go ahead and write to that space before the FAT and directory are updated
1651 file
->cursorCluster
= afatfs
.freeFile
.firstCluster
;
1652 file
->physicalSize
+= afatfs_superClusterSize();
1654 /* Remove the first supercluster from the freefile
1656 * Even if the freefile becomes empty, we still don't set its first cluster to zero. This is so that
1657 * afatfs_fileGetNextCluster() can tell where a contiguous file ends (at the start of the freefile).
1659 * Note that normally the freefile can't become empty because it is allocated as a non-integer number
1660 * of superclusters to avoid precisely this situation.
1662 afatfs
.freeFile
.firstCluster
+= afatfs_fatEntriesPerSector();
1663 afatfs
.freeFile
.logicalSize
-= afatfs_superClusterSize();
1664 afatfs
.freeFile
.physicalSize
-= afatfs_superClusterSize();
1666 // The new supercluster needs to have its clusters chained contiguously and marked with a terminator at the end
1667 opState
->fatRewriteStartCluster
= file
->cursorCluster
;
1668 opState
->fatRewriteEndCluster
= opState
->fatRewriteStartCluster
+ afatfs_fatEntriesPerSector();
1670 if (opState
->previousCluster
== 0) {
1671 // This is the new first cluster in the file so we need to update the directory entry
1672 file
->firstCluster
= file
->cursorCluster
;
1675 * We also need to update the FAT of the supercluster that used to end the file so that it no longer
1678 opState
->fatRewriteStartCluster
-= afatfs_fatEntriesPerSector();
1681 opState
->phase
= AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FREEFILE_DIRECTORY
;
1684 case AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FREEFILE_DIRECTORY
:
1685 // First update the freefile's directory entry to remove the first supercluster so we don't risk cross-linking the file
1686 status
= afatfs_saveDirectoryEntry(&afatfs
.freeFile
, AFATFS_SAVE_DIRECTORY_NORMAL
);
1688 if (status
== AFATFS_OPERATION_SUCCESS
) {
1689 opState
->phase
= AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FAT
;
1693 case AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FAT
:
1694 status
= afatfs_FATFillWithPattern(AFATFS_FAT_PATTERN_TERMINATED_CHAIN
, &opState
->fatRewriteStartCluster
, opState
->fatRewriteEndCluster
);
1696 if (status
== AFATFS_OPERATION_SUCCESS
) {
1697 opState
->phase
= AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FILE_DIRECTORY
;
1701 case AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FILE_DIRECTORY
:
1702 // Update the fileSize/firstCluster in the directory entry for the file
1703 status
= afatfs_saveDirectoryEntry(file
, AFATFS_SAVE_DIRECTORY_NORMAL
);
1706 status
= AFATFS_OPERATION_FAILURE
;
1709 if ((status
== AFATFS_OPERATION_FAILURE
|| status
== AFATFS_OPERATION_SUCCESS
) && file
->operation
.operation
== AFATFS_FILE_OPERATION_APPEND_SUPERCLUSTER
) {
1710 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
1717 * Attempt to queue up an operation to append the first supercluster of the freefile to the given `file` (file's cursor
1718 * must be at end-of-file).
1720 * The new cluster number will be set into the file's cursorCluster.
1723 * AFATFS_OPERATION_SUCCESS - The append completed successfully and the file's cursorCluster has been updated
1724 * AFATFS_OPERATION_IN_PROGRESS - The operation was queued on the file and will complete later, or there is already an
1725 * append in progress.
1726 * AFATFS_OPERATION_FAILURE - Operation could not be queued (file was busy) or append failed (filesystem is full).
1727 * Check afatfs.fileSystemFull
1729 static afatfsOperationStatus_e
afatfs_appendSupercluster(afatfsFilePtr_t file
)
1731 uint32_t superClusterSize
= afatfs_superClusterSize();
1733 if (file
->operation
.operation
== AFATFS_FILE_OPERATION_APPEND_SUPERCLUSTER
) {
1734 return AFATFS_OPERATION_IN_PROGRESS
;
1737 if (afatfs
.freeFile
.logicalSize
< superClusterSize
) {
1738 afatfs
.filesystemFull
= true;
1741 if (afatfs
.filesystemFull
|| afatfs_fileIsBusy(file
)) {
1742 return AFATFS_OPERATION_FAILURE
;
1745 afatfsAppendSupercluster_t
*opState
= &file
->operation
.state
.appendSupercluster
;
1747 file
->operation
.operation
= AFATFS_FILE_OPERATION_APPEND_SUPERCLUSTER
;
1748 opState
->phase
= AFATFS_APPEND_SUPERCLUSTER_PHASE_INIT
;
1749 opState
->previousCluster
= file
->cursorPreviousCluster
;
1751 return afatfs_appendSuperclusterContinue(file
);
1757 * Queue an operation to add a cluster of free space to the end of the file. Must be called when the file's cursor
1758 * is beyond the last allocated cluster.
1760 static afatfsOperationStatus_e
afatfs_appendFreeCluster(afatfsFilePtr_t file
)
1762 afatfsOperationStatus_e status
;
1764 #ifdef AFATFS_USE_FREEFILE
1765 if ((file
->mode
& AFATFS_FILE_MODE_CONTIGUOUS
) != 0) {
1766 // Steal the first cluster from the beginning of the freefile if we can
1767 status
= afatfs_appendSupercluster(file
);
1771 status
= afatfs_appendRegularFreeCluster(file
);
1778 * Returns true if the file's cursor is sitting beyond the end of the last allocated cluster (i.e. the logical fileSize
1781 static bool afatfs_isEndOfAllocatedFile(afatfsFilePtr_t file
)
1783 if (file
->type
== AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY
) {
1784 return file
->cursorOffset
>= AFATFS_SECTOR_SIZE
* afatfs
.rootDirectorySectors
;
1786 return file
->cursorCluster
== 0 || afatfs_FATIsEndOfChainMarker(file
->cursorCluster
);
1791 * Take a lock on the sector at the current file cursor position.
1793 * Returns a pointer to the sector buffer if successful, or NULL if at the end of file (check afatfs_isEndOfAllocatedFile())
1794 * or the sector has not yet been read in from disk.
1796 static uint8_t* afatfs_fileRetainCursorSectorForRead(afatfsFilePtr_t file
)
1800 uint32_t physicalSector
= afatfs_fileGetCursorPhysicalSector(file
);
1802 /* If we've already got a locked sector then we can assume that was the same one that's at the cursor (because this
1803 * cache is invalidated when crossing a sector boundary)
1805 if (file
->readRetainCacheIndex
!= -1) {
1806 if (!afatfs_assert(physicalSector
== afatfs
.cacheDescriptor
[file
->readRetainCacheIndex
].sectorIndex
)) {
1810 result
= afatfs_cacheSectorGetMemory(file
->readRetainCacheIndex
);
1812 if (afatfs_isEndOfAllocatedFile(file
)) {
1816 afatfs_assert(physicalSector
> 0); // We never read the root sector using files
1818 afatfsOperationStatus_e status
= afatfs_cacheSector(
1821 AFATFS_CACHE_READ
| AFATFS_CACHE_RETAIN
,
1825 if (status
!= AFATFS_OPERATION_SUCCESS
) {
1826 // Sector not ready for read
1830 file
->readRetainCacheIndex
= afatfs_getCacheDescriptorIndexForBuffer(result
);
1837 * Lock the sector at the file's cursor position for write, and return a reference to the memory for that sector.
1839 * Returns NULL if the cache was too busy, try again later.
1841 static uint8_t* afatfs_fileLockCursorSectorForWrite(afatfsFilePtr_t file
)
1843 afatfsOperationStatus_e status
;
1845 uint32_t eraseBlockCount
;
1847 // Do we already have a sector locked in our cache at the cursor position?
1848 if (file
->writeLockedCacheIndex
!= -1) {
1849 uint32_t physicalSector
= afatfs_fileGetCursorPhysicalSector(file
);
1851 if (!afatfs_assert(physicalSector
== afatfs
.cacheDescriptor
[file
->writeLockedCacheIndex
].sectorIndex
)) {
1855 result
= afatfs_cacheSectorGetMemory(file
->writeLockedCacheIndex
);
1857 // Find / allocate a sector and lock it in the cache so we can rely on it sticking around
1859 // Are we at the start of an empty file or the end of a non-empty file? If so we need to add a cluster
1860 if (afatfs_isEndOfAllocatedFile(file
) && afatfs_appendFreeCluster(file
) != AFATFS_OPERATION_SUCCESS
) {
1861 // The extension of the file is in progress so please call us again later to try again
1865 uint32_t physicalSector
= afatfs_fileGetCursorPhysicalSector(file
);
1866 uint8_t cacheFlags
= AFATFS_CACHE_WRITE
| AFATFS_CACHE_LOCK
;
1867 uint32_t cursorOffsetInSector
= file
->cursorOffset
% AFATFS_SECTOR_SIZE
;
1868 uint32_t offsetOfStartOfSector
= file
->cursorOffset
& ~((uint32_t) AFATFS_SECTOR_SIZE
- 1);
1869 uint32_t offsetOfEndOfSector
= offsetOfStartOfSector
+ AFATFS_SECTOR_SIZE
;
1872 * If there is data before the write point in this sector, or there could be data after the write-point
1873 * then we need to have the original contents of the sector in the cache for us to merge into
1876 cursorOffsetInSector
> 0
1877 || offsetOfEndOfSector
< file
->logicalSize
1879 cacheFlags
|= AFATFS_CACHE_READ
;
1882 // In contiguous append mode, we'll pre-erase the whole supercluster
1883 if ((file
->mode
& (AFATFS_FILE_MODE_APPEND
| AFATFS_FILE_MODE_CONTIGUOUS
)) == (AFATFS_FILE_MODE_APPEND
| AFATFS_FILE_MODE_CONTIGUOUS
)) {
1884 uint32_t cursorOffsetInSupercluster
= file
->cursorOffset
& (afatfs_superClusterSize() - 1);
1886 eraseBlockCount
= afatfs_fatEntriesPerSector() * afatfs
.sectorsPerCluster
- cursorOffsetInSupercluster
/ AFATFS_SECTOR_SIZE
;
1888 eraseBlockCount
= 0;
1891 status
= afatfs_cacheSector(
1898 if (status
!= AFATFS_OPERATION_SUCCESS
) {
1899 // Not enough cache available to accept this write / sector not ready for read
1903 file
->writeLockedCacheIndex
= afatfs_getCacheDescriptorIndexForBuffer(result
);
1910 * Attempt to seek the file pointer by the offset, relative to the current position.
1912 * Returns true if the seek was completed, or false if you should try again later by calling this routine again (the
1913 * cursor is not moved and no seek operation is queued on the file for you).
1915 * You can only seek forwards by the size of a cluster or less, or backwards to stay within the same cluster. Otherwise
1916 * false will always be returned (calling this routine again will never make progress on the seek).
1918 * This amount of seek is special because we will have to wait on at most one read operation, so it's easy to make
1921 static bool afatfs_fseekAtomic(afatfsFilePtr_t file
, int32_t offset
)
1923 // Seeks within a sector
1924 uint32_t newSectorOffset
= offset
+ file
->cursorOffset
% AFATFS_SECTOR_SIZE
;
1926 // i.e. newSectorOffset is non-negative and smaller than AFATFS_SECTOR_SIZE, we're staying within the same sector
1927 if (newSectorOffset
< AFATFS_SECTOR_SIZE
) {
1928 file
->cursorOffset
+= offset
;
1932 // We're seeking outside the sector so unlock it if we were holding it
1933 afatfs_fileUnlockCacheSector(file
);
1935 // FAT16 root directories are made up of contiguous sectors rather than clusters
1936 if (file
->type
== AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY
) {
1937 file
->cursorOffset
+= offset
;
1942 uint32_t clusterSizeBytes
= afatfs_clusterSize();
1943 uint32_t offsetInCluster
= afatfs_byteIndexInCluster(file
->cursorOffset
);
1944 uint32_t newOffsetInCluster
= offsetInCluster
+ offset
;
1946 afatfsOperationStatus_e status
;
1948 if (offset
> (int32_t) clusterSizeBytes
|| offset
< -(int32_t) offsetInCluster
) {
1952 // Are we seeking outside the cluster? If so we'll need to find out the next cluster number
1953 if (newOffsetInCluster
>= clusterSizeBytes
) {
1954 uint32_t nextCluster
;
1956 status
= afatfs_fileGetNextCluster(file
, file
->cursorCluster
, &nextCluster
);
1958 if (status
== AFATFS_OPERATION_SUCCESS
) {
1959 // Seek to the beginning of the next cluster
1960 uint32_t bytesToSeek
= clusterSizeBytes
- offsetInCluster
;
1962 file
->cursorPreviousCluster
= file
->cursorCluster
;
1963 file
->cursorCluster
= nextCluster
;
1964 file
->cursorOffset
+= bytesToSeek
;
1966 offset
-= bytesToSeek
;
1973 // If we didn't already hit the end of the file, add any remaining offset needed inside the cluster
1974 if (!afatfs_isEndOfAllocatedFile(file
)) {
1975 file
->cursorOffset
+= offset
;
1982 * Returns true if the seek was completed, or false if it is still in progress.
1984 static bool afatfs_fseekInternalContinue(afatfsFile_t
*file
)
1986 afatfsSeek_t
*opState
= &file
->operation
.state
.seek
;
1987 uint32_t clusterSizeBytes
= afatfs_clusterSize();
1988 uint32_t offsetInCluster
= afatfs_byteIndexInCluster(file
->cursorOffset
);
1990 afatfsOperationStatus_e status
;
1992 // Keep advancing the cursor cluster forwards to consume seekOffset
1993 while (offsetInCluster
+ opState
->seekOffset
>= clusterSizeBytes
&& !afatfs_isEndOfAllocatedFile(file
)) {
1994 uint32_t nextCluster
;
1996 status
= afatfs_fileGetNextCluster(file
, file
->cursorCluster
, &nextCluster
);
1998 if (status
== AFATFS_OPERATION_SUCCESS
) {
1999 // Seek to the beginning of the next cluster
2000 uint32_t bytesToSeek
= clusterSizeBytes
- offsetInCluster
;
2002 file
->cursorPreviousCluster
= file
->cursorCluster
;
2003 file
->cursorCluster
= nextCluster
;
2005 file
->cursorOffset
+= bytesToSeek
;
2006 opState
->seekOffset
-= bytesToSeek
;
2007 offsetInCluster
= 0;
2014 // If we didn't already hit the end of the file, add any remaining offset needed inside the cluster
2015 if (!afatfs_isEndOfAllocatedFile(file
)) {
2016 file
->cursorOffset
+= opState
->seekOffset
;
2019 afatfs_fileUpdateFilesize(file
); // TODO do we need this?
2021 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2023 if (opState
->callback
) {
2024 opState
->callback(file
);
2031 * Seek the file pointer forwards by offset bytes. Calls the callback when the seek is complete.
2033 * Will happily seek beyond the logical end of the file.
2036 * AFATFS_OPERATION_SUCCESS - The seek was completed immediately
2037 * AFATFS_OPERATION_IN_PROGRESS - The seek was queued and will complete later
2038 * AFATFS_OPERATION_FAILURE - The seek could not be queued because the file was busy with another operation,
2041 static afatfsOperationStatus_e
afatfs_fseekInternal(afatfsFilePtr_t file
, uint32_t offset
, afatfsFileCallback_t callback
)
2043 // See if we can seek without queuing an operation
2044 if (afatfs_fseekAtomic(file
, offset
)) {
2049 return AFATFS_OPERATION_SUCCESS
;
2051 // Our operation must queue
2052 if (afatfs_fileIsBusy(file
)) {
2053 return AFATFS_OPERATION_FAILURE
;
2056 afatfsSeek_t
*opState
= &file
->operation
.state
.seek
;
2058 file
->operation
.operation
= AFATFS_FILE_OPERATION_SEEK
;
2059 opState
->callback
= callback
;
2060 opState
->seekOffset
= offset
;
2062 return AFATFS_OPERATION_IN_PROGRESS
;
2067 * Attempt to seek the file cursor from the given point (`whence`) by the given offset, just like C's fseek().
2069 * AFATFS_SEEK_SET with offset 0 will always return AFATFS_OPERATION_SUCCESS.
2072 * AFATFS_OPERATION_SUCCESS - The seek was completed immediately
2073 * AFATFS_OPERATION_IN_PROGRESS - The seek was queued and will complete later. Feel free to attempt read/write
2074 * operations on the file, they'll fail until the seek is complete.
2075 * AFATFS_OPERATION_FAILURE - The seek could not be queued because the file was busy with another operation,
2078 afatfsOperationStatus_e
afatfs_fseek(afatfsFilePtr_t file
, int32_t offset
, afatfsSeek_e whence
)
2080 // We need an up-to-date logical filesize so we can clamp seeks to the EOF
2081 afatfs_fileUpdateFilesize(file
);
2084 case AFATFS_SEEK_CUR
:
2086 // Only forwards seeks are supported by this routine:
2087 return afatfs_fseekInternal(file
, MIN(file
->cursorOffset
+ offset
, file
->logicalSize
), NULL
);
2090 // Convert a backwards relative seek into a SEEK_SET. TODO considerable room for improvement if within the same cluster
2091 offset
+= file
->cursorOffset
;
2094 case AFATFS_SEEK_END
:
2095 // Are we already at the right position?
2096 if (file
->logicalSize
+ offset
== file
->cursorOffset
) {
2097 return AFATFS_OPERATION_SUCCESS
;
2100 // Convert into a SEEK_SET
2101 offset
+= file
->logicalSize
;
2104 case AFATFS_SEEK_SET
:
2109 // Now we have a SEEK_SET with a positive offset. Begin by seeking to the start of the file
2110 afatfs_fileUnlockCacheSector(file
);
2112 file
->cursorPreviousCluster
= 0;
2113 file
->cursorCluster
= file
->firstCluster
;
2114 file
->cursorOffset
= 0;
2116 // Then seek forwards by the offset
2117 return afatfs_fseekInternal(file
, MIN((uint32_t) offset
, file
->logicalSize
), NULL
);
2121 * Attempt to seek the file cursor from the given point (`whence`) by the given offset, just like C's fseek().
2123 * AFATFS_SEEK_SET with offset 0 will always return AFATFS_OPERATION_SUCCESS.
2126 * AFATFS_OPERATION_SUCCESS - The seek was completed immediately
2127 * AFATFS_OPERATION_IN_PROGRESS - The seek was queued and will complete later. Feel free to attempt read/write
2128 * operations on the file, they'll fail until the seek is complete.
2130 afatfsOperationStatus_e
afatfs_fseekSync(afatfsFilePtr_t file
, int32_t offset
, afatfsSeek_e whence
)
2132 while (afatfs_fileIsBusy(file
)) {
2136 return afatfs_fseek(file
, offset
, whence
);
2140 * Get the byte-offset of the file's cursor from the start of the file.
2142 * Returns true on success, or false if the file is busy (try again later).
2144 bool afatfs_ftell(afatfsFilePtr_t file
, uint32_t *position
)
2146 if (afatfs_fileIsBusy(file
)) {
2149 *position
= file
->cursorOffset
;
2155 * Attempt to advance the directory pointer `finder` to the next entry in the directory.
2158 * AFATFS_OPERATION_SUCCESS - A pointer to the next directory entry has been loaded into *dirEntry. If the
2159 * directory was exhausted then *dirEntry will be set to NULL.
2160 * AFATFS_OPERATION_IN_PROGRESS - The disk is busy. The pointer is not advanced, call again later to retry.
2162 afatfsOperationStatus_e
afatfs_findNext(afatfsFilePtr_t directory
, afatfsFinder_t
*finder
, fatDirectoryEntry_t
**dirEntry
)
2166 if (finder
->entryIndex
== AFATFS_FILES_PER_DIRECTORY_SECTOR
- 1) {
2167 if (afatfs_fseekAtomic(directory
, AFATFS_SECTOR_SIZE
)) {
2168 finder
->entryIndex
= -1;
2169 // Fall through to read the first entry of that new sector
2171 return AFATFS_OPERATION_IN_PROGRESS
;
2175 sector
= afatfs_fileRetainCursorSectorForRead(directory
);
2178 finder
->entryIndex
++;
2180 *dirEntry
= (fatDirectoryEntry_t
*) sector
+ finder
->entryIndex
;
2182 finder
->sectorNumberPhysical
= afatfs_fileGetCursorPhysicalSector(directory
);
2184 return AFATFS_OPERATION_SUCCESS
;
2186 if (afatfs_isEndOfAllocatedFile(directory
)) {
2189 return AFATFS_OPERATION_SUCCESS
;
2192 return AFATFS_OPERATION_IN_PROGRESS
;
2197 * Release resources associated with a find operation. Calling this more than once is harmless.
2199 void afatfs_findLast(afatfsFilePtr_t directory
)
2201 afatfs_fileUnlockCacheSector(directory
);
2205 * Initialise the finder so that the first call with the directory to findNext() will return the first file in the
2208 void afatfs_findFirst(afatfsFilePtr_t directory
, afatfsFinder_t
*finder
)
2210 afatfs_fseek(directory
, 0, AFATFS_SEEK_SET
);
2211 finder
->entryIndex
= -1;
2214 static afatfsOperationStatus_e
afatfs_extendSubdirectoryContinue(afatfsFile_t
*directory
)
2216 afatfsExtendSubdirectory_t
*opState
= &directory
->operation
.state
.extendSubdirectory
;
2217 afatfsOperationStatus_e status
;
2218 uint8_t *sectorBuffer
;
2219 uint32_t clusterNumber
, physicalSector
;
2220 uint16_t sectorInCluster
;
2223 switch (opState
->phase
) {
2224 case AFATFS_EXTEND_SUBDIRECTORY_PHASE_ADD_FREE_CLUSTER
:
2225 status
= afatfs_appendRegularFreeClusterContinue(directory
);
2227 if (status
== AFATFS_OPERATION_SUCCESS
) {
2228 opState
->phase
= AFATFS_EXTEND_SUBDIRECTORY_PHASE_WRITE_SECTORS
;
2230 } else if (status
== AFATFS_OPERATION_FAILURE
) {
2231 opState
->phase
= AFATFS_EXTEND_SUBDIRECTORY_PHASE_FAILURE
;
2235 case AFATFS_EXTEND_SUBDIRECTORY_PHASE_WRITE_SECTORS
:
2236 // Now, zero out that cluster
2237 afatfs_fileGetCursorClusterAndSector(directory
, &clusterNumber
, §orInCluster
);
2238 physicalSector
= afatfs_fileGetCursorPhysicalSector(directory
);
2241 status
= afatfs_cacheSector(physicalSector
, §orBuffer
, AFATFS_CACHE_WRITE
, 0);
2243 if (status
!= AFATFS_OPERATION_SUCCESS
) {
2247 memset(sectorBuffer
, 0, AFATFS_SECTOR_SIZE
);
2249 // If this is the first sector of a non-root directory, create the "." and ".." entries
2250 if (directory
->directoryEntryPos
.sectorNumberPhysical
!= 0 && directory
->cursorOffset
== 0) {
2251 fatDirectoryEntry_t
*dirEntries
= (fatDirectoryEntry_t
*) sectorBuffer
;
2253 memset(dirEntries
[0].filename
, ' ', sizeof(dirEntries
[0].filename
));
2254 dirEntries
[0].filename
[0] = '.';
2255 dirEntries
[0].firstClusterHigh
= directory
->firstCluster
>> 16;
2256 dirEntries
[0].firstClusterLow
= directory
->firstCluster
& 0xFFFF;
2257 dirEntries
[0].attrib
= FAT_FILE_ATTRIBUTE_DIRECTORY
;
2259 memset(dirEntries
[1].filename
, ' ', sizeof(dirEntries
[1].filename
));
2260 dirEntries
[1].filename
[0] = '.';
2261 dirEntries
[1].filename
[1] = '.';
2262 dirEntries
[1].firstClusterHigh
= opState
->parentDirectoryCluster
>> 16;
2263 dirEntries
[1].firstClusterLow
= opState
->parentDirectoryCluster
& 0xFFFF;
2264 dirEntries
[1].attrib
= FAT_FILE_ATTRIBUTE_DIRECTORY
;
2267 if (sectorInCluster
< afatfs
.sectorsPerCluster
- 1) {
2268 // Move to next sector
2269 afatfs_assert(afatfs_fseekAtomic(directory
, AFATFS_SECTOR_SIZE
));
2277 // Seek back to the beginning of the cluster
2278 afatfs_assert(afatfs_fseekAtomic(directory
, -AFATFS_SECTOR_SIZE
* (afatfs
.sectorsPerCluster
- 1)));
2279 opState
->phase
= AFATFS_EXTEND_SUBDIRECTORY_PHASE_SUCCESS
;
2282 case AFATFS_EXTEND_SUBDIRECTORY_PHASE_SUCCESS
:
2283 directory
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2285 if (opState
->callback
) {
2286 opState
->callback(directory
);
2289 return AFATFS_OPERATION_SUCCESS
;
2291 case AFATFS_EXTEND_SUBDIRECTORY_PHASE_FAILURE
:
2292 directory
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2294 if (opState
->callback
) {
2295 opState
->callback(NULL
);
2297 return AFATFS_OPERATION_FAILURE
;
2301 return AFATFS_OPERATION_IN_PROGRESS
;
2305 * Queue an operation to add a cluster to a sub-directory.
2307 * Tthe new cluster is zero-filled. "." and ".." entries are added if it is the first cluster of a new subdirectory.
2309 * The directory must not be busy, otherwise AFATFS_OPERATION_FAILURE is returned immediately.
2311 * The directory's cursor must lie at the end of the directory file (i.e. isEndOfAllocatedFile() would return true).
2313 * You must provide parentDirectory if this is the first extension to the subdirectory, otherwise pass NULL for that argument.
2315 static afatfsOperationStatus_e
afatfs_extendSubdirectory(afatfsFile_t
*directory
, afatfsFilePtr_t parentDirectory
, afatfsFileCallback_t callback
)
2317 // FAT16 root directories cannot be extended
2318 if (directory
->type
== AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY
|| afatfs_fileIsBusy(directory
)) {
2319 return AFATFS_OPERATION_FAILURE
;
2323 * We'll assume that we're never asked to append the first cluster of a root directory, since any
2324 * reasonably-formatted volume should have a root!
2326 afatfsExtendSubdirectory_t
*opState
= &directory
->operation
.state
.extendSubdirectory
;
2328 directory
->operation
.operation
= AFATFS_FILE_OPERATION_EXTEND_SUBDIRECTORY
;
2330 opState
->phase
= AFATFS_EXTEND_SUBDIRECTORY_PHASE_INITIAL
;
2331 opState
->parentDirectoryCluster
= parentDirectory
? parentDirectory
->firstCluster
: 0;
2332 opState
->callback
= callback
;
2334 afatfs_appendRegularFreeClusterInitOperationState(&opState
->appendFreeCluster
, directory
->cursorPreviousCluster
);
2336 return afatfs_extendSubdirectoryContinue(directory
);
2340 * Allocate space for a new directory entry to be written, store the position of that entry in the finder, and set
2341 * the *dirEntry pointer to point to the entry within the cached FAT sector. This pointer's lifetime is only as good
2342 * as the life of the cache, so don't dawdle.
2344 * Before the first call to this function, call afatfs_findFirst() on the directory.
2346 * The directory sector in the cache is marked as dirty, so any changes written through to the entry will be flushed out
2347 * in a subsequent poll cycle.
2350 * AFATFS_OPERATION_IN_PROGRESS - Call again later to continue
2351 * AFATFS_OPERATION_SUCCESS - Entry has been inserted and *dirEntry and *finder have been updated
2352 * AFATFS_OPERATION_FAILURE - When the directory is full.
2354 static afatfsOperationStatus_e
afatfs_allocateDirectoryEntry(afatfsFilePtr_t directory
, fatDirectoryEntry_t
**dirEntry
, afatfsFinder_t
*finder
)
2356 afatfsOperationStatus_e result
;
2358 if (afatfs_fileIsBusy(directory
)) {
2359 return AFATFS_OPERATION_IN_PROGRESS
;
2362 while ((result
= afatfs_findNext(directory
, finder
, dirEntry
)) == AFATFS_OPERATION_SUCCESS
) {
2364 if (fat_isDirectoryEntryEmpty(*dirEntry
) || fat_isDirectoryEntryTerminator(*dirEntry
)) {
2365 afatfs_cacheSectorMarkDirty(afatfs_getCacheDescriptorForBuffer((uint8_t*) *dirEntry
));
2367 afatfs_findLast(directory
);
2368 return AFATFS_OPERATION_SUCCESS
;
2371 // Need to extend directory size by adding a cluster
2372 result
= afatfs_extendSubdirectory(directory
, NULL
, NULL
);
2374 if (result
== AFATFS_OPERATION_SUCCESS
) {
2375 // Continue the search in the newly-extended directory
2378 // The status (in progress or failure) of extending the directory becomes our status
2388 * Return a pointer to a free entry in the open files table (a file whose type is "NONE"). You should initialize
2389 * the file afterwards with afatfs_initFileHandle().
2391 static afatfsFilePtr_t
afatfs_allocateFileHandle(void)
2393 for (int i
= 0; i
< AFATFS_MAX_OPEN_FILES
; i
++) {
2394 if (afatfs
.openFiles
[i
].type
== AFATFS_FILE_TYPE_NONE
) {
2395 return &afatfs
.openFiles
[i
];
2403 * Continue the file truncation.
2405 * When truncation finally succeeds or fails, the current operation is cleared on the file (if the current operation
2406 * was a truncate), then the truncate operation's callback is called. This allows the truncation to be called as a
2407 * sub-operation without it clearing the parent file operation.
2409 static afatfsOperationStatus_e
afatfs_ftruncateContinue(afatfsFilePtr_t file
, bool markDeleted
)
2411 afatfsTruncateFile_t
*opState
= &file
->operation
.state
.truncateFile
;
2412 afatfsOperationStatus_e status
;
2414 #ifdef AFATFS_USE_FREEFILE
2415 uint32_t oldFreeFileStart
, freeFileGrow
;
2420 switch (opState
->phase
) {
2421 case AFATFS_TRUNCATE_FILE_UPDATE_DIRECTORY
:
2422 status
= afatfs_saveDirectoryEntry(file
, markDeleted
? AFATFS_SAVE_DIRECTORY_DELETED
: AFATFS_SAVE_DIRECTORY_NORMAL
);
2424 if (status
== AFATFS_OPERATION_SUCCESS
) {
2425 #ifdef AFATFS_USE_FREEFILE
2426 if (opState
->endCluster
) {
2427 opState
->phase
= AFATFS_TRUNCATE_FILE_ERASE_FAT_CHAIN_CONTIGUOUS
;
2431 opState
->phase
= AFATFS_TRUNCATE_FILE_ERASE_FAT_CHAIN_NORMAL
;
2436 #ifdef AFATFS_USE_FREEFILE
2437 case AFATFS_TRUNCATE_FILE_ERASE_FAT_CHAIN_CONTIGUOUS
:
2438 // Prepare the clusters to be added back on to the beginning of the freefile
2439 status
= afatfs_FATFillWithPattern(AFATFS_FAT_PATTERN_UNTERMINATED_CHAIN
, &opState
->currentCluster
, opState
->endCluster
);
2441 if (status
== AFATFS_OPERATION_SUCCESS
) {
2442 opState
->phase
= AFATFS_TRUNCATE_FILE_PREPEND_TO_FREEFILE
;
2446 case AFATFS_TRUNCATE_FILE_PREPEND_TO_FREEFILE
:
2447 // Note, it's okay to run this code several times:
2448 oldFreeFileStart
= afatfs
.freeFile
.firstCluster
;
2450 afatfs
.freeFile
.firstCluster
= opState
->startCluster
;
2452 freeFileGrow
= (oldFreeFileStart
- opState
->startCluster
) * afatfs_clusterSize();
2454 afatfs
.freeFile
.logicalSize
+= freeFileGrow
;
2455 afatfs
.freeFile
.physicalSize
+= freeFileGrow
;
2457 status
= afatfs_saveDirectoryEntry(&afatfs
.freeFile
, AFATFS_SAVE_DIRECTORY_NORMAL
);
2458 if (status
== AFATFS_OPERATION_SUCCESS
) {
2459 opState
->phase
= AFATFS_TRUNCATE_FILE_SUCCESS
;
2464 case AFATFS_TRUNCATE_FILE_ERASE_FAT_CHAIN_NORMAL
:
2465 while (!afatfs_FATIsEndOfChainMarker(opState
->currentCluster
)) {
2466 uint32_t nextCluster
;
2468 status
= afatfs_FATGetNextCluster(0, opState
->currentCluster
, &nextCluster
);
2470 if (status
!= AFATFS_OPERATION_SUCCESS
) {
2474 status
= afatfs_FATSetNextCluster(opState
->currentCluster
, 0);
2476 if (status
!= AFATFS_OPERATION_SUCCESS
) {
2480 opState
->currentCluster
= nextCluster
;
2482 // Searches for unallocated regular clusters should be told about this free cluster now
2483 afatfs
.lastClusterAllocated
= MIN(afatfs
.lastClusterAllocated
, opState
->currentCluster
- 1);
2486 opState
->phase
= AFATFS_TRUNCATE_FILE_SUCCESS
;
2489 case AFATFS_TRUNCATE_FILE_SUCCESS
:
2490 if (file
->operation
.operation
== AFATFS_FILE_OPERATION_TRUNCATE
) {
2491 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2494 if (opState
->callback
) {
2495 opState
->callback(file
);
2498 return AFATFS_OPERATION_SUCCESS
;
2501 status
= AFATFS_OPERATION_FAILURE
;
2504 if (status
== AFATFS_OPERATION_FAILURE
&& file
->operation
.operation
== AFATFS_FILE_OPERATION_TRUNCATE
) {
2505 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2512 * Queue an operation to truncate the file to zero bytes in length.
2514 * Returns true if the operation was successfully queued or false if the file is busy (try again later).
2516 * The callback is called once the file has been truncated (some time after this routine returns).
2518 bool afatfs_ftruncate(afatfsFilePtr_t file
, afatfsFileCallback_t callback
)
2520 afatfsTruncateFile_t
*opState
;
2522 if (afatfs_fileIsBusy(file
))
2525 file
->operation
.operation
= AFATFS_FILE_OPERATION_TRUNCATE
;
2527 opState
= &file
->operation
.state
.truncateFile
;
2528 opState
->callback
= callback
;
2529 opState
->phase
= AFATFS_TRUNCATE_FILE_INITIAL
;
2530 opState
->startCluster
= file
->firstCluster
;
2531 opState
->currentCluster
= opState
->startCluster
;
2533 #ifdef AFATFS_USE_FREEFILE
2534 if ((file
->mode
& AFATFS_FILE_MODE_CONTIGUOUS
) != 0) {
2535 // The file is contiguous and ends where the freefile begins
2536 opState
->endCluster
= afatfs
.freeFile
.firstCluster
;
2540 // The range of clusters to delete is not contiguous, so follow it as a linked-list instead
2541 opState
->endCluster
= 0;
2544 // We'll drop the cluster chain from the directory entry immediately
2545 file
->firstCluster
= 0;
2546 file
->logicalSize
= 0;
2547 file
->physicalSize
= 0;
2549 afatfs_fseek(file
, 0, AFATFS_SEEK_SET
);
2555 * Load details from the given FAT directory entry into the file.
2557 static void afatfs_fileLoadDirectoryEntry(afatfsFile_t
*file
, fatDirectoryEntry_t
*entry
)
2559 file
->firstCluster
= (uint32_t) (entry
->firstClusterHigh
<< 16) | entry
->firstClusterLow
;
2560 file
->logicalSize
= entry
->fileSize
;
2561 file
->physicalSize
= roundUpTo(entry
->fileSize
, afatfs_clusterSize());
2562 file
->attrib
= entry
->attrib
;
2565 static void afatfs_createFileContinue(afatfsFile_t
*file
)
2567 afatfsCreateFile_t
*opState
= &file
->operation
.state
.createFile
;
2568 fatDirectoryEntry_t
*entry
;
2569 afatfsOperationStatus_e status
;
2576 switch (opState
->phase
) {
2577 case AFATFS_CREATEFILE_PHASE_INITIAL
:
2578 afatfs_findFirst(&afatfs
.currentDirectory
, &file
->directoryEntryPos
);
2579 opState
->phase
= AFATFS_CREATEFILE_PHASE_FIND_FILE
;
2582 case AFATFS_CREATEFILE_PHASE_FIND_FILE
:
2584 status
= afatfs_findNext(&afatfs
.currentDirectory
, &file
->directoryEntryPos
, &entry
);
2587 case AFATFS_OPERATION_SUCCESS
:
2588 // Is this the last entry in the directory?
2589 if (entry
== NULL
|| fat_isDirectoryEntryTerminator(entry
)) {
2590 afatfs_findLast(&afatfs
.currentDirectory
);
2592 if ((file
->mode
& AFATFS_FILE_MODE_CREATE
) != 0) {
2593 // The file didn't already exist, so we can create it. Allocate a new directory entry
2594 afatfs_findFirst(&afatfs
.currentDirectory
, &file
->directoryEntryPos
);
2596 opState
->phase
= AFATFS_CREATEFILE_PHASE_CREATE_NEW_FILE
;
2601 opState
->phase
= AFATFS_CREATEFILE_PHASE_FAILURE
;
2604 } else if (entry
->attrib
& FAT_FILE_ATTRIBUTE_VOLUME_ID
) {
2606 } else if (strncmp(entry
->filename
, (char*) opState
->filename
, FAT_FILENAME_LENGTH
) == 0) {
2607 // We found a file or directory with this name!
2609 // Do not open file as dir or dir as file
2610 if (((entry
->attrib
^ file
->attrib
) & FAT_FILE_ATTRIBUTE_DIRECTORY
) != 0) {
2611 afatfs_findLast(&afatfs
.currentDirectory
);
2612 opState
->phase
= AFATFS_CREATEFILE_PHASE_FAILURE
;
2616 afatfs_fileLoadDirectoryEntry(file
, entry
);
2618 afatfs_findLast(&afatfs
.currentDirectory
);
2620 opState
->phase
= AFATFS_CREATEFILE_PHASE_SUCCESS
;
2622 } // Else this entry doesn't match, fall through and continue the search
2624 case AFATFS_OPERATION_FAILURE
:
2625 afatfs_findLast(&afatfs
.currentDirectory
);
2626 opState
->phase
= AFATFS_CREATEFILE_PHASE_FAILURE
;
2629 case AFATFS_OPERATION_IN_PROGRESS
:
2632 } while (status
== AFATFS_OPERATION_SUCCESS
);
2634 case AFATFS_CREATEFILE_PHASE_CREATE_NEW_FILE
:
2635 status
= afatfs_allocateDirectoryEntry(&afatfs
.currentDirectory
, &entry
, &file
->directoryEntryPos
);
2637 if (status
== AFATFS_OPERATION_SUCCESS
) {
2638 memset(entry
, 0, sizeof(*entry
));
2640 memcpy(entry
->filename
, opState
->filename
, FAT_FILENAME_LENGTH
);
2641 entry
->attrib
= file
->attrib
;
2643 if (rtcGetDateTimeLocal(&now
)) {
2644 entry
->creationDate
= FAT_MAKE_DATE(now
.year
, now
.month
, now
.day
);
2645 entry
->creationTime
= FAT_MAKE_TIME(now
.hours
, now
.minutes
, now
.seconds
);
2648 entry
->creationDate
= AFATFS_DEFAULT_FILE_DATE
;
2649 entry
->creationTime
= AFATFS_DEFAULT_FILE_TIME
;
2653 entry
->lastWriteDate
= entry
->creationDate
;
2654 entry
->lastWriteTime
= entry
->creationTime
;
2656 #ifdef AFATFS_DEBUG_VERBOSE
2657 fprintf(stderr
, "Adding directory entry for %.*s to sector %u\n", FAT_FILENAME_LENGTH
, opState
->filename
, file
->directoryEntryPos
.sectorNumberPhysical
);
2660 opState
->phase
= AFATFS_CREATEFILE_PHASE_SUCCESS
;
2662 } else if (status
== AFATFS_OPERATION_FAILURE
) {
2663 opState
->phase
= AFATFS_CREATEFILE_PHASE_FAILURE
;
2667 case AFATFS_CREATEFILE_PHASE_SUCCESS
:
2668 if ((file
->mode
& AFATFS_FILE_MODE_RETAIN_DIRECTORY
) != 0) {
2670 * For this high performance file type, we require the directory entry for the file to be retained
2671 * in the cache at all times.
2673 uint8_t *directorySector
;
2675 status
= afatfs_cacheSector(
2676 file
->directoryEntryPos
.sectorNumberPhysical
,
2678 AFATFS_CACHE_READ
| AFATFS_CACHE_RETAIN
,
2682 if (status
!= AFATFS_OPERATION_SUCCESS
) {
2688 afatfs_fseek(file
, 0, AFATFS_SEEK_SET
);
2691 if (file
->cursorCluster
== 0) {
2692 #ifdef AFATFS_USE_FREEFILE
2693 if ((file
->mode
& AFATFS_FILE_MODE_CONTIGUOUS
) != 0) {
2694 if (afatfs_fileIsBusy(&afatfs
.freeFile
)) {
2695 // Someone else's using the freefile, come back later.
2698 // Lock the freefile for our exclusive access
2699 afatfs
.freeFile
.operation
.operation
= AFATFS_FILE_OPERATION_LOCKED
;
2704 // We can't guarantee that the existing file contents are contiguous
2705 file
->mode
&= ~AFATFS_FILE_MODE_CONTIGUOUS
;
2707 // Seek to the end of the file if it is in append mode
2708 if ((file
->mode
& AFATFS_FILE_MODE_APPEND
) != 0) {
2709 // This replaces our open file operation
2710 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2711 afatfs_fseekInternal(file
, file
->logicalSize
, opState
->callback
);
2715 // If we're only writing (not reading) the file must be truncated
2716 if (file
->mode
== (AFATFS_FILE_MODE_CREATE
| AFATFS_FILE_MODE_WRITE
)) {
2717 // This replaces our open file operation
2718 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2719 afatfs_ftruncate(file
, opState
->callback
);
2724 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2725 opState
->callback(file
);
2727 case AFATFS_CREATEFILE_PHASE_FAILURE
:
2728 file
->type
= AFATFS_FILE_TYPE_NONE
;
2729 opState
->callback(NULL
);
2735 * Reset the in-memory data for the given handle back to the zeroed initial state
2737 static void afatfs_initFileHandle(afatfsFilePtr_t file
)
2739 memset(file
, 0, sizeof(*file
));
2740 file
->writeLockedCacheIndex
= -1;
2741 file
->readRetainCacheIndex
= -1;
2744 static void afatfs_funlinkContinue(afatfsFilePtr_t file
)
2746 afatfsUnlinkFile_t
*opState
= &file
->operation
.state
.unlinkFile
;
2747 afatfsOperationStatus_e status
;
2749 status
= afatfs_ftruncateContinue(file
, true);
2751 if (status
== AFATFS_OPERATION_SUCCESS
) {
2752 // Once the truncation is completed, we can close the file handle
2753 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2754 afatfs_fclose(file
, opState
->callback
);
2759 * Delete and close the file.
2761 * Returns true if the operation was successfully queued (callback will be called some time after this routine returns)
2762 * or false if the file is busy and you should try again later.
2764 bool afatfs_funlink(afatfsFilePtr_t file
, afatfsCallback_t callback
)
2766 afatfsUnlinkFile_t
*opState
= &file
->operation
.state
.unlinkFile
;
2768 if (!file
|| file
->type
== AFATFS_FILE_TYPE_NONE
) {
2773 * Internally an unlink is implemented by first doing a ftruncate(), marking the directory entry as deleted,
2774 * then doing a fclose() operation.
2777 // Start the sub-operation of truncating the file
2778 if (!afatfs_ftruncate(file
, NULL
))
2782 * The unlink operation has its own private callback field so that the truncate suboperation doesn't end up
2783 * calling back early when it completes:
2785 opState
->callback
= callback
;
2787 file
->operation
.operation
= AFATFS_FILE_OPERATION_UNLINK
;
2793 * Open (or create) a file in the CWD with the given filename.
2795 * file - Memory location to store the newly opened file details
2796 * name - Filename in "name.ext" format. No path.
2797 * attrib - FAT file attributes to give the file (if created)
2798 * fileMode - Bitset of AFATFS_FILE_MODE_* constants. Include AFATFS_FILE_MODE_CREATE to create the file if
2799 * it does not exist.
2800 * callback - Called when the operation is complete
2802 static afatfsFilePtr_t
afatfs_createFile(afatfsFilePtr_t file
, const char *name
, uint8_t attrib
, uint8_t fileMode
,
2803 afatfsFileCallback_t callback
)
2805 afatfsCreateFile_t
*opState
= &file
->operation
.state
.createFile
;
2807 afatfs_initFileHandle(file
);
2809 // Queue the operation to finish the file creation
2810 file
->operation
.operation
= AFATFS_FILE_OPERATION_CREATE_FILE
;
2812 file
->mode
= fileMode
;
2814 if (strcmp(name
, ".") == 0) {
2815 file
->firstCluster
= afatfs
.currentDirectory
.firstCluster
;
2816 file
->physicalSize
= afatfs
.currentDirectory
.physicalSize
;
2817 file
->logicalSize
= afatfs
.currentDirectory
.logicalSize
;
2818 file
->attrib
= afatfs
.currentDirectory
.attrib
;
2819 file
->type
= afatfs
.currentDirectory
.type
;
2821 fat_convertFilenameToFATStyle(name
, opState
->filename
);
2822 file
->attrib
= attrib
;
2824 if ((attrib
& FAT_FILE_ATTRIBUTE_DIRECTORY
) != 0) {
2825 file
->type
= AFATFS_FILE_TYPE_DIRECTORY
;
2827 file
->type
= AFATFS_FILE_TYPE_NORMAL
;
2831 opState
->callback
= callback
;
2833 if (strcmp(name
, ".") == 0) {
2834 // Since we already have the directory entry details, we can skip straight to the final operations requried
2835 opState
->phase
= AFATFS_CREATEFILE_PHASE_SUCCESS
;
2837 opState
->phase
= AFATFS_CREATEFILE_PHASE_INITIAL
;
2840 afatfs_createFileContinue(file
);
2845 static void afatfs_fcloseContinue(afatfsFilePtr_t file
)
2847 afatfsCacheBlockDescriptor_t
*descriptor
;
2848 afatfsCloseFile_t
*opState
= &file
->operation
.state
.closeFile
;
2851 * Directories don't update their parent directory entries over time, because their fileSize field in the directory
2852 * never changes (when we add the first cluster to the directory we save the directory entry at that point and it
2853 * doesn't change afterwards). So don't bother trying to save their directory entries during fclose().
2855 * Also if we only opened the file for read then we didn't change the directory entry either.
2857 if (file
->type
!= AFATFS_FILE_TYPE_DIRECTORY
&& file
->type
!= AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY
2858 && (file
->mode
& (AFATFS_FILE_MODE_APPEND
| AFATFS_FILE_MODE_WRITE
)) != 0) {
2859 if (afatfs_saveDirectoryEntry(file
, AFATFS_SAVE_DIRECTORY_FOR_CLOSE
) != AFATFS_OPERATION_SUCCESS
) {
2864 // Release our reservation on the directory cache if needed
2865 if ((file
->mode
& AFATFS_FILE_MODE_RETAIN_DIRECTORY
) != 0) {
2866 descriptor
= afatfs_findCacheSector(file
->directoryEntryPos
.sectorNumberPhysical
);
2869 descriptor
->retainCount
= MAX((int) descriptor
->retainCount
- 1, 0);
2873 // Release locks on the sector at the file cursor position
2874 afatfs_fileUnlockCacheSector(file
);
2876 #ifdef AFATFS_USE_FREEFILE
2877 // Release our exclusive lock on the freefile if needed
2878 if ((file
->mode
& AFATFS_FILE_MODE_CONTIGUOUS
) != 0) {
2879 afatfs_assert(afatfs
.freeFile
.operation
.operation
== AFATFS_FILE_OPERATION_LOCKED
);
2880 afatfs
.freeFile
.operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2884 file
->type
= AFATFS_FILE_TYPE_NONE
;
2885 file
->operation
.operation
= AFATFS_FILE_OPERATION_NONE
;
2887 if (opState
->callback
) {
2888 opState
->callback();
2893 * Returns true if an operation was successfully queued to close the file and destroy the file handle. If the file is
2894 * currently busy, false is returned and you should retry later.
2896 * If provided, the callback will be called after the operation completes (pass NULL for no callback).
2898 * If this function returns true, you should not make any further calls to the file (as the handle might be reused for a
2901 bool afatfs_fclose(afatfsFilePtr_t file
, afatfsCallback_t callback
)
2903 if (!file
|| file
->type
== AFATFS_FILE_TYPE_NONE
) {
2905 } else if (afatfs_fileIsBusy(file
)) {
2908 afatfs_fileUpdateFilesize(file
);
2910 file
->operation
.operation
= AFATFS_FILE_OPERATION_CLOSE
;
2911 file
->operation
.state
.closeFile
.callback
= callback
;
2912 afatfs_fcloseContinue(file
);
2918 * Close `file` immediately
2920 void afatfs_fcloseSync(afatfsFilePtr_t file
)
2922 for(unsigned i
= 0; i
< 1000; ++i
) {
2925 afatfs_fclose(file
, NULL
);
2926 for(unsigned i
= 0; i
< 1000; ++i
) {
2932 * Create a new directory with the given name, or open the directory if it already exists.
2934 * The directory will be passed to the callback, or NULL if the creation failed.
2936 * Returns true if the directory creation was begun, or false if there are too many open files.
2938 bool afatfs_mkdir(const char *filename
, afatfsFileCallback_t callback
)
2940 afatfsFilePtr_t file
= afatfs_allocateFileHandle();
2943 afatfs_createFile(file
, filename
, FAT_FILE_ATTRIBUTE_DIRECTORY
, AFATFS_FILE_MODE_CREATE
| AFATFS_FILE_MODE_READ
| AFATFS_FILE_MODE_WRITE
, callback
);
2944 } else if (callback
) {
2948 return file
!= NULL
;
2952 * Change the working directory to the directory with the given handle (use fopen). Pass NULL for `directory` in order to
2953 * change to the root directory.
2955 * Returns true on success, false if you should call again later to retry. After changing into a directory, your handle
2956 * to that directory may be closed by fclose().
2958 bool afatfs_chdir(afatfsFilePtr_t directory
)
2960 if (afatfs_fileIsBusy(&afatfs
.currentDirectory
)) {
2965 if (afatfs_fileIsBusy(directory
)) {
2969 memcpy(&afatfs
.currentDirectory
, directory
, sizeof(*directory
));
2972 afatfs_initFileHandle(&afatfs
.currentDirectory
);
2974 afatfs
.currentDirectory
.mode
= AFATFS_FILE_MODE_READ
| AFATFS_FILE_MODE_WRITE
;
2976 if (afatfs
.filesystemType
== FAT_FILESYSTEM_TYPE_FAT16
)
2977 afatfs
.currentDirectory
.type
= AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY
;
2979 afatfs
.currentDirectory
.type
= AFATFS_FILE_TYPE_DIRECTORY
;
2981 afatfs
.currentDirectory
.firstCluster
= afatfs
.rootDirectoryCluster
;
2982 afatfs
.currentDirectory
.attrib
= FAT_FILE_ATTRIBUTE_DIRECTORY
;
2984 // Root directories don't have a directory entry to represent themselves:
2985 afatfs
.currentDirectory
.directoryEntryPos
.sectorNumberPhysical
= 0;
2987 afatfs_fseek(&afatfs
.currentDirectory
, 0, AFATFS_SEEK_SET
);
2994 * Begin the process of opening a file with the given name in the current working directory (paths in the filename are
2995 * not supported) using the given mode.
2997 * To open the current working directory, pass "." for filename.
2999 * The complete() callback is called when finished with either a file handle (file was opened) or NULL upon failure.
3001 * Supported file mode strings:
3003 * r - Read from an existing file
3004 * w - Create a file for write access, if the file already exists then truncate it
3005 * a - Create a file for write access to the end of the file only, if the file already exists then append to it
3007 * r+ - Read and write from an existing file
3008 * w+ - Read and write from an existing file, if the file doesn't already exist it is created
3009 * a+ - Read from or append to an existing file, if the file doesn't already exist it is created TODO
3011 * as - Create a new file which is stored contiguously on disk (high performance mode/freefile) for append or write
3012 * ws If the file is already non-empty or freefile support is not compiled in then it will fall back to non-contiguous
3015 * All other mode strings are illegal. In particular, don't add "b" to the end of the mode string.
3017 * Returns false if the the open failed really early (out of file handles).
3019 bool afatfs_fopen(const char *filename
, const char *mode
, afatfsFileCallback_t complete
)
3021 uint8_t fileMode
= 0;
3022 afatfsFilePtr_t file
;
3026 fileMode
= AFATFS_FILE_MODE_READ
;
3029 fileMode
= AFATFS_FILE_MODE_WRITE
| AFATFS_FILE_MODE_CREATE
;
3032 fileMode
= AFATFS_FILE_MODE_APPEND
| AFATFS_FILE_MODE_CREATE
;
3038 fileMode
|= AFATFS_FILE_MODE_READ
;
3040 if (fileMode
== AFATFS_FILE_MODE_READ
) {
3041 fileMode
|= AFATFS_FILE_MODE_WRITE
;
3045 #ifdef AFATFS_USE_FREEFILE
3046 fileMode
|= AFATFS_FILE_MODE_CONTIGUOUS
| AFATFS_FILE_MODE_RETAIN_DIRECTORY
;
3051 file
= afatfs_allocateFileHandle();
3054 afatfs_createFile(file
, filename
, FAT_FILE_ATTRIBUTE_ARCHIVE
, fileMode
, complete
);
3055 } else if (complete
) {
3059 return file
!= NULL
;
3062 uint32_t afatfs_fileSize(afatfsFilePtr_t file
)
3064 return file
->logicalSize
;
3068 * Write a single character to the file at the current cursor position. If the cache is too busy to accept the write,
3069 * it is silently dropped.
3071 void afatfs_fputc(afatfsFilePtr_t file
, uint8_t c
)
3073 uint32_t cursorOffsetInSector
= file
->cursorOffset
% AFATFS_SECTOR_SIZE
;
3075 int cacheIndex
= file
->writeLockedCacheIndex
;
3077 /* If we've already locked the current sector in the cache, and we won't be completing the sector, we won't
3078 * be caching/uncaching/seeking, so we can just run this simpler fast case.
3080 if (cacheIndex
!= -1 && cursorOffsetInSector
!= AFATFS_SECTOR_SIZE
- 1) {
3081 afatfs_cacheSectorGetMemory(cacheIndex
)[cursorOffsetInSector
] = c
;
3082 file
->cursorOffset
++;
3085 afatfs_fwrite(file
, &c
, sizeof(c
));
3090 * Attempt to write `len` bytes from `buffer` into the `file`.
3092 * Returns the number of bytes actually written.
3094 * 0 will be returned when:
3095 * The filesystem is busy (try again later)
3097 * Fewer bytes will be written than requested when:
3098 * The write spanned a sector boundary and the next sector's contents/location was not yet available in the cache.
3099 * Or you tried to extend the length of the file but the filesystem is full (check afatfs_isFull()).
3101 uint32_t afatfs_fwrite(afatfsFilePtr_t file
, const uint8_t *buffer
, uint32_t len
)
3103 if ((file
->mode
& (AFATFS_FILE_MODE_APPEND
| AFATFS_FILE_MODE_WRITE
)) == 0) {
3107 if (afatfs_fileIsBusy(file
)) {
3108 // There might be a seek pending
3112 uint32_t cursorOffsetInSector
= file
->cursorOffset
% AFATFS_SECTOR_SIZE
;
3113 uint32_t writtenBytes
= 0;
3116 uint32_t bytesToWriteThisSector
= MIN(AFATFS_SECTOR_SIZE
- cursorOffsetInSector
, len
);
3117 uint8_t *sectorBuffer
;
3119 sectorBuffer
= afatfs_fileLockCursorSectorForWrite(file
);
3120 if (!sectorBuffer
) {
3121 // Cache is currently busy
3125 memcpy(sectorBuffer
+ cursorOffsetInSector
, buffer
, bytesToWriteThisSector
);
3127 writtenBytes
+= bytesToWriteThisSector
;
3130 * If the seek doesn't complete immediately then we'll break and wait for that seek to complete by waiting for
3131 * the file to be non-busy on entry again.
3133 * A seek operation should always be able to queue on the file since we have checked that the file wasn't busy
3134 * on entry (fseek will never return AFATFS_OPERATION_FAILURE).
3136 * If the seek has to queue, when the seek completes, it'll update the fileSize for us to contain the cursor.
3138 if (afatfs_fseekInternal(file
, bytesToWriteThisSector
, NULL
) == AFATFS_OPERATION_IN_PROGRESS
) {
3142 #ifdef AFATFS_USE_FREEFILE
3143 if ((file
->mode
& AFATFS_FILE_MODE_CONTIGUOUS
) != 0) {
3144 afatfs_assert(file
->cursorCluster
< afatfs
.freeFile
.firstCluster
);
3148 len
-= bytesToWriteThisSector
;
3149 buffer
+= bytesToWriteThisSector
;
3150 cursorOffsetInSector
= 0;
3153 return writtenBytes
;
3157 * Attempt to write `len` bytes from `buffer` into the `file`.
3159 * Returns the number of bytes actually written
3161 * Fewer bytes than requested will be read when EOF is reached before all the bytes could be read
3163 uint32_t afatfs_fwriteSync(afatfsFilePtr_t file
, uint8_t *data
, uint32_t length
)
3165 uint32_t written
= 0;
3168 uint32_t leftToWrite
= length
- written
;
3169 uint32_t justWritten
= afatfs_fwrite(file
, data
+ written
, leftToWrite
);
3170 /*if (justWritten != leftToWrite) LOG_ERROR(SYSTEM, "%ld -> %ld", length, written);*/
3171 written
+= justWritten
;
3173 if (written
< length
) {
3175 if (afatfs_isFull()) {
3190 * Attempt to read `len` bytes from `file` into the `buffer`.
3192 * Returns the number of bytes actually read.
3194 * 0 will be returned when:
3195 * The filesystem is busy (try again later)
3196 * EOF was reached (check afatfs_isEof())
3198 * Fewer bytes than requested will be read when:
3199 * The read spans a AFATFS_SECTOR_SIZE boundary and the following sector was not available in the cache yet.
3201 uint32_t afatfs_fread(afatfsFilePtr_t file
, uint8_t *buffer
, uint32_t len
)
3203 if ((file
->mode
& AFATFS_FILE_MODE_READ
) == 0) {
3207 if (afatfs_fileIsBusy(file
)) {
3208 // There might be a seek pending
3213 * If we've just previously fwritten() to extend the file, the logical filesize will be out of date and the cursor
3214 * will appear to be beyond the end of the file (but actually it's precisely at the end of the file, because if
3215 * we had seeked backwards to where we could read something with fseek(), we would have updated the filesize).
3217 if (file
->cursorOffset
>= file
->logicalSize
)
3220 len
= MIN(file
->logicalSize
- file
->cursorOffset
, len
);
3222 uint32_t readBytes
= 0;
3223 uint32_t cursorOffsetInSector
= file
->cursorOffset
% AFATFS_SECTOR_SIZE
;
3226 uint32_t bytesToReadThisSector
= MIN(AFATFS_SECTOR_SIZE
- cursorOffsetInSector
, len
);
3227 uint8_t *sectorBuffer
;
3229 sectorBuffer
= afatfs_fileRetainCursorSectorForRead(file
);
3230 if (!sectorBuffer
) {
3231 // Cache is currently busy
3235 memcpy(buffer
, sectorBuffer
+ cursorOffsetInSector
, bytesToReadThisSector
);
3237 readBytes
+= bytesToReadThisSector
;
3240 * If the seek doesn't complete immediately then we'll break and wait for that seek to complete by waiting for
3241 * the file to be non-busy on entry again.
3243 * A seek operation should always be able to queue on the file since we have checked that the file wasn't busy
3244 * on entry (fseek will never return AFATFS_OPERATION_FAILURE).
3246 if (afatfs_fseekInternal(file
, bytesToReadThisSector
, NULL
) == AFATFS_OPERATION_IN_PROGRESS
) {
3250 len
-= bytesToReadThisSector
;
3251 buffer
+= bytesToReadThisSector
;
3252 cursorOffsetInSector
= 0;
3259 * Attempt to read `len` bytes from `file` into the `buffer`.
3261 * Returns the number of bytes actually read.
3263 * Fewer bytes than requested will be read when EOF is reached before all the bytes could be read
3265 uint32_t afatfs_freadSync(afatfsFilePtr_t file
, uint8_t *buffer
, uint32_t length
)
3267 uint32_t bytesRead
= 0;
3270 uint32_t leftToRead
= length
- bytesRead
;
3271 uint32_t readNow
= afatfs_fread(file
, buffer
, leftToRead
);
3272 bytesRead
+= readNow
;
3274 if (bytesRead
< length
) {
3276 if (afatfs_feof(file
)) {
3291 * Returns true if the file's pointer position currently lies at the end-of-file point (i.e. one byte beyond the last
3292 * byte in the file).
3294 bool afatfs_feof(afatfsFilePtr_t file
)
3296 return file
->cursorOffset
>= file
->logicalSize
;
3300 * Continue any queued operations on the given file.
3302 static void afatfs_fileOperationContinue(afatfsFile_t
*file
)
3304 if (file
->type
== AFATFS_FILE_TYPE_NONE
)
3307 switch (file
->operation
.operation
) {
3308 case AFATFS_FILE_OPERATION_CREATE_FILE
:
3309 afatfs_createFileContinue(file
);
3311 case AFATFS_FILE_OPERATION_SEEK
:
3312 afatfs_fseekInternalContinue(file
);
3314 case AFATFS_FILE_OPERATION_CLOSE
:
3315 afatfs_fcloseContinue(file
);
3317 case AFATFS_FILE_OPERATION_UNLINK
:
3318 afatfs_funlinkContinue(file
);
3320 case AFATFS_FILE_OPERATION_TRUNCATE
:
3321 afatfs_ftruncateContinue(file
, false);
3323 #ifdef AFATFS_USE_FREEFILE
3324 case AFATFS_FILE_OPERATION_APPEND_SUPERCLUSTER
:
3325 afatfs_appendSuperclusterContinue(file
);
3327 case AFATFS_FILE_OPERATION_LOCKED
:
3331 case AFATFS_FILE_OPERATION_APPEND_FREE_CLUSTER
:
3332 afatfs_appendRegularFreeClusterContinue(file
);
3334 case AFATFS_FILE_OPERATION_EXTEND_SUBDIRECTORY
:
3335 afatfs_extendSubdirectoryContinue(file
);
3337 case AFATFS_FILE_OPERATION_NONE
:
3344 * Check files for pending operations and execute them.
3346 static void afatfs_fileOperationsPoll(void)
3348 afatfs_fileOperationContinue(&afatfs
.currentDirectory
);
3350 for (int i
= 0; i
< AFATFS_MAX_OPEN_FILES
; i
++) {
3351 afatfs_fileOperationContinue(&afatfs
.openFiles
[i
]);
3355 #ifdef AFATFS_USE_FREEFILE
3358 * Return the available size of the freefile (used for files in contiguous append mode)
3360 uint32_t afatfs_getContiguousFreeSpace(void)
3362 return afatfs
.freeFile
.logicalSize
;
3366 * Call to set up the initial state for finding the largest block of free space on the device whose corresponding FAT
3367 * sectors are themselves entirely free space (so the free space has dedicated FAT sectors of its own).
3369 static void afatfs_findLargestContiguousFreeBlockBegin(void)
3371 // The first FAT sector has two reserved entries, so it isn't eligible for this search. Start at the next FAT sector.
3372 afatfs
.initState
.freeSpaceSearch
.candidateStart
= afatfs_fatEntriesPerSector();
3373 afatfs
.initState
.freeSpaceSearch
.candidateEnd
= afatfs
.initState
.freeSpaceSearch
.candidateStart
;
3374 afatfs
.initState
.freeSpaceSearch
.bestGapStart
= 0;
3375 afatfs
.initState
.freeSpaceSearch
.bestGapLength
= 0;
3376 afatfs
.initState
.freeSpaceSearch
.phase
= AFATFS_FREE_SPACE_SEARCH_PHASE_FIND_HOLE
;
3380 * Call to continue the search for the largest contiguous block of free space on the device.
3383 * AFATFS_OPERATION_IN_PROGRESS - SD card is busy, call again later to resume
3384 * AFATFS_OPERATION_SUCCESS - When the search has finished and afatfs.initState.freeSpaceSearch has been updated with the details of the best gap.
3385 * AFATFS_OPERATION_FAILURE - When a read error occured
3387 static afatfsOperationStatus_e
afatfs_findLargestContiguousFreeBlockContinue(void)
3389 afatfsFreeSpaceSearch_t
*opState
= &afatfs
.initState
.freeSpaceSearch
;
3390 uint32_t fatEntriesPerSector
= afatfs_fatEntriesPerSector();
3391 uint32_t candidateGapLength
, searchLimit
;
3392 afatfsFindClusterStatus_e searchStatus
;
3395 switch (opState
->phase
) {
3396 case AFATFS_FREE_SPACE_SEARCH_PHASE_FIND_HOLE
:
3397 // Find the first free cluster
3398 switch (afatfs_findClusterWithCondition(CLUSTER_SEARCH_FREE_AT_BEGINNING_OF_FAT_SECTOR
, &opState
->candidateStart
, afatfs
.numClusters
+ FAT_SMALLEST_LEGAL_CLUSTER_NUMBER
)) {
3399 case AFATFS_FIND_CLUSTER_FOUND
:
3400 opState
->candidateEnd
= opState
->candidateStart
+ 1;
3401 opState
->phase
= AFATFS_FREE_SPACE_SEARCH_PHASE_GROW_HOLE
;
3404 case AFATFS_FIND_CLUSTER_FATAL
:
3405 // Some sort of read error occured
3406 return AFATFS_OPERATION_FAILURE
;
3408 case AFATFS_FIND_CLUSTER_NOT_FOUND
:
3409 // We finished searching the volume (didn't find any more holes to examine)
3410 return AFATFS_OPERATION_SUCCESS
;
3412 case AFATFS_FIND_CLUSTER_IN_PROGRESS
:
3413 return AFATFS_OPERATION_IN_PROGRESS
;
3416 case AFATFS_FREE_SPACE_SEARCH_PHASE_GROW_HOLE
:
3417 // Find the first used cluster after the beginning of the hole (that signals the end of the hole)
3419 // Don't search beyond the end of the volume, or such that the freefile size would exceed the max filesize
3420 searchLimit
= MIN((uint64_t) opState
->candidateStart
+ FAT_MAXIMUM_FILESIZE
/ afatfs_clusterSize(), afatfs
.numClusters
+ FAT_SMALLEST_LEGAL_CLUSTER_NUMBER
);
3422 searchStatus
= afatfs_findClusterWithCondition(CLUSTER_SEARCH_OCCUPIED
, &opState
->candidateEnd
, searchLimit
);
3424 switch (searchStatus
) {
3425 case AFATFS_FIND_CLUSTER_NOT_FOUND
:
3426 case AFATFS_FIND_CLUSTER_FOUND
:
3427 // Either we found a used sector, or the search reached the end of the volume or exceeded the max filesize
3428 candidateGapLength
= opState
->candidateEnd
- opState
->candidateStart
;
3430 if (candidateGapLength
> opState
->bestGapLength
) {
3431 opState
->bestGapStart
= opState
->candidateStart
;
3432 opState
->bestGapLength
= candidateGapLength
;
3435 if (searchStatus
== AFATFS_FIND_CLUSTER_NOT_FOUND
) {
3436 // This is the best hole there can be
3437 return AFATFS_OPERATION_SUCCESS
;
3439 // Start a new search for a new hole
3440 opState
->candidateStart
= roundUpTo(opState
->candidateEnd
+ 1, fatEntriesPerSector
);
3441 opState
->phase
= AFATFS_FREE_SPACE_SEARCH_PHASE_FIND_HOLE
;
3445 case AFATFS_FIND_CLUSTER_FATAL
:
3446 // Some sort of read error occured
3447 return AFATFS_OPERATION_FAILURE
;
3449 case AFATFS_FIND_CLUSTER_IN_PROGRESS
:
3450 return AFATFS_OPERATION_IN_PROGRESS
;
3457 static void afatfs_freeFileCreated(afatfsFile_t
*file
)
3460 // Did the freefile already have allocated space?
3461 if (file
->logicalSize
> 0) {
3462 // We've completed freefile init, move on to the next init phase
3463 afatfs
.initPhase
= AFATFS_INITIALIZATION_FREEFILE_LAST
+ 1;
3465 // Allocate clusters for the freefile
3466 afatfs_findLargestContiguousFreeBlockBegin();
3467 afatfs
.initPhase
= AFATFS_INITIALIZATION_FREEFILE_FAT_SEARCH
;
3470 // Failed to allocate an entry
3471 afatfs
.lastError
= AFATFS_ERROR_GENERIC
;
3472 afatfs
.filesystemState
= AFATFS_FILESYSTEM_STATE_FATAL
;
3478 static void afatfs_initContinue(void)
3480 #ifdef AFATFS_USE_FREEFILE
3481 afatfsOperationStatus_e status
;
3488 switch (afatfs
.initPhase
) {
3489 case AFATFS_INITIALIZATION_READ_MBR
:
3490 if (afatfs_cacheSector(0, §or
, AFATFS_CACHE_READ
| AFATFS_CACHE_DISCARDABLE
, 0) == AFATFS_OPERATION_SUCCESS
) {
3491 if (afatfs_parseMBR(sector
)) {
3492 afatfs
.initPhase
= AFATFS_INITIALIZATION_READ_VOLUME_ID
;
3495 afatfs
.lastError
= AFATFS_ERROR_BAD_MBR
;
3496 afatfs
.filesystemState
= AFATFS_FILESYSTEM_STATE_FATAL
;
3500 case AFATFS_INITIALIZATION_READ_VOLUME_ID
:
3501 if (afatfs_cacheSector(afatfs
.partitionStartSector
, §or
, AFATFS_CACHE_READ
| AFATFS_CACHE_DISCARDABLE
, 0) == AFATFS_OPERATION_SUCCESS
) {
3502 if (afatfs_parseVolumeID(sector
)) {
3503 // Open the root directory
3508 afatfs
.lastError
= AFATFS_ERROR_BAD_FILESYSTEM_HEADER
;
3509 afatfs
.filesystemState
= AFATFS_FILESYSTEM_STATE_FATAL
;
3514 #ifdef AFATFS_USE_FREEFILE
3515 case AFATFS_INITIALIZATION_FREEFILE_CREATE
:
3516 afatfs
.initPhase
= AFATFS_INITIALIZATION_FREEFILE_CREATING
;
3518 afatfs_createFile(&afatfs
.freeFile
, AFATFS_FREESPACE_FILENAME
, FAT_FILE_ATTRIBUTE_SYSTEM
| FAT_FILE_ATTRIBUTE_READ_ONLY
,
3519 AFATFS_FILE_MODE_CREATE
| AFATFS_FILE_MODE_RETAIN_DIRECTORY
, afatfs_freeFileCreated
);
3521 case AFATFS_INITIALIZATION_FREEFILE_CREATING
:
3522 afatfs_fileOperationContinue(&afatfs
.freeFile
);
3524 case AFATFS_INITIALIZATION_FREEFILE_FAT_SEARCH
:
3525 if (afatfs_findLargestContiguousFreeBlockContinue() == AFATFS_OPERATION_SUCCESS
) {
3526 // If the freefile ends up being empty then we only have to save its directory entry:
3527 afatfs
.initPhase
= AFATFS_INITIALIZATION_FREEFILE_SAVE_DIR_ENTRY
;
3529 if (afatfs
.initState
.freeSpaceSearch
.bestGapLength
> AFATFS_FREEFILE_LEAVE_CLUSTERS
+ 1) {
3530 afatfs
.initState
.freeSpaceSearch
.bestGapLength
-= AFATFS_FREEFILE_LEAVE_CLUSTERS
;
3532 /* So that the freefile never becomes empty, we want it to occupy a non-integer number of
3533 * superclusters. So its size mod the number of clusters in a supercluster should be 1.
3535 afatfs
.initState
.freeSpaceSearch
.bestGapLength
= ((afatfs
.initState
.freeSpaceSearch
.bestGapLength
- 1) & ~(afatfs_fatEntriesPerSector() - 1)) + 1;
3537 // Anything useful left over?
3538 if (afatfs
.initState
.freeSpaceSearch
.bestGapLength
> afatfs_fatEntriesPerSector()) {
3539 uint32_t startCluster
= afatfs
.initState
.freeSpaceSearch
.bestGapStart
;
3540 // Points 1-beyond the final cluster of the freefile:
3541 uint32_t endCluster
= afatfs
.initState
.freeSpaceSearch
.bestGapStart
+ afatfs
.initState
.freeSpaceSearch
.bestGapLength
;
3543 afatfs_assert(endCluster
< afatfs
.numClusters
+ FAT_SMALLEST_LEGAL_CLUSTER_NUMBER
);
3545 afatfs
.initState
.freeSpaceFAT
.startCluster
= startCluster
;
3546 afatfs
.initState
.freeSpaceFAT
.endCluster
= endCluster
;
3548 afatfs
.freeFile
.firstCluster
= startCluster
;
3550 afatfs
.freeFile
.logicalSize
= afatfs
.initState
.freeSpaceSearch
.bestGapLength
* afatfs_clusterSize();
3551 afatfs
.freeFile
.physicalSize
= afatfs
.freeFile
.logicalSize
;
3553 // We can write the FAT table for the freefile now
3554 afatfs
.initPhase
= AFATFS_INITIALIZATION_FREEFILE_UPDATE_FAT
;
3555 } // Else the freefile's FAT chain and filesize remains the default (empty)
3561 case AFATFS_INITIALIZATION_FREEFILE_UPDATE_FAT
:
3562 status
= afatfs_FATFillWithPattern(AFATFS_FAT_PATTERN_TERMINATED_CHAIN
, &afatfs
.initState
.freeSpaceFAT
.startCluster
, afatfs
.initState
.freeSpaceFAT
.endCluster
);
3564 if (status
== AFATFS_OPERATION_SUCCESS
) {
3565 afatfs
.initPhase
= AFATFS_INITIALIZATION_FREEFILE_SAVE_DIR_ENTRY
;
3568 } else if (status
== AFATFS_OPERATION_FAILURE
) {
3569 afatfs
.lastError
= AFATFS_ERROR_GENERIC
;
3570 afatfs
.filesystemState
= AFATFS_FILESYSTEM_STATE_FATAL
;
3573 case AFATFS_INITIALIZATION_FREEFILE_SAVE_DIR_ENTRY
:
3574 status
= afatfs_saveDirectoryEntry(&afatfs
.freeFile
, AFATFS_SAVE_DIRECTORY_NORMAL
);
3576 if (status
== AFATFS_OPERATION_SUCCESS
) {
3579 } else if (status
== AFATFS_OPERATION_FAILURE
) {
3580 afatfs
.lastError
= AFATFS_ERROR_GENERIC
;
3581 afatfs
.filesystemState
= AFATFS_FILESYSTEM_STATE_FATAL
;
3586 case AFATFS_INITIALIZATION_DONE
:
3587 afatfs
.filesystemState
= AFATFS_FILESYSTEM_STATE_READY
;
3593 * Check to see if there are any pending operations on the filesystem and perform a little work (without waiting on the
3594 * sdcard). You must call this periodically.
3596 void afatfs_poll(void)
3598 // Only attempt to continue FS operations if the card is present & ready, otherwise we would just be wasting time
3599 if (sdcard_poll()) {
3602 switch (afatfs
.filesystemState
) {
3603 case AFATFS_FILESYSTEM_STATE_INITIALIZATION
:
3604 afatfs_initContinue();
3606 case AFATFS_FILESYSTEM_STATE_READY
:
3607 afatfs_fileOperationsPoll();
3615 afatfsFilesystemState_e
afatfs_getFilesystemState(void)
3617 return afatfs
.filesystemState
;
3620 afatfsError_e
afatfs_getLastError(void)
3622 return afatfs
.lastError
;
3625 void afatfs_init(void)
3628 afatfs
.cache
= afatfs_cache
;
3630 afatfs
.filesystemState
= AFATFS_FILESYSTEM_STATE_INITIALIZATION
;
3631 afatfs
.initPhase
= AFATFS_INITIALIZATION_READ_MBR
;
3632 afatfs
.lastClusterAllocated
= FAT_SMALLEST_LEGAL_CLUSTER_NUMBER
;
3636 * Shut down the filesystem, flushing all data to the disk. Keep calling until it returns true.
3638 * dirty - Set to true to skip the flush operation and terminate immediately (buffered data will be lost!)
3640 bool afatfs_destroy(bool dirty
)
3642 // Only attempt detailed cleanup if the filesystem is in reasonable looking state
3643 if (!dirty
&& afatfs
.filesystemState
== AFATFS_FILESYSTEM_STATE_READY
) {
3644 int openFileCount
= 0;
3646 for (int i
= 0; i
< AFATFS_MAX_OPEN_FILES
; i
++) {
3647 if (afatfs
.openFiles
[i
].type
!= AFATFS_FILE_TYPE_NONE
) {
3648 afatfs_fclose(&afatfs
.openFiles
[i
], NULL
);
3649 // The close operation might not finish right away, so count this file as still open for now
3654 #ifdef AFATFS_USE_FREEFILE
3655 if (afatfs
.freeFile
.type
!= AFATFS_FILE_TYPE_NONE
) {
3656 afatfs_fclose(&afatfs
.freeFile
, NULL
);
3661 if (afatfs
.currentDirectory
.type
!= AFATFS_FILE_TYPE_NONE
) {
3662 afatfs_fclose(&afatfs
.currentDirectory
, NULL
);
3668 if (!afatfs_flush()) {
3672 if (afatfs
.cacheFlushInProgress
) {
3676 if (openFileCount
> 0) {
3681 /* All sector locks should have been released by closing the files, so the subsequent flush should have written
3682 * all dirty pages to disk. If not, something's wrong:
3684 for (int i
= 0; i
< AFATFS_NUM_CACHE_SECTORS
; i
++) {
3685 afatfs_assert(afatfs
.cacheDescriptor
[i
].state
!= AFATFS_CACHE_STATE_DIRTY
);
3690 // Clear the afatfs so it's as if we never ran
3691 memset(&afatfs
, 0, sizeof(afatfs
));
3697 * Get a pessimistic estimate of the amount of buffer space that we have available to write to immediately.
3699 uint32_t afatfs_getFreeBufferSpace(void)
3701 uint32_t result
= 0;
3702 for (int i
= 0; i
< AFATFS_NUM_CACHE_SECTORS
; i
++) {
3703 if (!afatfs
.cacheDescriptor
[i
].locked
&& (afatfs
.cacheDescriptor
[i
].state
== AFATFS_CACHE_STATE_EMPTY
|| afatfs
.cacheDescriptor
[i
].state
== AFATFS_CACHE_STATE_IN_SYNC
)) {
3704 result
+= AFATFS_SECTOR_SIZE
;