mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 08:42:40 +01:00 
			
		
		
		
	Expanded inline files up to a limit of 1023 bytes
One of the big benefits of inline files is that small files no longer need to take up a full block. This opens up an opportunity to provide much better support for storage devices with only a handful of very large blocks. Such as the internal flash found on most microcontrollers. After investigating some use cases for a filesystem on internal flash, it has become apparent that the 255-byte limit is going to be too restrictive to be useful in many cases. Most uses I found needed files ~4-64 bytes in size, but it wasn't uncommon to find files ~512 bytes in length. To try to remedy this, I've pushed the 255 byte limit up to 1023 bytes, by stealing some bits from the previously-unused attributes's size. Unfortunately this limits attributes to 63 bytes in total and has a minor code cost, but I'm not sure even 1023 bytes will be sufficient for a lot of cases. The littlefs will probably never be as efficient with internal flash as other filesystems such as SPIFFS, it just wasn't designed for this sort of limited geometry. However, this feature has been heavily requested, even with limitations, because of the opportunity for code reuse on microcontrollers with both internal and external flash.
This commit is contained in:
		
							
								
								
									
										103
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -381,6 +381,26 @@ static void lfs_superblock_tole32(struct lfs_disk_superblock *d) { | ||||
|     d->name_size   = lfs_tole32(d->name_size); | ||||
| } | ||||
|  | ||||
| /// Other struct functions /// | ||||
| static inline lfs_size_t lfs_entry_elen(const lfs_entry_t *entry) { | ||||
|     return (lfs_size_t)(entry->d.elen) | | ||||
|         ((lfs_size_t)(entry->d.alen & 0xc0) << 2); | ||||
| } | ||||
|  | ||||
| static inline lfs_size_t lfs_entry_alen(const lfs_entry_t *entry) { | ||||
|     return entry->d.alen & 0x3f; | ||||
| } | ||||
|  | ||||
| static inline lfs_size_t lfs_entry_nlen(const lfs_entry_t *entry) { | ||||
|     return entry->d.nlen; | ||||
| } | ||||
|  | ||||
| static inline lfs_size_t lfs_entry_size(const lfs_entry_t *entry) { | ||||
|     return 4 + lfs_entry_elen(entry) + | ||||
|             lfs_entry_alen(entry) + | ||||
|             lfs_entry_nlen(entry); | ||||
| } | ||||
|  | ||||
|  | ||||
| /// Metadata pair and directory operations /// | ||||
| static inline void lfs_pairswap(lfs_block_t pair[2]) { | ||||
| @@ -573,7 +593,7 @@ static int lfs_commit_region(lfs_t *lfs, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int lfs_dif_commit(lfs_t *lfs, lfs_dir_t *dir, | ||||
| static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir, | ||||
|         const struct lfs_region *regions, int count) { | ||||
|     // state for copying over | ||||
|     const lfs_block_t oldpair[2] = {dir->pair[1], dir->pair[0]}; | ||||
| @@ -733,7 +753,7 @@ static int lfs_dir_set(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry, | ||||
|             } | ||||
|  | ||||
|             type |= LFS_STRUCT_MOVED; | ||||
|             err = lfs_dif_commit(lfs, &olddir, (struct lfs_region[]){ | ||||
|             err = lfs_dir_commit(lfs, &olddir, (struct lfs_region[]){ | ||||
|                         {LFS_FROM_MEM, oldoff, &type, 1}, | ||||
|                         {LFS_FROM_DROP, oldoff, NULL, -1}}, 2); | ||||
|             if (err) { | ||||
| @@ -769,7 +789,7 @@ static int lfs_dir_set(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry, | ||||
|         // writing out new entry | ||||
|         entry->off = dir->d.size - 4; | ||||
|         entry->size += diff; | ||||
|         int err = lfs_dif_commit(lfs, dir, (struct lfs_region[]){ | ||||
|         int err = lfs_dir_commit(lfs, dir, (struct lfs_region[]){ | ||||
|                 {LFS_FROM_REGION, entry->off, &(struct lfs_region_region){ | ||||
|                     olddir.pair[0], oldoff, | ||||
|                     regions, count}, entry->size}}, 1); | ||||
| @@ -783,7 +803,7 @@ static int lfs_dir_set(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry, | ||||
|             pdir.d.tail[0] = dir->pair[0]; | ||||
|             pdir.d.tail[1] = dir->pair[1]; | ||||
|  | ||||
|             err = lfs_dif_commit(lfs, &pdir, NULL, 0); | ||||
|             err = lfs_dir_commit(lfs, &pdir, NULL, 0); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
| @@ -818,7 +838,7 @@ static int lfs_dir_set(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry, | ||||
|             pdir.d.size &= dir->d.size | 0x7fffffff; | ||||
|             pdir.d.tail[0] = dir->d.tail[0]; | ||||
|             pdir.d.tail[1] = dir->d.tail[1]; | ||||
|             int err = lfs_dif_commit(lfs, &pdir, NULL, 0); | ||||
|             int err = lfs_dir_commit(lfs, &pdir, NULL, 0); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
| @@ -830,7 +850,7 @@ static int lfs_dir_set(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry, | ||||
|         regions[i].off += entry->off; | ||||
|     } | ||||
|  | ||||
|     int err = lfs_dif_commit(lfs, dir, regions, count); | ||||
|     int err = lfs_dir_commit(lfs, dir, regions, count); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
| @@ -885,7 +905,7 @@ static int lfs_dir_next(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) { | ||||
|     } | ||||
|  | ||||
|     entry->off = dir->off; | ||||
|     entry->size = 4 + entry->d.elen + entry->d.alen + entry->d.nlen; | ||||
|     entry->size = lfs_entry_size(entry); | ||||
|     dir->off += entry->size; | ||||
|     dir->pos += entry->size; | ||||
|     return 0; | ||||
| @@ -1041,7 +1061,7 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { | ||||
|     dir.d.tail[0] = cwd.d.tail[0]; | ||||
|     dir.d.tail[1] = cwd.d.tail[1]; | ||||
|  | ||||
|     err = lfs_dif_commit(lfs, &dir, NULL, 0); | ||||
|     err = lfs_dir_commit(lfs, &dir, NULL, 0); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
| @@ -1166,7 +1186,7 @@ int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) { | ||||
|     if (entry.d.type == (LFS_STRUCT_CTZ | LFS_TYPE_REG)) { | ||||
|         info->size = entry.d.u.file.size; | ||||
|     } else if (entry.d.type == (LFS_STRUCT_INLINE | LFS_TYPE_REG)) { | ||||
|         info->size = entry.d.elen; | ||||
|         info->size = lfs_entry_elen(&entry); | ||||
|     } | ||||
|  | ||||
|     int err = lfs_dir_get(lfs, dir, | ||||
| @@ -1480,29 +1500,22 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // TODO combine these below? | ||||
|     // setup file struct | ||||
|     file->pair[0] = cwd.pair[0]; | ||||
|     file->pair[1] = cwd.pair[1]; | ||||
|     file->poff = entry.off; | ||||
|     file->head = entry.d.u.file.head; | ||||
|     file->size = entry.d.u.file.size; | ||||
|     file->flags = flags; | ||||
|     file->pos = 0; | ||||
|  | ||||
|     if (flags & LFS_O_TRUNC) { | ||||
|         if (file->size != 0) { | ||||
|             file->flags |= LFS_F_DIRTY; | ||||
|         } | ||||
|     // calculate max inline size based on the size of the entry | ||||
|     file->inline_size = lfs_min(lfs->inline_size, | ||||
|         lfs->cfg->block_size - (sizeof(cwd.d)+4) - | ||||
|         (lfs_entry_size(&entry) - lfs_entry_elen(&entry))); | ||||
|  | ||||
|         entry.d.type = LFS_STRUCT_INLINE | LFS_TYPE_REG; | ||||
|         entry.d.elen = 0; | ||||
|     } | ||||
|  | ||||
|     // load inline files | ||||
|     if ((0x70 & entry.d.type) == LFS_STRUCT_INLINE) { | ||||
|         // load inline files | ||||
|         file->head = 0xfffffffe; | ||||
|         file->size = entry.d.elen; | ||||
|         file->size = lfs_entry_elen(&entry); | ||||
|         file->flags |= LFS_F_INLINE; | ||||
|         file->cache.block = file->head; | ||||
|         file->cache.off = 0; | ||||
| @@ -1513,6 +1526,23 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | ||||
|             lfs_free(file->cache.buffer); | ||||
|             return err; | ||||
|         } | ||||
|     } else { | ||||
|         // use ctz list from entry | ||||
|         file->head = entry.d.u.file.head; | ||||
|         file->size = entry.d.u.file.size; | ||||
|     } | ||||
|  | ||||
|     // truncate if requested | ||||
|     if (flags & LFS_O_TRUNC) { | ||||
|         if (file->size != 0) { | ||||
|             file->flags |= LFS_F_DIRTY; | ||||
|         } | ||||
|  | ||||
|         file->head = 0xfffffffe; | ||||
|         file->size = 0; | ||||
|         file->flags |= LFS_F_INLINE; | ||||
|         file->cache.block = file->head; | ||||
|         file->cache.off = 0; | ||||
|     } | ||||
|  | ||||
|     // add to list of files | ||||
| @@ -1686,8 +1716,8 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { | ||||
|         } | ||||
|  | ||||
|         LFS_ASSERT((0xf & entry.d.type) == LFS_TYPE_REG); | ||||
|         lfs_size_t oldlen = entry.d.elen; | ||||
|         entry.size = 4 + entry.d.elen + entry.d.alen + entry.d.nlen; | ||||
|         lfs_size_t oldlen = lfs_entry_elen(&entry); | ||||
|         entry.size = lfs_entry_size(&entry); | ||||
|  | ||||
|         // either update the references or inline the whole file | ||||
|         if (!(file->flags & LFS_F_INLINE)) { | ||||
| @@ -1704,7 +1734,8 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { | ||||
|             } | ||||
|         } else { | ||||
|             entry.d.type = LFS_STRUCT_INLINE | LFS_TYPE_REG; | ||||
|             entry.d.elen = file->size; | ||||
|             entry.d.elen = file->size & 0xff; | ||||
|             entry.d.alen = (entry.d.alen & 0x3f) | ((file->size >> 2) & 0xc0); | ||||
|  | ||||
|             err = lfs_dir_set(lfs, &cwd, &entry, (struct lfs_region[]){ | ||||
|                     {LFS_FROM_MEM, 0, &entry.d, 4}, | ||||
| @@ -1822,7 +1853,7 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, | ||||
|     // TODO store INLINE_MAX in superblock? | ||||
|     // TODO what if inline files is > block size (ie 128) | ||||
|     if ((file->flags & LFS_F_INLINE) && | ||||
|             file->pos + nsize >= lfs->inline_size) { | ||||
|             file->pos + nsize >= file->inline_size) { | ||||
|         file->block = 0xfffffffe; | ||||
|         file->off = file->pos; | ||||
|  | ||||
| @@ -2034,7 +2065,7 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { | ||||
|     if (entry.d.type == (LFS_STRUCT_CTZ | LFS_TYPE_REG)) { | ||||
|         info->size = entry.d.u.file.size; | ||||
|     } else if (entry.d.type == (LFS_STRUCT_INLINE | LFS_TYPE_REG)) { | ||||
|         info->size = entry.d.elen; | ||||
|         info->size = lfs_entry_elen(&entry); | ||||
|     } | ||||
|  | ||||
|     if (lfs_paircmp(entry.d.u.dir, lfs->root) == 0) { | ||||
| @@ -2103,7 +2134,7 @@ int lfs_remove(lfs_t *lfs, const char *path) { | ||||
|         cwd.d.tail[0] = dir.d.tail[0]; | ||||
|         cwd.d.tail[1] = dir.d.tail[1]; | ||||
|  | ||||
|         err = lfs_dif_commit(lfs, &cwd, NULL, 0); | ||||
|         err = lfs_dir_commit(lfs, &cwd, NULL, 0); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
| @@ -2234,7 +2265,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|         newcwd.d.tail[0] = dir.d.tail[0]; | ||||
|         newcwd.d.tail[1] = dir.d.tail[1]; | ||||
|  | ||||
|         err = lfs_dif_commit(lfs, &newcwd, NULL, 0); | ||||
|         err = lfs_dir_commit(lfs, &newcwd, NULL, 0); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
| @@ -2364,7 +2395,7 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     err = lfs_dif_commit(lfs, &root, NULL, 0); | ||||
|     err = lfs_dir_commit(lfs, &root, NULL, 0); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
| @@ -2446,14 +2477,14 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     memset(&superblock.d, 0, sizeof(superblock.d)); | ||||
|     err = lfs_dir_get(lfs, &dir, | ||||
|             sizeof(dir.d)+4, &superblock.d, | ||||
|             lfs_min(sizeof(superblock.d), entry.d.elen)); | ||||
|             lfs_min(sizeof(superblock.d), lfs_entry_elen(&entry))); | ||||
|     lfs_superblock_fromle32(&superblock.d); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     err = lfs_dir_get(lfs, &dir, | ||||
|             sizeof(dir.d)+4+entry.d.elen+entry.d.alen, magic, | ||||
|             sizeof(dir.d)+lfs_entry_size(&entry)-entry.d.nlen, magic, | ||||
|             lfs_min(sizeof(magic), entry.d.nlen)); | ||||
|     if (err) { | ||||
|         return err; | ||||
| @@ -2546,7 +2577,7 @@ int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) { | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             dir.off += 4 + entry.d.elen + entry.d.alen + entry.d.nlen; | ||||
|             dir.off += lfs_entry_size(&entry); | ||||
|             if ((0x70 & entry.d.type) == LFS_STRUCT_CTZ) { | ||||
|                 err = lfs_ctz_traverse(lfs, &lfs->rcache, NULL, | ||||
|                         entry.d.u.file.head, entry.d.u.file.size, cb, data); | ||||
| @@ -2730,7 +2761,7 @@ static int lfs_relocate(lfs_t *lfs, | ||||
|         parent.d.tail[0] = newpair[0]; | ||||
|         parent.d.tail[1] = newpair[1]; | ||||
|  | ||||
|         return lfs_dif_commit(lfs, &parent, NULL, 0); | ||||
|         return lfs_dir_commit(lfs, &parent, NULL, 0); | ||||
|     } | ||||
|  | ||||
|     // couldn't find dir, must be new | ||||
| @@ -2772,7 +2803,7 @@ int lfs_deorphan(lfs_t *lfs) { | ||||
|                 pdir.d.tail[0] = cwd.d.tail[0]; | ||||
|                 pdir.d.tail[1] = cwd.d.tail[1]; | ||||
|  | ||||
|                 err = lfs_dif_commit(lfs, &pdir, NULL, 0); | ||||
|                 err = lfs_dir_commit(lfs, &pdir, NULL, 0); | ||||
|                 if (err) { | ||||
|                     return err; | ||||
|                 } | ||||
| @@ -2788,7 +2819,7 @@ int lfs_deorphan(lfs_t *lfs) { | ||||
|                 pdir.d.tail[0] = entry.d.u.dir[0]; | ||||
|                 pdir.d.tail[1] = entry.d.u.dir[1]; | ||||
|  | ||||
|                 err = lfs_dif_commit(lfs, &pdir, NULL, 0); | ||||
|                 err = lfs_dir_commit(lfs, &pdir, NULL, 0); | ||||
|                 if (err) { | ||||
|                     return err; | ||||
|                 } | ||||
|   | ||||
							
								
								
									
										7
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -52,17 +52,17 @@ typedef uint32_t lfs_block_t; | ||||
|  | ||||
| // Maximum inline file size in bytes | ||||
| #ifndef LFS_INLINE_MAX | ||||
| #define LFS_INLINE_MAX 255 | ||||
| #define LFS_INLINE_MAX 0x3ff | ||||
| #endif | ||||
|  | ||||
| // Maximum size of all attributes per file in bytes | ||||
| #ifndef LFS_ATTRS_MAX | ||||
| #define LFS_ATTRS_MAX 255 | ||||
| #define LFS_ATTRS_MAX 0x3f | ||||
| #endif | ||||
|  | ||||
| // Max name size in bytes | ||||
| #ifndef LFS_NAME_MAX | ||||
| #define LFS_NAME_MAX 255 | ||||
| #define LFS_NAME_MAX 0xff | ||||
| #endif | ||||
|  | ||||
| // Possible error codes, these are negative to allow | ||||
| @@ -248,6 +248,7 @@ typedef struct lfs_file { | ||||
|     lfs_size_t size; | ||||
|  | ||||
|     uint32_t flags; | ||||
|     lfs_size_t inline_size; | ||||
|     lfs_off_t pos; | ||||
|     lfs_block_t block; | ||||
|     lfs_off_t off; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user