mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 08:42:40 +01:00 
			
		
		
		
	Structured some of the bulk of the codebase
- Removed lfs_config.h, distributed between lfs.h and lfs_util.h - Moved some functions that felt out of place
This commit is contained in:
		
							
								
								
									
										679
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										679
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -8,7 +8,6 @@ | ||||
| #include "lfs_util.h" | ||||
|  | ||||
| #include <string.h> | ||||
| #include <stdbool.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
|  | ||||
| @@ -1086,6 +1085,43 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { | ||||
|     return lfs_dir_commit(lfs, &cwd, &file->entry, NULL); | ||||
| } | ||||
|  | ||||
| lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, | ||||
|         void *buffer, lfs_size_t size) { | ||||
|     uint8_t *data = buffer; | ||||
|     size = lfs_min(size, file->entry.d.u.file.size - file->rpos); | ||||
|     lfs_size_t nsize = size; | ||||
|  | ||||
|     if ((file->flags & 3) == LFS_O_WRONLY) { | ||||
|         return LFS_ERROR_INVALID; | ||||
|     } | ||||
|  | ||||
|     while (nsize > 0) { | ||||
|         // check if we need a new block | ||||
|         if (!file->rblock || file->roff == lfs->cfg->block_size) { | ||||
|             int err = lfs_index_find(lfs, | ||||
|                     file->entry.d.u.file.head, file->entry.d.u.file.size, | ||||
|                     file->rpos, &file->rblock, &file->roff); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // read as much as we can in current block | ||||
|         lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->roff); | ||||
|         int err = lfs_bd_read(lfs, file->rblock, file->roff, diff, data); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         file->rpos += diff; | ||||
|         file->roff += diff; | ||||
|         data += diff; | ||||
|         nsize -= diff; | ||||
|     } | ||||
|  | ||||
|     return size; | ||||
| } | ||||
|  | ||||
| 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; | ||||
| @@ -1144,43 +1180,6 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, | ||||
|     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; | ||||
|     size = lfs_min(size, file->entry.d.u.file.size - file->rpos); | ||||
|     lfs_size_t nsize = size; | ||||
|  | ||||
|     if ((file->flags & 3) == LFS_O_WRONLY) { | ||||
|         return LFS_ERROR_INVALID; | ||||
|     } | ||||
|  | ||||
|     while (nsize > 0) { | ||||
|         // check if we need a new block | ||||
|         if (!file->rblock || file->roff == lfs->cfg->block_size) { | ||||
|             int err = lfs_index_find(lfs, | ||||
|                     file->entry.d.u.file.head, file->entry.d.u.file.size, | ||||
|                     file->rpos, &file->rblock, &file->roff); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // read as much as we can in current block | ||||
|         lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->roff); | ||||
|         int err = lfs_bd_read(lfs, file->rblock, file->roff, diff, data); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         file->rpos += diff; | ||||
|         file->roff += diff; | ||||
|         data += diff; | ||||
|         nsize -= diff; | ||||
|     } | ||||
|  | ||||
|     return size; | ||||
| } | ||||
|  | ||||
| lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, | ||||
|         lfs_soff_t off, int whence) { | ||||
|     // write out everything beforehand, may be noop if rdonly | ||||
| @@ -1215,10 +1214,6 @@ lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, | ||||
|     return prev; | ||||
| } | ||||
|  | ||||
| lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) { | ||||
|     return lfs_max(file->wpos, file->entry.d.u.file.size); | ||||
| } | ||||
|  | ||||
| lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file) { | ||||
|     return file->rpos; | ||||
| } | ||||
| @@ -1232,300 +1227,38 @@ int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file) { | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|  | ||||
| /// Generic filesystem operations /// | ||||
| static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     lfs->cfg = cfg; | ||||
|     lfs->words = lfs->cfg->block_size / sizeof(uint32_t); | ||||
|  | ||||
|     // setup read cache | ||||
|     lfs->rcache.off = -1; | ||||
|     if (lfs->cfg->read_buffer) { | ||||
|         lfs->rcache.buffer = lfs->cfg->read_buffer; | ||||
|     } else { | ||||
|         lfs->rcache.buffer = malloc(lfs->cfg->read_size); | ||||
|         if (!lfs->rcache.buffer) { | ||||
|             return LFS_ERROR_NO_MEM; | ||||
|         } | ||||
| lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) { | ||||
|     return lfs_max(file->wpos, file->entry.d.u.file.size); | ||||
| } | ||||
|  | ||||
|     // setup program cache | ||||
|     lfs->pcache.off = -1; | ||||
|     if (lfs->cfg->prog_buffer) { | ||||
|         lfs->pcache.buffer = lfs->cfg->prog_buffer; | ||||
|     } else { | ||||
|         lfs->pcache.buffer = malloc(lfs->cfg->prog_size); | ||||
|         if (!lfs->pcache.buffer) { | ||||
|             return LFS_ERROR_NO_MEM; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // setup lookahead | ||||
|     if (lfs->cfg->lookahead_buffer) { | ||||
|         lfs->free.lookahead = lfs->cfg->lookahead_buffer; | ||||
|     } else { | ||||
|         lfs->free.lookahead = malloc(lfs->cfg->lookahead/8); | ||||
|         if (!lfs->free.lookahead) { | ||||
|             return LFS_ERROR_NO_MEM; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int lfs_deinit(lfs_t *lfs) { | ||||
|     // Free allocated memory | ||||
|     if (!lfs->cfg->read_buffer) { | ||||
|         free(lfs->rcache.buffer); | ||||
|     } | ||||
|  | ||||
|     if (!lfs->cfg->prog_buffer) { | ||||
|         free(lfs->pcache.buffer); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     int err = lfs_init(lfs, cfg); | ||||
| /// General fs oprations /// | ||||
| int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { | ||||
|     lfs_dir_t cwd; | ||||
|     int err = lfs_dir_fetch(lfs, &cwd, lfs->root); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // Create free lookahead | ||||
|     memset(lfs->free.lookahead, 0, lfs->cfg->lookahead/8); | ||||
|     lfs->free.start = 0; | ||||
|     lfs->free.off = 0; | ||||
|  | ||||
|     // Create superblock dir | ||||
|     lfs_dir_t superdir; | ||||
|     err = lfs_dir_alloc(lfs, &superdir); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // Write root directory | ||||
|     lfs_dir_t root; | ||||
|     err = lfs_dir_alloc(lfs, &root); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     err = lfs_dir_commit(lfs, &root, NULL, NULL); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     lfs->root[0] = root.pair[0]; | ||||
|     lfs->root[1] = root.pair[1]; | ||||
|  | ||||
|     // Write superblocks | ||||
|     lfs_superblock_t superblock = { | ||||
|         .off = sizeof(superdir.d), | ||||
|         .d.type = LFS_TYPE_SUPERBLOCK, | ||||
|         .d.len = sizeof(superblock.d), | ||||
|         .d.version = 0x00000001, | ||||
|         .d.magic = {"littlefs"}, | ||||
|         .d.block_size  = lfs->cfg->block_size, | ||||
|         .d.block_count = lfs->cfg->block_count, | ||||
|         .d.root = {lfs->root[0], lfs->root[1]}, | ||||
|     }; | ||||
|     superdir.d.tail[0] = root.pair[0]; | ||||
|     superdir.d.tail[1] = root.pair[1]; | ||||
|     superdir.d.size += sizeof(superdir.d); | ||||
|  | ||||
|     for (int i = 0; i < 2; i++) { | ||||
|         // Write both pairs for extra safety, do some finagling to pretend | ||||
|         // the superblock is an entry | ||||
|         int err = lfs_dir_commit(lfs, &superdir, | ||||
|                 (const lfs_entry_t*)&superblock, | ||||
|                 (const struct lfs_disk_entry*)&superblock.d + 1); | ||||
|         if (err) { | ||||
|             LFS_ERROR("Failed to write superblock at %d", superdir.pair[0]); | ||||
|             return err; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // sanity check that fetch works | ||||
|     err = lfs_dir_fetch(lfs, &superdir, (const lfs_block_t[2]){0, 1}); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     return lfs_deinit(lfs); | ||||
| } | ||||
|  | ||||
| int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     int err = lfs_init(lfs, cfg); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // setup free lookahead | ||||
|     lfs->free.start = -lfs->cfg->lookahead; | ||||
|     lfs->free.off = lfs->cfg->lookahead; | ||||
|  | ||||
|     // load superblock | ||||
|     lfs_dir_t dir; | ||||
|     lfs_superblock_t superblock; | ||||
|     err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); | ||||
|     if (!err) { | ||||
|         err = lfs_bd_read(lfs, dir.pair[0], | ||||
|                 sizeof(dir.d), sizeof(superblock.d), &superblock.d); | ||||
|     } | ||||
|  | ||||
|     if (err == LFS_ERROR_CORRUPT || | ||||
|             memcmp(superblock.d.magic, "littlefs", 8) != 0) { | ||||
|         LFS_ERROR("Invalid superblock at %d %d", dir.pair[0], dir.pair[1]); | ||||
|         return LFS_ERROR_CORRUPT; | ||||
|     } | ||||
|  | ||||
|     if (superblock.d.version > 0x0000ffff) { | ||||
|         LFS_ERROR("Invalid version %d.%d\n", | ||||
|                 0xffff & (superblock.d.version >> 16), | ||||
|                 0xffff & (superblock.d.version >> 0)); | ||||
|     } | ||||
|  | ||||
|     lfs->root[0] = superblock.d.root[0]; | ||||
|     lfs->root[1] = superblock.d.root[1]; | ||||
|  | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int lfs_unmount(lfs_t *lfs) { | ||||
|     return lfs_deinit(lfs); | ||||
| } | ||||
|  | ||||
| int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) { | ||||
|     // iterate over metadata pairs | ||||
|     lfs_dir_t dir; | ||||
|     lfs_file_t file; | ||||
|     lfs_block_t cwd[2] = {0, 1}; | ||||
|  | ||||
|     while (true) { | ||||
|         for (int i = 0; i < 2; i++) { | ||||
|             int err = cb(data, cwd[i]); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         int err = lfs_dir_fetch(lfs, &dir, cwd); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         // iterate over contents | ||||
|         while ((0x7fffffff & dir.d.size) >= dir.off + sizeof(file.entry.d)) { | ||||
|             int err = lfs_bd_read(lfs, dir.pair[0], dir.off, | ||||
|                     sizeof(file.entry.d), &file.entry.d); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             dir.off += file.entry.d.len; | ||||
|             if ((0xf & file.entry.d.type) == LFS_TYPE_REG) { | ||||
|                 if (file.entry.d.u.file.size < lfs->cfg->block_size) { | ||||
|                     int err = cb(data, file.entry.d.u.file.head); | ||||
|                     if (err) { | ||||
|                         return err; | ||||
|                     } | ||||
|                 } else { | ||||
|                     int err = lfs_index_traverse(lfs, | ||||
|                             file.entry.d.u.file.head, | ||||
|                             file.entry.d.u.file.size, | ||||
|                             cb, data); | ||||
|                     if (err) { | ||||
|                         return err; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         cwd[0] = dir.d.tail[0]; | ||||
|         cwd[1] = dir.d.tail[1]; | ||||
|  | ||||
|         if (!cwd[0]) { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2]) { | ||||
|     // iterate over all directory directory entries | ||||
|     lfs_dir_t parent = { | ||||
|         .d.tail[0] = lfs->root[0], | ||||
|         .d.tail[1] = lfs->root[1], | ||||
|     }; | ||||
|  | ||||
|     while (parent.d.tail[0]) { | ||||
|     lfs_entry_t entry; | ||||
|         int err = lfs_dir_fetch(lfs, &parent, parent.d.tail); | ||||
|     err = lfs_dir_find(lfs, &cwd, &entry, &path); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|         while (true) { | ||||
|             int err = lfs_dir_next(lfs, &parent, &entry); | ||||
|             if (err && err != LFS_ERROR_NO_ENTRY) { | ||||
|                 return err; | ||||
|     // TODO abstract out info assignment | ||||
|     memset(info, 0, sizeof(*info)); | ||||
|     info->type = entry.d.type & 0xff; | ||||
|     if (info->type == LFS_TYPE_REG) { | ||||
|         info->size = entry.d.u.file.size; | ||||
|     } | ||||
|  | ||||
|             if (err == LFS_ERROR_NO_ENTRY) { | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             if ((0xf & entry.d.type) == LFS_TYPE_DIR && | ||||
|                     lfs_paircmp(entry.d.u.dir, dir) == 0) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| int lfs_deorphan(lfs_t *lfs) { | ||||
|     // iterate over all directories | ||||
|     lfs_dir_t pdir; | ||||
|     lfs_dir_t cdir; | ||||
|  | ||||
|     // skip root | ||||
|     int err = lfs_dir_fetch(lfs, &pdir, lfs->root); | ||||
|     err = lfs_bd_read(lfs, cwd.pair[0], entry.off + sizeof(entry.d), | ||||
|             entry.d.len - sizeof(entry.d), info->name); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     while (pdir.d.tail[0]) { | ||||
|         int err = lfs_dir_fetch(lfs, &cdir, pdir.d.tail); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         // check if we have a parent | ||||
|         int parent = lfs_parent(lfs, pdir.d.tail); | ||||
|         if (parent < 0) { | ||||
|             return parent; | ||||
|         } | ||||
|  | ||||
|         if (!parent) { | ||||
|             // we are an orphan | ||||
|             LFS_INFO("Orphan %d %d", pdir.d.tail[0], pdir.d.tail[1]); | ||||
|  | ||||
|             pdir.d.tail[0] = cdir.d.tail[0]; | ||||
|             pdir.d.tail[1] = cdir.d.tail[1]; | ||||
|  | ||||
|             err = lfs_dir_commit(lfs, &pdir, NULL, NULL); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         memcpy(&pdir, &cdir, sizeof(pdir)); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -1666,31 +1399,303 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { | ||||
|     lfs_dir_t cwd; | ||||
|     int err = lfs_dir_fetch(lfs, &cwd, lfs->root); | ||||
|     if (err) { | ||||
|         return err; | ||||
|  | ||||
| /// Filesystem operations /// | ||||
| static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     lfs->cfg = cfg; | ||||
|     lfs->words = lfs->cfg->block_size / sizeof(uint32_t); | ||||
|  | ||||
|     // setup read cache | ||||
|     lfs->rcache.off = -1; | ||||
|     if (lfs->cfg->read_buffer) { | ||||
|         lfs->rcache.buffer = lfs->cfg->read_buffer; | ||||
|     } else { | ||||
|         lfs->rcache.buffer = malloc(lfs->cfg->read_size); | ||||
|         if (!lfs->rcache.buffer) { | ||||
|             return LFS_ERROR_NO_MEM; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     lfs_entry_t entry; | ||||
|     err = lfs_dir_find(lfs, &cwd, &entry, &path); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     // setup program cache | ||||
|     lfs->pcache.off = -1; | ||||
|     if (lfs->cfg->prog_buffer) { | ||||
|         lfs->pcache.buffer = lfs->cfg->prog_buffer; | ||||
|     } else { | ||||
|         lfs->pcache.buffer = malloc(lfs->cfg->prog_size); | ||||
|         if (!lfs->pcache.buffer) { | ||||
|             return LFS_ERROR_NO_MEM; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // TODO abstract out info assignment | ||||
|     memset(info, 0, sizeof(*info)); | ||||
|     info->type = entry.d.type & 0xff; | ||||
|     if (info->type == LFS_TYPE_REG) { | ||||
|         info->size = entry.d.u.file.size; | ||||
|     // setup lookahead | ||||
|     if (lfs->cfg->lookahead_buffer) { | ||||
|         lfs->free.lookahead = lfs->cfg->lookahead_buffer; | ||||
|     } else { | ||||
|         lfs->free.lookahead = malloc(lfs->cfg->lookahead/8); | ||||
|         if (!lfs->free.lookahead) { | ||||
|             return LFS_ERROR_NO_MEM; | ||||
|         } | ||||
|  | ||||
|     err = lfs_bd_read(lfs, cwd.pair[0], entry.off + sizeof(entry.d), | ||||
|             entry.d.len - sizeof(entry.d), info->name); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int lfs_deinit(lfs_t *lfs) { | ||||
|     // Free allocated memory | ||||
|     if (!lfs->cfg->read_buffer) { | ||||
|         free(lfs->rcache.buffer); | ||||
|     } | ||||
|  | ||||
|     if (!lfs->cfg->prog_buffer) { | ||||
|         free(lfs->pcache.buffer); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     int err = lfs_init(lfs, cfg); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // Create free lookahead | ||||
|     memset(lfs->free.lookahead, 0, lfs->cfg->lookahead/8); | ||||
|     lfs->free.start = 0; | ||||
|     lfs->free.off = 0; | ||||
|  | ||||
|     // Create superblock dir | ||||
|     lfs_dir_t superdir; | ||||
|     err = lfs_dir_alloc(lfs, &superdir); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // Write root directory | ||||
|     lfs_dir_t root; | ||||
|     err = lfs_dir_alloc(lfs, &root); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     err = lfs_dir_commit(lfs, &root, NULL, NULL); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     lfs->root[0] = root.pair[0]; | ||||
|     lfs->root[1] = root.pair[1]; | ||||
|  | ||||
|     // Write superblocks | ||||
|     lfs_superblock_t superblock = { | ||||
|         .off = sizeof(superdir.d), | ||||
|         .d.type = LFS_TYPE_SUPERBLOCK, | ||||
|         .d.len = sizeof(superblock.d), | ||||
|         .d.version = 0x00000001, | ||||
|         .d.magic = {"littlefs"}, | ||||
|         .d.block_size  = lfs->cfg->block_size, | ||||
|         .d.block_count = lfs->cfg->block_count, | ||||
|         .d.root = {lfs->root[0], lfs->root[1]}, | ||||
|     }; | ||||
|     superdir.d.tail[0] = root.pair[0]; | ||||
|     superdir.d.tail[1] = root.pair[1]; | ||||
|     superdir.d.size += sizeof(superdir.d); | ||||
|  | ||||
|     for (int i = 0; i < 2; i++) { | ||||
|         // Write both pairs for extra safety, do some finagling to pretend | ||||
|         // the superblock is an entry | ||||
|         int err = lfs_dir_commit(lfs, &superdir, | ||||
|                 (const lfs_entry_t*)&superblock, | ||||
|                 (const struct lfs_disk_entry*)&superblock.d + 1); | ||||
|         if (err) { | ||||
|             LFS_ERROR("Failed to write superblock at %d", superdir.pair[0]); | ||||
|             return err; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // sanity check that fetch works | ||||
|     err = lfs_dir_fetch(lfs, &superdir, (const lfs_block_t[2]){0, 1}); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     return lfs_deinit(lfs); | ||||
| } | ||||
|  | ||||
| int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     int err = lfs_init(lfs, cfg); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // setup free lookahead | ||||
|     lfs->free.start = -lfs->cfg->lookahead; | ||||
|     lfs->free.off = lfs->cfg->lookahead; | ||||
|  | ||||
|     // load superblock | ||||
|     lfs_dir_t dir; | ||||
|     lfs_superblock_t superblock; | ||||
|     err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); | ||||
|     if (!err) { | ||||
|         err = lfs_bd_read(lfs, dir.pair[0], | ||||
|                 sizeof(dir.d), sizeof(superblock.d), &superblock.d); | ||||
|  | ||||
|         lfs->root[0] = superblock.d.root[0]; | ||||
|         lfs->root[1] = superblock.d.root[1]; | ||||
|     } | ||||
|  | ||||
|     if (err == LFS_ERROR_CORRUPT || | ||||
|             memcmp(superblock.d.magic, "littlefs", 8) != 0) { | ||||
|         LFS_ERROR("Invalid superblock at %d %d", dir.pair[0], dir.pair[1]); | ||||
|         return LFS_ERROR_CORRUPT; | ||||
|     } | ||||
|  | ||||
|     if (superblock.d.version > 0x0000ffff) { | ||||
|         LFS_ERROR("Invalid version %d.%d\n", | ||||
|                 0xffff & (superblock.d.version >> 16), | ||||
|                 0xffff & (superblock.d.version >> 0)); | ||||
|         return LFS_ERROR_INVALID; | ||||
|     } | ||||
|  | ||||
|     return err; | ||||
| } | ||||
|  | ||||
| int lfs_unmount(lfs_t *lfs) { | ||||
|     return lfs_deinit(lfs); | ||||
| } | ||||
|  | ||||
|  | ||||
| /// Littlefs specific operations /// | ||||
| int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) { | ||||
|     // iterate over metadata pairs | ||||
|     lfs_dir_t dir; | ||||
|     lfs_file_t file; | ||||
|     lfs_block_t cwd[2] = {0, 1}; | ||||
|  | ||||
|     while (true) { | ||||
|         for (int i = 0; i < 2; i++) { | ||||
|             int err = cb(data, cwd[i]); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         int err = lfs_dir_fetch(lfs, &dir, cwd); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         // iterate over contents | ||||
|         while ((0x7fffffff & dir.d.size) >= dir.off + sizeof(file.entry.d)) { | ||||
|             int err = lfs_bd_read(lfs, dir.pair[0], dir.off, | ||||
|                     sizeof(file.entry.d), &file.entry.d); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             dir.off += file.entry.d.len; | ||||
|             if ((0xf & file.entry.d.type) == LFS_TYPE_REG) { | ||||
|                 if (file.entry.d.u.file.size < lfs->cfg->block_size) { | ||||
|                     int err = cb(data, file.entry.d.u.file.head); | ||||
|                     if (err) { | ||||
|                         return err; | ||||
|                     } | ||||
|                 } else { | ||||
|                     int err = lfs_index_traverse(lfs, | ||||
|                             file.entry.d.u.file.head, | ||||
|                             file.entry.d.u.file.size, | ||||
|                             cb, data); | ||||
|                     if (err) { | ||||
|                         return err; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         cwd[0] = dir.d.tail[0]; | ||||
|         cwd[1] = dir.d.tail[1]; | ||||
|  | ||||
|         if (!cwd[0]) { | ||||
|             return 0; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2]) { | ||||
|     // iterate over all directory directory entries | ||||
|     lfs_dir_t parent = { | ||||
|         .d.tail[0] = lfs->root[0], | ||||
|         .d.tail[1] = lfs->root[1], | ||||
|     }; | ||||
|  | ||||
|     while (parent.d.tail[0]) { | ||||
|         lfs_entry_t entry; | ||||
|         int err = lfs_dir_fetch(lfs, &parent, parent.d.tail); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         while (true) { | ||||
|             int err = lfs_dir_next(lfs, &parent, &entry); | ||||
|             if (err && err != LFS_ERROR_NO_ENTRY) { | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             if (err == LFS_ERROR_NO_ENTRY) { | ||||
|                 break; | ||||
|             } | ||||
|  | ||||
|             if ((0xf & entry.d.type) == LFS_TYPE_DIR && | ||||
|                     lfs_paircmp(entry.d.u.dir, dir) == 0) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| int lfs_deorphan(lfs_t *lfs) { | ||||
|     // iterate over all directories | ||||
|     lfs_dir_t pdir; | ||||
|     lfs_dir_t cdir; | ||||
|  | ||||
|     // skip root | ||||
|     int err = lfs_dir_fetch(lfs, &pdir, lfs->root); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     while (pdir.d.tail[0]) { | ||||
|         int err = lfs_dir_fetch(lfs, &cdir, pdir.d.tail); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         // check if we have a parent | ||||
|         int parent = lfs_parent(lfs, pdir.d.tail); | ||||
|         if (parent < 0) { | ||||
|             return parent; | ||||
|         } | ||||
|  | ||||
|         if (!parent) { | ||||
|             // we are an orphan | ||||
|             LFS_DEBUG("Orphan %d %d", pdir.d.tail[0], pdir.d.tail[1]); | ||||
|  | ||||
|             pdir.d.tail[0] = cdir.d.tail[0]; | ||||
|             pdir.d.tail[1] = cdir.d.tail[1]; | ||||
|  | ||||
|             err = lfs_dir_commit(lfs, &pdir, NULL, NULL); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         memcpy(&pdir, &cdir, sizeof(pdir)); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										35
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -7,9 +7,25 @@ | ||||
| #ifndef LFS_H | ||||
| #define LFS_H | ||||
|  | ||||
| #include "lfs_config.h" | ||||
| #include <stdint.h> | ||||
| #include <stdbool.h> | ||||
|  | ||||
|  | ||||
| // Type definitions | ||||
| typedef uint32_t lfs_size_t; | ||||
| typedef uint32_t lfs_off_t; | ||||
|  | ||||
| typedef int32_t  lfs_ssize_t; | ||||
| typedef int32_t  lfs_soff_t; | ||||
|  | ||||
| typedef uint32_t lfs_block_t; | ||||
|  | ||||
|  | ||||
| // Configurable littlefs constants | ||||
| #ifndef LFS_NAME_MAX | ||||
| #define LFS_NAME_MAX 255 | ||||
| #endif | ||||
|  | ||||
| // The littefs constants | ||||
| enum lfs_error { | ||||
|     LFS_ERROR_OK       = 0, | ||||
| @@ -109,7 +125,7 @@ struct lfs_info { | ||||
| }; | ||||
|  | ||||
|  | ||||
| // Internal data structures | ||||
| // littlefs data structures | ||||
| typedef struct lfs_entry { | ||||
|     lfs_block_t pair[2]; | ||||
|     lfs_off_t off; | ||||
| @@ -169,8 +185,7 @@ typedef struct lfs_superblock { | ||||
|     } d; | ||||
| } lfs_superblock_t; | ||||
|  | ||||
|  | ||||
| // Little filesystem type | ||||
| // littlefs type | ||||
| typedef struct lfs { | ||||
|     const struct lfs_config *cfg; | ||||
|     lfs_size_t words;       // number of 32-bit words that can fit in a block | ||||
| @@ -191,15 +206,17 @@ typedef struct lfs { | ||||
| } lfs_t; | ||||
|  | ||||
|  | ||||
| // Functions | ||||
| // filesystem functions | ||||
| int lfs_format(lfs_t *lfs, const struct lfs_config *config); | ||||
| int lfs_mount(lfs_t *lfs, const struct lfs_config *config); | ||||
| int lfs_unmount(lfs_t *lfs); | ||||
|  | ||||
| // general operations | ||||
| int lfs_remove(lfs_t *lfs, const char *path); | ||||
| int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath); | ||||
| int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info); | ||||
|  | ||||
| // directory operations | ||||
| int lfs_mkdir(lfs_t *lfs, const char *path); | ||||
| int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path); | ||||
| int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir); | ||||
| @@ -208,20 +225,22 @@ int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off); | ||||
| lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir); | ||||
| int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); | ||||
|  | ||||
| // file operations | ||||
| int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||
|         const char *path, int flags); | ||||
| int lfs_file_close(lfs_t *lfs, lfs_file_t *file); | ||||
| int lfs_file_sync(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); | ||||
| lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, | ||||
|         const void *buffer, lfs_size_t size); | ||||
| lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, | ||||
|         lfs_soff_t off, int whence); | ||||
| lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file); | ||||
| lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file); | ||||
| int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file); | ||||
| lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file); | ||||
|  | ||||
| // miscellaneous lfs specific operations | ||||
| int lfs_deorphan(lfs_t *lfs); | ||||
| int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data); | ||||
|  | ||||
|   | ||||
							
								
								
									
										33
									
								
								lfs_config.h
									
									
									
									
									
								
							
							
						
						
									
										33
									
								
								lfs_config.h
									
									
									
									
									
								
							| @@ -1,33 +0,0 @@ | ||||
| /* | ||||
|  * Configuration and type definitions | ||||
|  * | ||||
|  * Copyright (c) 2017 Christopher Haster | ||||
|  * Distributed under the MIT license | ||||
|  */ | ||||
| #ifndef LFS_CONFIG_H | ||||
| #define LFS_CONFIG_H | ||||
|  | ||||
| #include <stdint.h> | ||||
|  | ||||
| // Type definitions | ||||
| typedef uint32_t lfs_size_t; | ||||
| typedef uint32_t lfs_off_t; | ||||
|  | ||||
| typedef int32_t  lfs_ssize_t; | ||||
| typedef int32_t  lfs_soff_t; | ||||
|  | ||||
| typedef uint32_t lfs_block_t; | ||||
|  | ||||
| // Maximum length of file name | ||||
| #ifndef LFS_NAME_MAX | ||||
| #define LFS_NAME_MAX 255 | ||||
| #endif | ||||
|  | ||||
| // Logging operations | ||||
| #include <stdio.h> | ||||
| #define LFS_ERROR(fmt, ...) printf("lfs error: " fmt "\n", __VA_ARGS__) | ||||
| #define LFS_WARN(fmt, ...)  printf("lfs warn: " fmt "\n", __VA_ARGS__) | ||||
| #define LFS_INFO(fmt, ...)  printf("lfs info: " fmt "\n", __VA_ARGS__) | ||||
|  | ||||
|  | ||||
| #endif | ||||
| @@ -7,7 +7,7 @@ | ||||
| #include "lfs_util.h" | ||||
|  | ||||
|  | ||||
| uint32_t lfs_crc(uint32_t crc, lfs_size_t size, const void *buffer) { | ||||
| uint32_t lfs_crc(uint32_t crc, size_t size, const void *buffer) { | ||||
|     static const uint32_t rtable[16] = { | ||||
|         0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, | ||||
|         0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, | ||||
| @@ -17,7 +17,7 @@ uint32_t lfs_crc(uint32_t crc, lfs_size_t size, const void *buffer) { | ||||
|  | ||||
|     const uint8_t *data = buffer; | ||||
|  | ||||
|     for (lfs_size_t i = 0; i < size; i++) { | ||||
|     for (size_t i = 0; i < size; i++) { | ||||
|         crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 0)) & 0xf]; | ||||
|         crc = (crc >> 4) ^ rtable[(crc ^ (data[i] >> 4)) & 0xf]; | ||||
|     } | ||||
|   | ||||
							
								
								
									
										11
									
								
								lfs_util.h
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								lfs_util.h
									
									
									
									
									
								
							| @@ -7,8 +7,9 @@ | ||||
| #ifndef LFS_UTIL_H | ||||
| #define LFS_UTIL_H | ||||
|  | ||||
| #include "lfs_config.h" | ||||
| #include <stdlib.h> | ||||
| #include <stdint.h> | ||||
| #include <stdio.h> | ||||
|  | ||||
|  | ||||
| // Builtin functions | ||||
| @@ -32,7 +33,13 @@ static inline int lfs_scmp(uint32_t a, uint32_t b) { | ||||
|     return (int)(unsigned)(a - b); | ||||
| } | ||||
|  | ||||
| uint32_t lfs_crc(uint32_t crc, lfs_size_t size, const void *buffer); | ||||
| uint32_t lfs_crc(uint32_t crc, size_t size, const void *buffer); | ||||
|  | ||||
|  | ||||
| // Logging functions | ||||
| #define LFS_DEBUG(fmt, ...) printf("lfs debug: " fmt "\n", __VA_ARGS__) | ||||
| #define LFS_WARN(fmt, ...)  printf("lfs warn: " fmt "\n", __VA_ARGS__) | ||||
| #define LFS_ERROR(fmt, ...) printf("lfs error: " fmt "\n", __VA_ARGS__) | ||||
|  | ||||
|  | ||||
| #endif | ||||
|   | ||||
		Reference in New Issue
	
	Block a user