mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 08:42:40 +01:00 
			
		
		
		
	Added support for the basic file operation
Missing seek, but these are the core filesystem operations provided by this filesystem: - Read a file - Append to a file Additional work is needed around freeing the previous file, so right now it's limited to appending to existing files, a real append only filesystem. Unfortunately the overhead of the free list with multiple open files is becoming tricky.
This commit is contained in:
		
							
								
								
									
										251
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										251
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -67,9 +67,19 @@ static lfs_off_t lfs_inext(lfs_t *lfs, lfs_off_t ioff) { | |||||||
|     return ioff; |     return ioff; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static lfs_off_t lfs_toindex(lfs_t *lfs, lfs_off_t off) { | ||||||
|  |     lfs_off_t i = 0; | ||||||
|  |     while (off > 512) { | ||||||
|  |         i = lfs_inext(lfs, i); | ||||||
|  |         off -= 512; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return i; | ||||||
|  | } | ||||||
|  |  | ||||||
| // Find index in index chain given its index offset | // Find index in index chain given its index offset | ||||||
| static lfs_error_t lfs_ifind(lfs_t *lfs, lfs_ino_t head, | static lfs_error_t lfs_ifind_block(lfs_t *lfs, lfs_ino_t head, | ||||||
|         lfs_size_t icount, lfs_off_t ioff, lfs_ino_t *ino) { |         lfs_size_t icount, lfs_off_t ioff, lfs_ino_t *block) { | ||||||
|     lfs_size_t wcount = lfs->info.erase_size/4; |     lfs_size_t wcount = lfs->info.erase_size/4; | ||||||
|     lfs_off_t iitarget = ioff / wcount; |     lfs_off_t iitarget = ioff / wcount; | ||||||
|     lfs_off_t iicurrent = (icount-1) / wcount; |     lfs_off_t iicurrent = (icount-1) / wcount; | ||||||
| @@ -88,6 +98,18 @@ static lfs_error_t lfs_ifind(lfs_t *lfs, lfs_ino_t head, | |||||||
|         iicurrent -= 1 << skip; |         iicurrent -= 1 << skip; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     *block = head; | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static lfs_error_t lfs_ifind(lfs_t *lfs, lfs_ino_t head, | ||||||
|  |         lfs_size_t icount, lfs_off_t ioff, lfs_ino_t *ino) { | ||||||
|  |     lfs_size_t wcount = lfs->info.erase_size/4; | ||||||
|  |     int err = lfs_ifind_block(lfs, head, icount, ioff, &head); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return lfs->ops->read(lfs->bd, (void*)ino, head, 4*(ioff % wcount), 4); |     return lfs->ops->read(lfs->bd, (void*)ino, head, 4*(ioff % wcount), 4); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -107,7 +129,7 @@ static lfs_error_t lfs_iappend(lfs_t *lfs, lfs_ino_t *headp, | |||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         lfs_off_t skips = lfs_min(lfs_ctz(ioff/wcount + 1), wcount-1) + 1; |         lfs_off_t skips = lfs_min(lfs_ctz(ioff/wcount + 1), wcount-2) + 1; | ||||||
|         for (lfs_off_t i = 0; i < skips; i++) { |         for (lfs_off_t i = 0; i < skips; i++) { | ||||||
|             err = lfs->ops->write(lfs->bd, (void*)&head, nhead, 4*i, 4); |             err = lfs->ops->write(lfs->bd, (void*)&head, nhead, 4*i, 4); | ||||||
|             if (err) { |             if (err) { | ||||||
| @@ -141,20 +163,44 @@ static lfs_error_t lfs_iappend(lfs_t *lfs, lfs_ino_t *headp, | |||||||
| static lfs_error_t lfs_alloc(lfs_t *lfs, lfs_ino_t *ino) { | static lfs_error_t lfs_alloc(lfs_t *lfs, lfs_ino_t *ino) { | ||||||
|     // TODO save slot for freeing? |     // TODO save slot for freeing? | ||||||
|     lfs_error_t err = lfs_ifind(lfs, lfs->free.d.head, |     lfs_error_t err = lfs_ifind(lfs, lfs->free.d.head, | ||||||
|             lfs->free.d.icount, lfs->free.d.ioff, ino); |             lfs->free.end, lfs->free.off, ino); | ||||||
|     if (err) { |     if (err) { | ||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     lfs->free.d.ioff = lfs_inext(lfs, lfs->free.d.ioff); |     lfs->free.off = lfs_inext(lfs, lfs->free.off); | ||||||
|  |  | ||||||
|     return lfs->ops->erase(lfs->bd, *ino, 0, lfs->info.erase_size); |     return lfs->ops->erase(lfs->bd, *ino, 0, lfs->info.erase_size); | ||||||
| } | } | ||||||
|  |  | ||||||
| static lfs_error_t lfs_free(lfs_t *lfs, lfs_ino_t ino) { | static lfs_error_t lfs_free(lfs_t *lfs, lfs_ino_t ino) { | ||||||
|     return lfs_iappend(lfs, &lfs->free.d.head, &lfs->free.d.icount, ino); |     return lfs_iappend(lfs, &lfs->free.d.head, &lfs->free.end, ino); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static lfs_error_t lfs_free_flush(lfs_t *lfs) { | ||||||
|  |     lfs_size_t wcount = lfs->info.erase_size/4; | ||||||
|  |  | ||||||
|  |     for (lfs_word_t i = lfs->free.begin / wcount; | ||||||
|  |             i + wcount < lfs->free.off; i += wcount) { | ||||||
|  |         lfs_ino_t block; | ||||||
|  |         int err = lfs_ifind_block(lfs, lfs->free.d.head, | ||||||
|  |                 lfs->free.end, i, &block); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs_free(lfs, block); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (lfs->free.off != lfs->free.d.off || lfs->free.end != lfs->free.d.end) { | ||||||
|  |         // TODO handle overflow? | ||||||
|  |         lfs->free.d.rev += 1; | ||||||
|  |     } | ||||||
|  |     lfs->free.d.off = lfs->free.off; | ||||||
|  |     lfs->free.d.end = lfs->free.end; | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| lfs_error_t lfs_check(lfs_t *lfs, lfs_ino_t block) { | lfs_error_t lfs_check(lfs_t *lfs, lfs_ino_t block) { | ||||||
|     uint32_t crc = 0xffffffff; |     uint32_t crc = 0xffffffff; | ||||||
| @@ -303,8 +349,7 @@ lfs_error_t lfs_dir_make(lfs_t *lfs, lfs_dir_t *dir, lfs_ino_t parent[2]) { | |||||||
|     dir->d.size = sizeof(struct lfs_disk_dir); |     dir->d.size = sizeof(struct lfs_disk_dir); | ||||||
|     dir->d.tail[0] = 0; |     dir->d.tail[0] = 0; | ||||||
|     dir->d.tail[1] = 0; |     dir->d.tail[1] = 0; | ||||||
|  |     lfs_free_flush(lfs); | ||||||
|     // TODO sort this out |  | ||||||
|     dir->d.free = lfs->free.d; |     dir->d.free = lfs->free.d; | ||||||
|  |  | ||||||
|     if (parent) { |     if (parent) { | ||||||
| @@ -469,6 +514,8 @@ lfs_error_t lfs_mkdir(lfs_t *lfs, const char *path) { | |||||||
|  |  | ||||||
|     cwd.d.rev += 1; |     cwd.d.rev += 1; | ||||||
|     cwd.d.size += entry.d.len; |     cwd.d.size += entry.d.len; | ||||||
|  |     lfs_free_flush(lfs); | ||||||
|  |     cwd.d.free = lfs->free.d; | ||||||
|  |  | ||||||
|     return lfs_pair_commit(lfs, entry.dir, |     return lfs_pair_commit(lfs, entry.dir, | ||||||
|         3, (struct lfs_commit_region[3]) { |         3, (struct lfs_commit_region[3]) { | ||||||
| @@ -478,6 +525,184 @@ lfs_error_t lfs_mkdir(lfs_t *lfs, const char *path) { | |||||||
|         }); |         }); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | lfs_error_t lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||||
|  |         const char *path, int flags) { | ||||||
|  |     // Allocate entry for file if it doesn't exist | ||||||
|  |     // TODO check open files | ||||||
|  |     lfs_dir_t cwd; | ||||||
|  |     int err = lfs_dir_fetch(lfs, &cwd, lfs->cwd); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (flags & LFS_O_CREAT) { | ||||||
|  |         err = lfs_dir_alloc(lfs, &cwd, path, | ||||||
|  |                 &file->entry, sizeof(file->entry.d)+strlen(path)); | ||||||
|  |         if (err && err != LFS_ERROR_EXISTS) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         err = lfs_dir_find(lfs, &cwd, path, &file->entry); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if ((flags & LFS_O_CREAT) && err != LFS_ERROR_EXISTS) { | ||||||
|  |         // Store file | ||||||
|  |         file->head = 0; | ||||||
|  |         file->size = 0; | ||||||
|  |         file->wblock = 0; | ||||||
|  |         file->windex = 0; | ||||||
|  |         file->rblock = 0; | ||||||
|  |         file->rindex = 0; | ||||||
|  |         file->roff = 0; | ||||||
|  |  | ||||||
|  |         file->entry.d.type = 1; | ||||||
|  |         file->entry.d.len = sizeof(file->entry.d) + strlen(path); | ||||||
|  |         file->entry.d.u.file.head = file->head; | ||||||
|  |         file->entry.d.u.file.size = file->size; | ||||||
|  |  | ||||||
|  |         cwd.d.rev += 1; | ||||||
|  |         cwd.d.size += file->entry.d.len; | ||||||
|  |         lfs_free_flush(lfs); | ||||||
|  |         cwd.d.free = lfs->free.d; | ||||||
|  |  | ||||||
|  |         return lfs_pair_commit(lfs, file->entry.dir, | ||||||
|  |             3, (struct lfs_commit_region[3]) { | ||||||
|  |                 {0, sizeof(cwd.d), &cwd.d}, | ||||||
|  |                 {file->entry.off, sizeof(file->entry.d), | ||||||
|  |                         &file->entry.d}, | ||||||
|  |                 {file->entry.off+sizeof(file->entry.d), | ||||||
|  |                         file->entry.d.len-sizeof(file->entry.d), path} | ||||||
|  |             }); | ||||||
|  |     } else { | ||||||
|  |         file->head = file->entry.d.u.file.head; | ||||||
|  |         file->size = file->entry.d.u.file.size; | ||||||
|  |         file->windex = lfs_toindex(lfs, file->size); | ||||||
|  |         file->rblock = 0; | ||||||
|  |         file->rindex = 0; | ||||||
|  |         file->roff = 0; | ||||||
|  |  | ||||||
|  |         // TODO do this lazily in write? | ||||||
|  |         // TODO cow the head i/d block | ||||||
|  |         if (file->size < lfs->info.erase_size) { | ||||||
|  |             file->wblock = file->head; | ||||||
|  |         } else { | ||||||
|  |             int err = lfs_ifind(lfs, file->head, file->windex, | ||||||
|  |                     file->windex, &file->wblock); | ||||||
|  |             if (err) { | ||||||
|  |                 return err; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | lfs_error_t lfs_file_close(lfs_t *lfs, lfs_file_t *file) { | ||||||
|  |     // Store file | ||||||
|  |     lfs_dir_t cwd; | ||||||
|  |     int err = lfs_dir_fetch(lfs, &cwd, file->entry.dir); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     file->entry.d.u.file.head = file->head; | ||||||
|  |     file->entry.d.u.file.size = file->size; | ||||||
|  |  | ||||||
|  |     cwd.d.rev += 1; | ||||||
|  |     lfs_free_flush(lfs); | ||||||
|  |     cwd.d.free = lfs->free.d; | ||||||
|  |  | ||||||
|  |     return lfs_pair_commit(lfs, file->entry.dir, | ||||||
|  |         3, (struct lfs_commit_region[3]) { | ||||||
|  |             {0, sizeof(cwd.d), &cwd.d}, | ||||||
|  |             {file->entry.off, sizeof(file->entry.d), &file->entry.d}, | ||||||
|  |         }); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, | ||||||
|  |         const void *buffer, lfs_size_t size) { | ||||||
|  |     const uint8_t *data = buffer; | ||||||
|  |     lfs_size_t nsize = size; | ||||||
|  |  | ||||||
|  |     while (nsize > 0) { | ||||||
|  |         lfs_off_t woff = file->size % lfs->info.erase_size; | ||||||
|  |  | ||||||
|  |         if (file->size == 0) { | ||||||
|  |             int err = lfs_alloc(lfs, &file->wblock); | ||||||
|  |             if (err) { | ||||||
|  |                 return err; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             file->head = file->wblock; | ||||||
|  |             file->windex = 0; | ||||||
|  |         } else if (woff == 0) { | ||||||
|  |             // TODO check that 2 blocks are available | ||||||
|  |             // TODO check for available blocks for backing up scratch files? | ||||||
|  |             int err = lfs_alloc(lfs, &file->wblock); | ||||||
|  |             if (err) { | ||||||
|  |                 return err; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             err = lfs_iappend(lfs, &file->head, &file->windex, file->wblock); | ||||||
|  |             if (err) { | ||||||
|  |                 return err; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs_size_t diff = lfs_min(nsize, lfs->info.erase_size - woff); | ||||||
|  |         int err = lfs->ops->write(lfs->bd, data, file->wblock, woff, diff); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         file->size += diff; | ||||||
|  |         data += diff; | ||||||
|  |         nsize -= diff; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return size; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, | ||||||
|  |         void *buffer, lfs_size_t size) { | ||||||
|  |     uint8_t *data = buffer; | ||||||
|  |     lfs_size_t nsize = size; | ||||||
|  |  | ||||||
|  |     while (nsize > 0 && file->roff < file->size) { | ||||||
|  |         lfs_off_t roff = file->roff % lfs->info.erase_size; | ||||||
|  |  | ||||||
|  |         // TODO cache index blocks | ||||||
|  |         if (file->size < lfs->info.erase_size) { | ||||||
|  |             file->rblock = file->head; | ||||||
|  |         } else if (roff == 0) { | ||||||
|  |             int err = lfs_ifind(lfs, file->head, file->windex, | ||||||
|  |                     file->rindex, &file->rblock); | ||||||
|  |             if (err) { | ||||||
|  |                 return err; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             file->rindex = lfs_inext(lfs, file->rindex); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs_size_t diff = lfs_min( | ||||||
|  |                 lfs_min(nsize, file->size-file->roff), | ||||||
|  |                 lfs->info.erase_size - roff); | ||||||
|  |         int err = lfs->ops->read(lfs->bd, data, file->rblock, roff, diff); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         file->roff += diff; | ||||||
|  |         data += diff; | ||||||
|  |         nsize -= diff; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return size - nsize; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| // Little filesystem operations | // Little filesystem operations | ||||||
| @@ -509,10 +734,14 @@ lfs_error_t lfs_format(lfs_t *lfs) { | |||||||
|  |  | ||||||
|     {   // Create free list |     {   // Create free list | ||||||
|         lfs->free = (lfs_free_t){ |         lfs->free = (lfs_free_t){ | ||||||
|             .d.head = 2, |             .begin = 1, | ||||||
|             .d.ioff = 1, |             .off = 1, | ||||||
|             .d.icount = 1, |             .end = 1, | ||||||
|  |  | ||||||
|             .d.rev = 1, |             .d.rev = 1, | ||||||
|  |             .d.head = 2, | ||||||
|  |             .d.off = 1, | ||||||
|  |             .d.end = 1, | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         lfs_size_t block_count = lfs->info.total_size / lfs->info.erase_size; |         lfs_size_t block_count = lfs->info.total_size / lfs->info.erase_size; | ||||||
|   | |||||||
							
								
								
									
										42
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -26,12 +26,27 @@ enum lfs_type { | |||||||
|     LFS_TYPE_DIR = 2, |     LFS_TYPE_DIR = 2, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | enum lfs_open_flags { | ||||||
|  |     LFS_O_RDONLY = 0, | ||||||
|  |     LFS_O_WRONLY = 1, | ||||||
|  |     LFS_O_RDWR   = 2, | ||||||
|  |     LFS_O_CREAT  = 0x0040, | ||||||
|  |     LFS_O_EXCL   = 0x0080, | ||||||
|  |     LFS_O_TRUNC  = 0x0200, | ||||||
|  |     LFS_O_APPEND = 0x0400, | ||||||
|  |     LFS_O_SYNC   = 0x1000, | ||||||
|  | }; | ||||||
|  |  | ||||||
| typedef struct lfs_free { | typedef struct lfs_free { | ||||||
|  |     lfs_word_t begin; | ||||||
|  |     lfs_word_t off; | ||||||
|  |     lfs_word_t end; | ||||||
|  |  | ||||||
|     lfs_disk_struct lfs_disk_free { |     lfs_disk_struct lfs_disk_free { | ||||||
|         lfs_ino_t head; |  | ||||||
|         lfs_word_t ioff; |  | ||||||
|         lfs_word_t icount; |  | ||||||
|         lfs_word_t rev; |         lfs_word_t rev; | ||||||
|  |         lfs_ino_t head; | ||||||
|  |         lfs_word_t off; | ||||||
|  |         lfs_word_t end; | ||||||
|     } d; |     } d; | ||||||
| } lfs_free_t; | } lfs_free_t; | ||||||
|  |  | ||||||
| @@ -65,6 +80,20 @@ typedef struct lfs_entry { | |||||||
|     } d; |     } d; | ||||||
| } lfs_entry_t; | } lfs_entry_t; | ||||||
|  |  | ||||||
|  | typedef struct lfs_file { | ||||||
|  |     lfs_ino_t head; | ||||||
|  |     lfs_size_t size; | ||||||
|  |  | ||||||
|  |     lfs_ino_t wblock; | ||||||
|  |     lfs_word_t windex; | ||||||
|  |  | ||||||
|  |     lfs_ino_t rblock; | ||||||
|  |     lfs_word_t rindex; | ||||||
|  |     lfs_off_t roff; | ||||||
|  |  | ||||||
|  |     struct lfs_entry entry; | ||||||
|  | } lfs_file_t; | ||||||
|  |  | ||||||
| typedef struct lfs_superblock { | typedef struct lfs_superblock { | ||||||
|     lfs_ino_t pair[2]; |     lfs_ino_t pair[2]; | ||||||
|     lfs_disk_struct lfs_disk_superblock { |     lfs_disk_struct lfs_disk_superblock { | ||||||
| @@ -94,5 +123,12 @@ lfs_error_t lfs_mount(lfs_t *lfs); | |||||||
|  |  | ||||||
| lfs_error_t lfs_mkdir(lfs_t *lfs, const char *path); | lfs_error_t lfs_mkdir(lfs_t *lfs, const char *path); | ||||||
|  |  | ||||||
|  | lfs_error_t lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||||
|  |         const char *path, int flags); | ||||||
|  | lfs_error_t lfs_file_close(lfs_t *lfs, lfs_file_t *file); | ||||||
|  | lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, | ||||||
|  |         const void *buffer, lfs_size_t size); | ||||||
|  | lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, | ||||||
|  |         void *buffer, lfs_size_t size); | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ typedef uint32_t lfs_word_t; | |||||||
| typedef uint16_t lfs_hword_t; | typedef uint16_t lfs_hword_t; | ||||||
|  |  | ||||||
| typedef lfs_word_t lfs_size_t; | typedef lfs_word_t lfs_size_t; | ||||||
|  | typedef int32_t lfs_ssize_t; | ||||||
| typedef lfs_word_t lfs_off_t; | typedef lfs_word_t lfs_off_t; | ||||||
| typedef int lfs_error_t; | typedef int lfs_error_t; | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user