Blackbox device type 'file' (SITL) considered working when file handler is available
[inav.git] / src / main / io / asyncfatfs / asyncfatfs.c
blob82ed7d6583095a0c20a25e279ec1a75faa3f04f6
1 /**
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
8 * files.
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.
16 #include <stdint.h>
17 #include <stdlib.h>
18 #include <string.h>
20 #include <platform.h>
22 #include "common/time.h"
23 #include "common/utils.h"
25 #ifdef AFATFS_DEBUG
26 #include <signal.h>
27 #include <stdio.h>
28 #endif
30 #include "asyncfatfs.h"
32 #include "fat_standard.h"
33 #include "drivers/sdcard/sdcard.h"
35 #ifdef AFATFS_DEBUG
36 #define ONLY_EXPOSE_FOR_TESTING
37 #else
38 #define ONLY_EXPOSE_FOR_TESTING static
39 #endif
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))
101 typedef enum {
102 AFATFS_SAVE_DIRECTORY_NORMAL,
103 AFATFS_SAVE_DIRECTORY_FOR_CLOSE,
104 AFATFS_SAVE_DIRECTORY_DELETED,
105 } afatfsSaveDirectoryEntryMode_e;
107 typedef enum {
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;
115 typedef enum {
116 AFATFS_FILE_TYPE_NONE,
117 AFATFS_FILE_TYPE_NORMAL,
118 AFATFS_FILE_TYPE_FAT16_ROOT_DIRECTORY,
119 AFATFS_FILE_TYPE_DIRECTORY
120 } afatfsFileType_e;
122 typedef enum {
123 CLUSTER_SEARCH_FREE_AT_BEGINNING_OF_FAT_SECTOR,
124 CLUSTER_SEARCH_FREE,
125 CLUSTER_SEARCH_OCCUPIED,
126 } afatfsClusterSearchCondition_e;
128 enum {
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,
136 typedef enum {
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 {
146 uint8_t *bytes;
147 uint16_t *fat16;
148 uint32_t *fat32;
149 } 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.
182 unsigned locked:1;
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;
199 typedef enum {
200 AFATFS_FAT_PATTERN_UNTERMINATED_CHAIN,
201 AFATFS_FAT_PATTERN_TERMINATED_CHAIN,
202 AFATFS_FAT_PATTERN_FREE
203 } afatfsFATPattern_e;
205 typedef enum {
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;
220 uint32_t endCluster;
221 } afatfsFreeSpaceFAT_t;
223 typedef struct afatfsCreateFile_t {
224 afatfsFileCallback_t callback;
226 uint8_t phase;
227 uint8_t filename[FAT_FILENAME_LENGTH];
228 } afatfsCreateFile_t;
230 typedef struct afatfsSeek_t {
231 afatfsFileCallback_t callback;
233 uint32_t seekOffset;
234 } afatfsSeek_t;
236 typedef enum {
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;
250 typedef enum {
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;
266 typedef enum {
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;
284 typedef enum {
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,
291 #endif
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;
303 typedef enum {
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;
315 } afatfsCloseFile_t;
317 typedef enum {
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,
327 #endif
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;
334 union {
335 afatfsCreateFile_t createFile;
336 afatfsSeek_t seek;
337 afatfsAppendSupercluster_t appendSupercluster;
338 afatfsAppendFreeCluster_t appendFreeCluster;
339 afatfsExtendSubdirectory_t extendSubdirectory;
340 afatfsUnlinkFile_t unlinkFile;
341 afatfsTruncateFile_t truncateFile;
342 afatfsCloseFile_t closeFile;
343 } state;
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
364 * the cluster size.
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
378 * set to zero.
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;
406 } afatfsFile_t;
408 typedef enum {
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,
419 #endif
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
432 union {
433 afatfsFreeSpaceSearch_t freeSpaceSearch;
434 afatfsFreeSpaceFAT_t freeSpaceFAT;
435 } initState;
436 #endif
438 #ifdef STM32H7
439 uint8_t *cache;
440 #else
441 uint8_t cache[AFATFS_SECTOR_SIZE * AFATFS_NUM_CACHE_SECTORS];
442 #endif
443 afatfsCacheBlockDescriptor_t cacheDescriptor[AFATFS_NUM_CACHE_SECTORS];
444 uint32_t cacheTimer;
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;
453 #endif
455 afatfsError_e lastError;
457 bool filesystemFull;
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
477 * cluster.
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
486 } afatfs_t;
488 #ifdef STM32H7
489 static uint8_t afatfs_cache[AFATFS_SECTOR_SIZE * AFATFS_NUM_CACHE_SECTORS] __attribute__((aligned(32)));
490 #endif
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;
502 if (remainder > 0) {
503 value += rounding - remainder;
506 return value;
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)
523 if (!condition) {
524 if (afatfs.lastError == AFATFS_ERROR_NONE) {
525 afatfs.lastError = AFATFS_ERROR_GENERIC;
527 afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_FATAL;
528 #ifdef AFATFS_DEBUG
529 raise(SIGTRAP);
530 #endif
533 return condition;
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)) {
587 return index;
588 } else {
589 return -1;
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)
627 (void) operation;
628 (void) 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;
637 } else {
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;
643 break;
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)
653 (void) operation;
654 (void) 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++;
669 } else {
670 afatfs_assert(afatfs_cacheSectorGetMemory(i) == buffer);
672 afatfs.cacheDescriptor[i].state = AFATFS_CACHE_STATE_IN_SYNC;
674 break;
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);
690 #endif
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;
698 break;
700 case SDCARD_OPERATION_SUCCESS:
701 // Buffer is already transmitted
702 afatfs.cacheDirtyEntries--;
703 cacheDescriptor->state = AFATFS_CACHE_STATE_IN_SYNC;
704 break;
706 case SDCARD_OPERATION_BUSY:
707 case SDCARD_OPERATION_FAILURE:
708 default:
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];
725 return NULL;
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)
741 int allocateIndex;
742 int emptyIndex = -1, discardableIndex = -1;
744 uint32_t oldestSyncedSectorLastUse = 0xFFFFFFFF;
745 int oldestSyncedSectorIndex = -1;
747 if (
748 !afatfs_assert(
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
753 return -1;
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) {
763 emptyIndex = i;
764 break;
767 // Bump the last access time
768 afatfs.cacheDescriptor[i].accessTimestamp = ++afatfs.cacheTimer;
769 return i;
772 switch (afatfs.cacheDescriptor[i].state) {
773 case AFATFS_CACHE_STATE_EMPTY:
774 emptyIndex = i;
775 break;
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;
787 break;
788 default:
793 if (emptyIndex > -1) {
794 allocateIndex = emptyIndex;
795 } else if (discardableIndex > -1) {
796 allocateIndex = discardableIndex;
797 } else if (oldestSyncedSectorIndex > -1) {
798 allocateIndex = oldestSyncedSectorIndex;
799 } else {
800 allocateIndex = -1;
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
833 return false;
837 return true;
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;
866 } else {
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;
881 } else {
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)
895 * Returns:
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;
917 break;
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) {
933 eraseCount = 0;
934 } else {
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;
939 #endif
940 FALLTHROUGH;
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]);
947 FALLTHROUGH;
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;
960 break;
962 default:
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)
976 return false;
978 mbrPartitionEntry_t *partition = (mbrPartitionEntry_t *) (sector + 446);
980 for (int i = 0; i < 4; i++) {
981 if (
982 partition[i].lbaBegin > 0
983 && (
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;
992 return true;
996 return false;
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) {
1007 return false;
1010 afatfs.fatStartSector = afatfs.partitionStartSector + volume->reservedSectorCount;
1012 afatfs.sectorsPerCluster = volume->sectorsPerCluster;
1013 if (afatfs.sectorsPerCluster < 1 || afatfs.sectorsPerCluster > 128 || !isPowerOfTwo(afatfs.sectorsPerCluster)) {
1014 return false;
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;
1034 } else {
1035 afatfs.filesystemType = FAT_FILESYSTEM_TYPE_FAT32;
1038 if (afatfs.filesystemType == FAT_FILESYSTEM_TYPE_FAT32) {
1039 afatfs.rootDirectoryCluster = volume->fatDescriptor.fat32.rootCluster;
1040 } else {
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;
1049 return true;
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);
1062 } else {
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);
1074 } else {
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
1083 * cluster numbers.
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).
1088 * Returns:
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), &sector.bytes, AFATFS_CACHE_READ, 0);
1101 if (result == AFATFS_OPERATION_SUCCESS) {
1102 if (afatfs.filesystemType == FAT_FILESYSTEM_TYPE_FAT16) {
1103 *nextCluster = sector.fat16[fatSectorEntryIndex];
1104 } else {
1105 *nextCluster = fat32_decodeClusterNumber(sector.fat32[fatSectorEntryIndex]);
1109 return result;
1113 * Set the cluster number that follows the given cluster. Pass 0xFFFFFFFF for nextCluster to terminate the FAT chain.
1115 * Returns:
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;
1126 #ifdef AFATFS_DEBUG
1127 afatfs_assert(startCluster >= FAT_SMALLEST_LEGAL_CLUSTER_NUMBER);
1128 #endif
1130 afatfs_getFATPositionForCluster(startCluster, &fatSectorIndex, &fatSectorEntryIndex);
1132 fatPhysicalSector = afatfs_fatSectorToPhysical(0, fatSectorIndex);
1134 result = afatfs_cacheSector(fatPhysicalSector, &sector.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;
1139 } else {
1140 sector.fat32[fatSectorEntryIndex] = nextCluster;
1144 return result;
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
1169 * condition.
1171 * searchLimit - Last cluster to examine (exclusive). To search the entire volume, pass:
1172 * afatfs.numClusters + FAT_SMALLEST_LEGAL_CLUSTER_NUMBER
1174 * Condition:
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.
1180 * Returns:
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;
1195 int jump;
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;
1208 break;
1209 case CLUSTER_SEARCH_OCCUPIED:
1210 case CLUSTER_SEARCH_FREE:
1211 jump = 1;
1212 break;
1213 default:
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
1229 #endif
1231 afatfsOperationStatus_e status = afatfs_cacheSector(afatfs_fatSectorToPhysical(0, fatSectorIndex), &sector.bytes, AFATFS_CACHE_READ | AFATFS_CACHE_DISCARDABLE, 0);
1233 switch (status) {
1234 case AFATFS_OPERATION_SUCCESS:
1235 do {
1236 uint32_t clusterNumber;
1238 switch (afatfs.filesystemType) {
1239 case FAT_FILESYSTEM_TYPE_FAT16:
1240 clusterNumber = sector.fat16[fatSectorEntryIndex];
1241 break;
1242 case FAT_FILESYSTEM_TYPE_FAT32:
1243 clusterNumber = fat32_decodeClusterNumber(sector.fat32[fatSectorEntryIndex]);
1244 break;
1245 default:
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;
1256 } else {
1257 *cluster = searchLimit;
1258 return AFATFS_FIND_CLUSTER_NOT_FOUND;
1262 (*cluster) += jump;
1263 fatSectorEntryIndex += jump;
1264 } while (fatSectorEntryIndex < fatEntriesPerSector);
1266 // Move on to the next FAT sector
1267 fatSectorIndex++;
1268 fatSectorEntryIndex = 0;
1269 break;
1270 case AFATFS_OPERATION_FAILURE:
1271 return AFATFS_FIND_CLUSTER_FATAL;
1272 break;
1273 case AFATFS_OPERATION_IN_PROGRESS:
1274 return AFATFS_FIND_CLUSTER_IN_PROGRESS;
1275 break;
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.
1287 * Returns:
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
1294 (void) file;
1295 #else
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) {
1303 *nextCluster = 0;
1304 } else {
1305 *nextCluster = currentCluster + 1;
1308 return AFATFS_OPERATION_SUCCESS;
1309 } else
1310 #endif
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
1326 * Returns -
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, &sector.bytes, cacheFlags, eraseSectorCount);
1360 if (result != AFATFS_OPERATION_SUCCESS) {
1361 return result;
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);
1367 } else {
1368 fprintf(stderr, "Writing FAT chain from cluster %u to %u in FAT sector %u...\n", *startCluster, endCluster, fatPhysicalSector);
1370 #endif
1372 switch (pattern) {
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;
1381 } else {
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;
1393 } else {
1394 sector.fat32[lastEntryIndex - 1] = 0xFFFFFFFF;
1396 break;
1398 break;
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;
1405 break;
1408 fatPhysicalSector++;
1409 eraseSectorCount--;
1410 firstEntryIndex = 0;
1413 return AFATFS_OPERATION_SUCCESS;
1416 #endif
1419 * Write the directory entry for the file into its `directoryEntryPos` position in its containing directory.
1421 * mode:
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
1426 * Returns:
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)
1433 uint8_t *sector;
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, &sector, 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);
1444 #endif
1446 if (result == AFATFS_OPERATION_SUCCESS) {
1447 if (afatfs_assert(file->directoryEntryPos.entryIndex >= 0)) {
1448 fatDirectoryEntry_t *entry = (fatDirectoryEntry_t *) sector + file->directoryEntryPos.entryIndex;
1450 switch (mode) {
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;
1458 break;
1459 case AFATFS_SAVE_DIRECTORY_DELETED:
1460 entry->filename[0] = FAT_DELETED_FILE_MARKER;
1461 FALLTHROUGH;
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;
1475 } else {
1476 return AFATFS_OPERATION_FAILURE;
1480 return result;
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.
1487 * Returns:
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
1495 * file.
1497 static afatfsOperationStatus_e afatfs_appendRegularFreeClusterContinue(afatfsFile_t *file)
1499 afatfsAppendFreeCluster_t *opState = &file->operation.state.appendFreeCluster;
1500 afatfsOperationStatus_e status;
1502 doMore:
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;
1520 goto doMore;
1521 break;
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;
1526 goto doMore;
1527 break;
1528 case AFATFS_FIND_CLUSTER_IN_PROGRESS:
1529 break;
1531 break;
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;
1539 } else {
1540 opState->phase = AFATFS_APPEND_FREE_CLUSTER_PHASE_UPDATE_FILE_DIRECTORY;
1543 goto doMore;
1545 break;
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;
1552 goto doMore;
1554 break;
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;
1558 goto doMore;
1560 break;
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;
1567 break;
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;
1575 break;
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.
1597 * Returns:
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.
1635 * Returns:
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;
1645 doMore:
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;
1673 } else {
1675 * We also need to update the FAT of the supercluster that used to end the file so that it no longer
1676 * terminates there
1678 opState->fatRewriteStartCluster -= afatfs_fatEntriesPerSector();
1681 opState->phase = AFATFS_APPEND_SUPERCLUSTER_PHASE_UPDATE_FREEFILE_DIRECTORY;
1682 goto doMore;
1683 break;
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;
1690 goto doMore;
1692 break;
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;
1698 goto doMore;
1700 break;
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);
1704 break;
1705 default:
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;
1713 return status;
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.
1722 * Returns:
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);
1754 #endif
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);
1768 } else
1769 #endif
1771 status = afatfs_appendRegularFreeCluster(file);
1774 return status;
1778 * Returns true if the file's cursor is sitting beyond the end of the last allocated cluster (i.e. the logical fileSize
1779 * is not checked).
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;
1785 } else {
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)
1798 uint8_t *result;
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)) {
1807 return NULL;
1810 result = afatfs_cacheSectorGetMemory(file->readRetainCacheIndex);
1811 } else {
1812 if (afatfs_isEndOfAllocatedFile(file)) {
1813 return NULL;
1816 afatfs_assert(physicalSector > 0); // We never read the root sector using files
1818 afatfsOperationStatus_e status = afatfs_cacheSector(
1819 physicalSector,
1820 &result,
1821 AFATFS_CACHE_READ | AFATFS_CACHE_RETAIN,
1825 if (status != AFATFS_OPERATION_SUCCESS) {
1826 // Sector not ready for read
1827 return NULL;
1830 file->readRetainCacheIndex = afatfs_getCacheDescriptorIndexForBuffer(result);
1833 return 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;
1844 uint8_t *result;
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)) {
1852 return NULL;
1855 result = afatfs_cacheSectorGetMemory(file->writeLockedCacheIndex);
1856 } else {
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
1862 return NULL;
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
1875 if (
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;
1887 } else {
1888 eraseBlockCount = 0;
1891 status = afatfs_cacheSector(
1892 physicalSector,
1893 &result,
1894 cacheFlags,
1895 eraseBlockCount
1898 if (status != AFATFS_OPERATION_SUCCESS) {
1899 // Not enough cache available to accept this write / sector not ready for read
1900 return NULL;
1903 file->writeLockedCacheIndex = afatfs_getCacheDescriptorIndexForBuffer(result);
1906 return 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
1919 * the seek atomic.
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;
1929 return true;
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;
1939 return true;
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) {
1949 return false;
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;
1967 } else {
1968 // Try again later
1969 return false;
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;
1978 return true;
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;
2008 } else {
2009 // Try again later
2010 return false;
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);
2027 return true;
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.
2035 * Returns:
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,
2039 * try again later.
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)) {
2045 if (callback) {
2046 callback(file);
2049 return AFATFS_OPERATION_SUCCESS;
2050 } else {
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.
2071 * Returns:
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,
2076 * try again later.
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);
2083 switch (whence) {
2084 case AFATFS_SEEK_CUR:
2085 if (offset >= 0) {
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;
2092 break;
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;
2102 break;
2104 case AFATFS_SEEK_SET:
2106 // Fall through
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.
2125 * Returns:
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)) {
2133 afatfs_poll();
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)) {
2147 return false;
2148 } else {
2149 *position = file->cursorOffset;
2150 return true;
2155 * Attempt to advance the directory pointer `finder` to the next entry in the directory.
2157 * Returns:
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)
2164 uint8_t *sector;
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
2170 } else {
2171 return AFATFS_OPERATION_IN_PROGRESS;
2175 sector = afatfs_fileRetainCursorSectorForRead(directory);
2177 if (sector) {
2178 finder->entryIndex++;
2180 *dirEntry = (fatDirectoryEntry_t*) sector + finder->entryIndex;
2182 finder->sectorNumberPhysical = afatfs_fileGetCursorPhysicalSector(directory);
2184 return AFATFS_OPERATION_SUCCESS;
2185 } else {
2186 if (afatfs_isEndOfAllocatedFile(directory)) {
2187 *dirEntry = NULL;
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
2206 * directory.
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;
2222 doMore:
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;
2229 goto doMore;
2230 } else if (status == AFATFS_OPERATION_FAILURE) {
2231 opState->phase = AFATFS_EXTEND_SUBDIRECTORY_PHASE_FAILURE;
2232 goto doMore;
2234 break;
2235 case AFATFS_EXTEND_SUBDIRECTORY_PHASE_WRITE_SECTORS:
2236 // Now, zero out that cluster
2237 afatfs_fileGetCursorClusterAndSector(directory, &clusterNumber, &sectorInCluster);
2238 physicalSector = afatfs_fileGetCursorPhysicalSector(directory);
2240 while (1) {
2241 status = afatfs_cacheSector(physicalSector, &sectorBuffer, AFATFS_CACHE_WRITE, 0);
2243 if (status != AFATFS_OPERATION_SUCCESS) {
2244 return status;
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));
2270 sectorInCluster++;
2271 physicalSector++;
2272 } else {
2273 break;
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;
2280 goto doMore;
2281 break;
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;
2290 break;
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;
2298 break;
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.
2349 * Returns:
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) {
2363 if (*dirEntry) {
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;
2370 } else {
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
2376 continue;
2377 } else {
2378 // The status (in progress or failure) of extending the directory becomes our status
2379 break;
2384 return result;
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];
2399 return NULL;
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;
2416 #endif
2418 doMore:
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;
2428 } else
2429 #endif
2431 opState->phase = AFATFS_TRUNCATE_FILE_ERASE_FAT_CHAIN_NORMAL;
2433 goto doMore;
2435 break;
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;
2443 goto doMore;
2445 break;
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;
2460 goto doMore;
2462 break;
2463 #endif
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) {
2471 return status;
2474 status = afatfs_FATSetNextCluster(opState->currentCluster, 0);
2476 if (status != AFATFS_OPERATION_SUCCESS) {
2477 return status;
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;
2487 goto doMore;
2488 break;
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;
2499 break;
2500 default:
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;
2508 return status;
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))
2523 return false;
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;
2537 } else
2538 #endif
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);
2551 return true;
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;
2570 #ifndef BOOTLOADER
2571 dateTime_t now;
2572 #endif
2574 doMore:
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;
2580 goto doMore;
2581 break;
2582 case AFATFS_CREATEFILE_PHASE_FIND_FILE:
2583 do {
2584 status = afatfs_findNext(&afatfs.currentDirectory, &file->directoryEntryPos, &entry);
2586 switch (status) {
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;
2597 goto doMore;
2598 } else {
2599 // File not found.
2601 opState->phase = AFATFS_CREATEFILE_PHASE_FAILURE;
2602 goto doMore;
2604 } else if (entry->attrib & FAT_FILE_ATTRIBUTE_VOLUME_ID) {
2605 break;
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;
2613 goto doMore;
2616 afatfs_fileLoadDirectoryEntry(file, entry);
2618 afatfs_findLast(&afatfs.currentDirectory);
2620 opState->phase = AFATFS_CREATEFILE_PHASE_SUCCESS;
2621 goto doMore;
2622 } // Else this entry doesn't match, fall through and continue the search
2623 break;
2624 case AFATFS_OPERATION_FAILURE:
2625 afatfs_findLast(&afatfs.currentDirectory);
2626 opState->phase = AFATFS_CREATEFILE_PHASE_FAILURE;
2627 goto doMore;
2628 break;
2629 case AFATFS_OPERATION_IN_PROGRESS:
2632 } while (status == AFATFS_OPERATION_SUCCESS);
2633 break;
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;
2642 #ifndef BOOTLOADER
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);
2646 } else {
2647 #endif
2648 entry->creationDate = AFATFS_DEFAULT_FILE_DATE;
2649 entry->creationTime = AFATFS_DEFAULT_FILE_TIME;
2650 #ifndef BOOTLOADER
2652 #endif
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);
2658 #endif
2660 opState->phase = AFATFS_CREATEFILE_PHASE_SUCCESS;
2661 goto doMore;
2662 } else if (status == AFATFS_OPERATION_FAILURE) {
2663 opState->phase = AFATFS_CREATEFILE_PHASE_FAILURE;
2664 goto doMore;
2666 break;
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,
2677 &directorySector,
2678 AFATFS_CACHE_READ | AFATFS_CACHE_RETAIN,
2682 if (status != AFATFS_OPERATION_SUCCESS) {
2683 // Retry next time
2684 break;
2688 afatfs_fseek(file, 0, AFATFS_SEEK_SET);
2690 // Is file empty?
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.
2696 break;
2697 } else {
2698 // Lock the freefile for our exclusive access
2699 afatfs.freeFile.operation.operation = AFATFS_FILE_OPERATION_LOCKED;
2702 #endif
2703 } else {
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);
2712 break;
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);
2720 break;
2724 file->operation.operation = AFATFS_FILE_OPERATION_NONE;
2725 opState->callback(file);
2726 break;
2727 case AFATFS_CREATEFILE_PHASE_FAILURE:
2728 file->type = AFATFS_FILE_TYPE_NONE;
2729 opState->callback(NULL);
2730 break;
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) {
2769 return true;
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))
2779 return false;
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;
2789 return true;
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;
2820 } else {
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;
2826 } else {
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;
2836 } else {
2837 opState->phase = AFATFS_CREATEFILE_PHASE_INITIAL;
2840 afatfs_createFileContinue(file);
2842 return 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) {
2860 return;
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);
2868 if (descriptor) {
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;
2882 #endif
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
2899 * new file).
2901 bool afatfs_fclose(afatfsFilePtr_t file, afatfsCallback_t callback)
2903 if (!file || file->type == AFATFS_FILE_TYPE_NONE) {
2904 return true;
2905 } else if (afatfs_fileIsBusy(file)) {
2906 return false;
2907 } else {
2908 afatfs_fileUpdateFilesize(file);
2910 file->operation.operation = AFATFS_FILE_OPERATION_CLOSE;
2911 file->operation.state.closeFile.callback = callback;
2912 afatfs_fcloseContinue(file);
2913 return true;
2918 * Close `file` immediately
2920 void afatfs_fcloseSync(afatfsFilePtr_t file)
2922 for(unsigned i = 0; i < 1000; ++i) {
2923 afatfs_poll();
2925 afatfs_fclose(file, NULL);
2926 for(unsigned i = 0; i < 1000; ++i) {
2927 afatfs_poll();
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();
2942 if (file) {
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) {
2945 callback(NULL);
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)) {
2961 return false;
2964 if (directory) {
2965 if (afatfs_fileIsBusy(directory)) {
2966 return false;
2969 memcpy(&afatfs.currentDirectory, directory, sizeof(*directory));
2970 return true;
2971 } else {
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;
2978 else
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);
2989 return true;
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
3013 * operation.
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;
3024 switch (mode[0]) {
3025 case 'r':
3026 fileMode = AFATFS_FILE_MODE_READ;
3027 break;
3028 case 'w':
3029 fileMode = AFATFS_FILE_MODE_WRITE | AFATFS_FILE_MODE_CREATE;
3030 break;
3031 case 'a':
3032 fileMode = AFATFS_FILE_MODE_APPEND | AFATFS_FILE_MODE_CREATE;
3033 break;
3036 switch (mode[1]) {
3037 case '+':
3038 fileMode |= AFATFS_FILE_MODE_READ;
3040 if (fileMode == AFATFS_FILE_MODE_READ) {
3041 fileMode |= AFATFS_FILE_MODE_WRITE;
3043 break;
3044 case 's':
3045 #ifdef AFATFS_USE_FREEFILE
3046 fileMode |= AFATFS_FILE_MODE_CONTIGUOUS | AFATFS_FILE_MODE_RETAIN_DIRECTORY;
3047 #endif
3048 break;
3051 file = afatfs_allocateFileHandle();
3053 if (file) {
3054 afatfs_createFile(file, filename, FAT_FILE_ATTRIBUTE_ARCHIVE, fileMode, complete);
3055 } else if (complete) {
3056 complete(NULL);
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++;
3083 } else {
3084 // Slow path
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) {
3104 return 0;
3107 if (afatfs_fileIsBusy(file)) {
3108 // There might be a seek pending
3109 return 0;
3112 uint32_t cursorOffsetInSector = file->cursorOffset % AFATFS_SECTOR_SIZE;
3113 uint32_t writtenBytes = 0;
3115 while (len > 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
3122 break;
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) {
3139 break;
3142 #ifdef AFATFS_USE_FREEFILE
3143 if ((file->mode & AFATFS_FILE_MODE_CONTIGUOUS) != 0) {
3144 afatfs_assert(file->cursorCluster < afatfs.freeFile.firstCluster);
3146 #endif
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;
3167 while (true) {
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()) {
3176 break;
3179 afatfs_poll();
3181 } else {
3182 break;
3186 return written;
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) {
3204 return 0;
3207 if (afatfs_fileIsBusy(file)) {
3208 // There might be a seek pending
3209 return 0;
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)
3218 return 0;
3220 len = MIN(file->logicalSize - file->cursorOffset, len);
3222 uint32_t readBytes = 0;
3223 uint32_t cursorOffsetInSector = file->cursorOffset % AFATFS_SECTOR_SIZE;
3225 while (len > 0) {
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
3232 return readBytes;
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) {
3247 break;
3250 len -= bytesToReadThisSector;
3251 buffer += bytesToReadThisSector;
3252 cursorOffsetInSector = 0;
3255 return readBytes;
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;
3269 while (true) {
3270 uint32_t leftToRead = length - bytesRead;
3271 uint32_t readNow = afatfs_fread(file, buffer, leftToRead);
3272 bytesRead += readNow;
3273 buffer += readNow;
3274 if (bytesRead < length) {
3276 if (afatfs_feof(file)) {
3277 break;
3280 afatfs_poll();
3282 } else {
3283 break;
3287 return bytesRead;
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)
3305 return;
3307 switch (file->operation.operation) {
3308 case AFATFS_FILE_OPERATION_CREATE_FILE:
3309 afatfs_createFileContinue(file);
3310 break;
3311 case AFATFS_FILE_OPERATION_SEEK:
3312 afatfs_fseekInternalContinue(file);
3313 break;
3314 case AFATFS_FILE_OPERATION_CLOSE:
3315 afatfs_fcloseContinue(file);
3316 break;
3317 case AFATFS_FILE_OPERATION_UNLINK:
3318 afatfs_funlinkContinue(file);
3319 break;
3320 case AFATFS_FILE_OPERATION_TRUNCATE:
3321 afatfs_ftruncateContinue(file, false);
3322 break;
3323 #ifdef AFATFS_USE_FREEFILE
3324 case AFATFS_FILE_OPERATION_APPEND_SUPERCLUSTER:
3325 afatfs_appendSuperclusterContinue(file);
3326 break;
3327 case AFATFS_FILE_OPERATION_LOCKED:
3329 break;
3330 #endif
3331 case AFATFS_FILE_OPERATION_APPEND_FREE_CLUSTER:
3332 afatfs_appendRegularFreeClusterContinue(file);
3333 break;
3334 case AFATFS_FILE_OPERATION_EXTEND_SUBDIRECTORY:
3335 afatfs_extendSubdirectoryContinue(file);
3336 break;
3337 case AFATFS_FILE_OPERATION_NONE:
3339 break;
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.
3382 * Returns:
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;
3394 while (1) {
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;
3402 break;
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;
3415 break;
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;
3438 } else {
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;
3443 break;
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;
3452 break;
3457 static void afatfs_freeFileCreated(afatfsFile_t *file)
3459 if (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;
3464 } else {
3465 // Allocate clusters for the freefile
3466 afatfs_findLargestContiguousFreeBlockBegin();
3467 afatfs.initPhase = AFATFS_INITIALIZATION_FREEFILE_FAT_SEARCH;
3469 } else {
3470 // Failed to allocate an entry
3471 afatfs.lastError = AFATFS_ERROR_GENERIC;
3472 afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_FATAL;
3476 #endif
3478 static void afatfs_initContinue(void)
3480 #ifdef AFATFS_USE_FREEFILE
3481 afatfsOperationStatus_e status;
3482 #endif
3484 uint8_t *sector;
3486 doMore:
3488 switch (afatfs.initPhase) {
3489 case AFATFS_INITIALIZATION_READ_MBR:
3490 if (afatfs_cacheSector(0, &sector, AFATFS_CACHE_READ | AFATFS_CACHE_DISCARDABLE, 0) == AFATFS_OPERATION_SUCCESS) {
3491 if (afatfs_parseMBR(sector)) {
3492 afatfs.initPhase = AFATFS_INITIALIZATION_READ_VOLUME_ID;
3493 goto doMore;
3494 } else {
3495 afatfs.lastError = AFATFS_ERROR_BAD_MBR;
3496 afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_FATAL;
3499 break;
3500 case AFATFS_INITIALIZATION_READ_VOLUME_ID:
3501 if (afatfs_cacheSector(afatfs.partitionStartSector, &sector, AFATFS_CACHE_READ | AFATFS_CACHE_DISCARDABLE, 0) == AFATFS_OPERATION_SUCCESS) {
3502 if (afatfs_parseVolumeID(sector)) {
3503 // Open the root directory
3504 afatfs_chdir(NULL);
3506 afatfs.initPhase++;
3507 } else {
3508 afatfs.lastError = AFATFS_ERROR_BAD_FILESYSTEM_HEADER;
3509 afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_FATAL;
3512 break;
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);
3520 break;
3521 case AFATFS_INITIALIZATION_FREEFILE_CREATING:
3522 afatfs_fileOperationContinue(&afatfs.freeFile);
3523 break;
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)
3558 goto doMore;
3560 break;
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;
3567 goto doMore;
3568 } else if (status == AFATFS_OPERATION_FAILURE) {
3569 afatfs.lastError = AFATFS_ERROR_GENERIC;
3570 afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_FATAL;
3572 break;
3573 case AFATFS_INITIALIZATION_FREEFILE_SAVE_DIR_ENTRY:
3574 status = afatfs_saveDirectoryEntry(&afatfs.freeFile, AFATFS_SAVE_DIRECTORY_NORMAL);
3576 if (status == AFATFS_OPERATION_SUCCESS) {
3577 afatfs.initPhase++;
3578 goto doMore;
3579 } else if (status == AFATFS_OPERATION_FAILURE) {
3580 afatfs.lastError = AFATFS_ERROR_GENERIC;
3581 afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_FATAL;
3583 break;
3584 #endif
3586 case AFATFS_INITIALIZATION_DONE:
3587 afatfs.filesystemState = AFATFS_FILESYSTEM_STATE_READY;
3588 break;
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()) {
3600 afatfs_flush();
3602 switch (afatfs.filesystemState) {
3603 case AFATFS_FILESYSTEM_STATE_INITIALIZATION:
3604 afatfs_initContinue();
3605 break;
3606 case AFATFS_FILESYSTEM_STATE_READY:
3607 afatfs_fileOperationsPoll();
3608 break;
3609 default:
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)
3627 #ifdef STM32H7
3628 afatfs.cache = afatfs_cache;
3629 #endif
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
3650 openFileCount++;
3654 #ifdef AFATFS_USE_FREEFILE
3655 if (afatfs.freeFile.type != AFATFS_FILE_TYPE_NONE) {
3656 afatfs_fclose(&afatfs.freeFile, NULL);
3657 openFileCount++;
3659 #endif
3661 if (afatfs.currentDirectory.type != AFATFS_FILE_TYPE_NONE) {
3662 afatfs_fclose(&afatfs.currentDirectory, NULL);
3663 openFileCount++;
3666 afatfs_poll();
3668 if (!afatfs_flush()) {
3669 return false;
3672 if (afatfs.cacheFlushInProgress) {
3673 return false;
3676 if (openFileCount > 0) {
3677 return false;
3680 #ifdef AFATFS_DEBUG
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);
3687 #endif
3690 // Clear the afatfs so it's as if we never ran
3691 memset(&afatfs, 0, sizeof(afatfs));
3693 return true;
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;
3707 return result;