mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-11-01 00:38:29 +01:00 
			
		
		
		
	Added file-level and fs-level custom attribute APIs
In the form of lfs_file_setattr, lfs_file_getattr, lfs_fs_setattr,
lfs_fs_getattr.
This enables atomic updates of custom attributes as described in
6c754c8, and provides a custom attribute API that allows custom attributes
to be stored on the filesystem itself.
			
			
This commit is contained in:
		
							
								
								
									
										193
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										193
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -932,11 +932,11 @@ shift: | |||||||
|     // shift over any files/directories that are affected |     // shift over any files/directories that are affected | ||||||
|     for (lfs_file_t *f = lfs->files; f; f = f->next) { |     for (lfs_file_t *f = lfs->files; f; f = f->next) { | ||||||
|         if (lfs_paircmp(f->pair, dir->pair) == 0) { |         if (lfs_paircmp(f->pair, dir->pair) == 0) { | ||||||
|             if (f->poff == entry->off && entry->size == 0) { |             if (f->pairoff == entry->off && entry->size == 0) { | ||||||
|                 f->pair[0] = 0xffffffff; |                 f->pair[0] = 0xffffffff; | ||||||
|                 f->pair[1] = 0xffffffff; |                 f->pair[1] = 0xffffffff; | ||||||
|             } else if (f->poff > entry->off) { |             } else if (f->pairoff > entry->off) { | ||||||
|                 f->poff += diff; |                 f->pairoff += diff; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -1135,8 +1135,8 @@ static int lfs_dir_getattrs(lfs_t *lfs, | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         for (int j = 0; j < count; j++) { |         for (int j = 0; j < count; j++) { | ||||||
|             if (attr.d.type == attrs[j].type) { |             if (attrs[j].type == attr.d.type) { | ||||||
|                 if (attr.d.len > attrs[j].size) { |                 if (attrs[j].size < attr.d.len) { | ||||||
|                     return LFS_ERR_RANGE; |                     return LFS_ERR_RANGE; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
| @@ -1190,8 +1190,8 @@ static lfs_ssize_t lfs_dir_checkattrs(lfs_t *lfs, | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (nsize > lfs->attrs_size || ( |     if (nsize > lfs->attrs_size || ( | ||||||
|             (0x7fffffff & dir->d.size) + lfs_entry_size(entry) - |             lfs_entry_size(entry) - lfs_entry_alen(entry) + nsize | ||||||
|             lfs_entry_alen(entry) + nsize > lfs->cfg->block_size)) { |                 > lfs->cfg->block_size)) { | ||||||
|         return LFS_ERR_NOSPC; |         return LFS_ERR_NOSPC; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -1688,7 +1688,7 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, | |||||||
|     // setup file struct |     // setup file struct | ||||||
|     file->pair[0] = cwd.pair[0]; |     file->pair[0] = cwd.pair[0]; | ||||||
|     file->pair[1] = cwd.pair[1]; |     file->pair[1] = cwd.pair[1]; | ||||||
|     file->poff = entry.off; |     file->pairoff = entry.off; | ||||||
|     file->flags = flags; |     file->flags = flags; | ||||||
|     file->pos = 0; |     file->pos = 0; | ||||||
|  |  | ||||||
| @@ -1754,10 +1754,10 @@ int lfs_file_close(lfs_t *lfs, lfs_file_t *file) { | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         return err; |         return err; | ||||||
| } |     } | ||||||
|  |  | ||||||
| static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) { |     static int lfs_file_relocate(lfs_t *lfs, lfs_file_t *file) { | ||||||
| relocate:; |     relocate:; | ||||||
|         // just relocate what exists into new block |         // just relocate what exists into new block | ||||||
|         lfs_block_t nblock; |         lfs_block_t nblock; | ||||||
|         int err = lfs_alloc(lfs, &nblock); |         int err = lfs_alloc(lfs, &nblock); | ||||||
| @@ -1800,9 +1800,9 @@ relocate:; | |||||||
|  |  | ||||||
|         file->block = nblock; |         file->block = nblock; | ||||||
|         return 0; |         return 0; | ||||||
| } |     } | ||||||
|  |  | ||||||
| static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { |     static int lfs_file_flush(lfs_t *lfs, lfs_file_t *file) { | ||||||
|         if (file->flags & LFS_F_READING) { |         if (file->flags & LFS_F_READING) { | ||||||
|             file->flags &= ~LFS_F_READING; |             file->flags &= ~LFS_F_READING; | ||||||
|         } |         } | ||||||
| @@ -1892,43 +1892,55 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) { | |||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         lfs_entry_t entry = {.off = file->poff}; |         lfs_entry_t entry = {.off = file->pairoff}; | ||||||
|         err = lfs_dir_get(lfs, &cwd, entry.off, &entry.d, sizeof(entry.d)); |         err = lfs_dir_get(lfs, &cwd, entry.off, &entry.d, sizeof(entry.d)); | ||||||
|         lfs_entry_fromle32(&entry.d); |         lfs_entry_fromle32(&entry.d); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|  |         entry.size = lfs_entry_size(&entry); | ||||||
|  |  | ||||||
|         LFS_ASSERT((0xf & entry.d.type) == LFS_TYPE_REG); |         LFS_ASSERT((0xf & entry.d.type) == LFS_TYPE_REG); | ||||||
|         lfs_size_t oldlen = lfs_entry_elen(&entry); |         lfs_size_t oldelen = lfs_entry_elen(&entry); | ||||||
|         entry.size = lfs_entry_size(&entry); |         lfs_size_t oldalen = lfs_entry_alen(&entry); | ||||||
|  |         const void *buffer; | ||||||
|  |         lfs_size_t size; | ||||||
|  |  | ||||||
|         // either update the references or inline the whole file |         // either update the references or inline the whole file | ||||||
|         if (!(file->flags & LFS_F_INLINE)) { |         if (!(file->flags & LFS_F_INLINE)) { | ||||||
|             entry.d.type = LFS_STRUCT_CTZ | LFS_TYPE_REG; |             entry.d.type = LFS_STRUCT_CTZ | LFS_TYPE_REG; | ||||||
|             entry.d.elen = sizeof(entry.d)-4; |  | ||||||
|             entry.d.u.file.head = file->head; |             entry.d.u.file.head = file->head; | ||||||
|             entry.d.u.file.size = file->size; |             entry.d.u.file.size = file->size; | ||||||
|  |  | ||||||
|             err = lfs_dir_set(lfs, &cwd, &entry, (struct lfs_region[]){ |             buffer = (const uint8_t *)&entry.d + 4; | ||||||
|                     {LFS_FROM_MEM, 0, 4+oldlen, |             size = sizeof(entry.d) - 4; | ||||||
|                         &entry.d, sizeof(entry.d)}}, 1); |  | ||||||
|             if (err) { |  | ||||||
|                 return err; |  | ||||||
|             } |  | ||||||
|         } else { |         } else { | ||||||
|             entry.d.type = LFS_STRUCT_INLINE | LFS_TYPE_REG; |             entry.d.type = LFS_STRUCT_INLINE | LFS_TYPE_REG; | ||||||
|             entry.d.elen = file->size & 0xff; |  | ||||||
|             entry.d.alen = (entry.d.alen & 0x3f) | ((file->size >> 2) & 0xc0); |  | ||||||
|  |  | ||||||
|  |             buffer = file->cache.buffer; | ||||||
|  |             size = file->size; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // get new alen from disk | ||||||
|  |         lfs_ssize_t newalen = lfs_dir_checkattrs(lfs, &cwd, &entry, | ||||||
|  |                 file->attrs, file->attrcount); | ||||||
|  |         if (newalen < 0) { | ||||||
|  |             return newalen; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         entry.d.elen = size & 0xff; | ||||||
|  |         entry.d.alen = (newalen & 0x3f) | ((size >> 2) & 0xc0); | ||||||
|  |  | ||||||
|  |         // write out update | ||||||
|         err = lfs_dir_set(lfs, &cwd, &entry, (struct lfs_region[]){ |         err = lfs_dir_set(lfs, &cwd, &entry, (struct lfs_region[]){ | ||||||
|                     {LFS_FROM_MEM, 0, 0, &entry.d, 4}, |                 {LFS_FROM_MEM, 0, 4, &entry.d, 4}, | ||||||
|                     {LFS_FROM_MEM, 0, 4+oldlen, |                 {LFS_FROM_MEM, 4, oldelen, buffer, size}, | ||||||
|                         file->cache.buffer, file->size}}, 2); |                 {LFS_FROM_ATTRS, 4+oldelen, oldalen, | ||||||
|  |                     &(struct lfs_region_attrs){file->attrs, file->attrcount}, | ||||||
|  |                     newalen}}, 3); | ||||||
|         if (err) { |         if (err) { | ||||||
|             return err; |             return err; | ||||||
|         } |         } | ||||||
|         } |  | ||||||
|  |  | ||||||
|         file->flags &= ~LFS_F_DIRTY; |         file->flags &= ~LFS_F_DIRTY; | ||||||
|     } |     } | ||||||
| @@ -2221,6 +2233,81 @@ lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int lfs_file_getattrs(lfs_t *lfs, lfs_file_t *file, | ||||||
|  |         const struct lfs_attr *attrs, int count) { | ||||||
|  |     // set to null in case we can't find the attrs (missing file?) | ||||||
|  |     for (int j = 0; j < count; j++) { | ||||||
|  |         memset(attrs[j].buffer, 0, attrs[j].size); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // load from disk if we haven't already been deleted | ||||||
|  |     if (!lfs_pairisnull(file->pair)) { | ||||||
|  |         lfs_dir_t cwd; | ||||||
|  |         int err = lfs_dir_fetch(lfs, &cwd, file->pair); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs_entry_t entry = {.off = file->pairoff}; | ||||||
|  |         err = lfs_dir_get(lfs, &cwd, entry.off, &entry.d, sizeof(entry.d)); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |         entry.size = lfs_entry_size(&entry); | ||||||
|  |  | ||||||
|  |         err = lfs_dir_getattrs(lfs, &cwd, &entry, attrs, count); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // override an attrs we have stored locally | ||||||
|  |     for (int i = 0; i < file->attrcount; i++) { | ||||||
|  |         for (int j = 0; j < count; j++) { | ||||||
|  |             if (attrs[j].type == file->attrs[i].type) { | ||||||
|  |                 if (attrs[j].size < file->attrs[i].size) { | ||||||
|  |                     return LFS_ERR_RANGE; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 memcpy(attrs[j].buffer, | ||||||
|  |                         file->attrs[i].buffer, file->attrs[i].size); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs_file_setattrs(lfs_t *lfs, lfs_file_t *file, | ||||||
|  |         const struct lfs_attr *attrs, int count) { | ||||||
|  |     // just tack to the file, will be written at sync time | ||||||
|  |     file->attrs = attrs; | ||||||
|  |     file->attrcount = count; | ||||||
|  |  | ||||||
|  |     // at least make sure attributes fit | ||||||
|  |     if (!lfs_pairisnull(file->pair)) { | ||||||
|  |         lfs_dir_t cwd; | ||||||
|  |         int err = lfs_dir_fetch(lfs, &cwd, file->pair); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         lfs_entry_t entry = {.off = file->pairoff}; | ||||||
|  |         err = lfs_dir_get(lfs, &cwd, entry.off, &entry.d, sizeof(entry.d)); | ||||||
|  |         if (err) { | ||||||
|  |             return err; | ||||||
|  |         } | ||||||
|  |         entry.size = lfs_entry_size(&entry); | ||||||
|  |  | ||||||
|  |         lfs_ssize_t res = lfs_dir_checkattrs(lfs, &cwd, &entry, attrs, count); | ||||||
|  |         if (res < 0) { | ||||||
|  |             return res; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
| /// General fs operations /// | /// General fs operations /// | ||||||
| int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { | int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) { | ||||||
| @@ -2427,8 +2514,6 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| /// Attribute operations /// |  | ||||||
| int lfs_getattrs(lfs_t *lfs, const char *path, | int lfs_getattrs(lfs_t *lfs, const char *path, | ||||||
|         const struct lfs_attr *attrs, int count) { |         const struct lfs_attr *attrs, int count) { | ||||||
|     lfs_dir_t cwd; |     lfs_dir_t cwd; | ||||||
| @@ -2646,10 +2731,6 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|  |  | ||||||
|     // load superblock |     // load superblock | ||||||
|     lfs_dir_t dir; |     lfs_dir_t dir; | ||||||
|     lfs_entry_t entry; |  | ||||||
|     lfs_superblock_t superblock; |  | ||||||
|     char magic[8]; |  | ||||||
|  |  | ||||||
|     err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); |     err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); | ||||||
|     if (err) { |     if (err) { | ||||||
|         if (err == LFS_ERR_CORRUPT) { |         if (err == LFS_ERR_CORRUPT) { | ||||||
| @@ -2658,11 +2739,13 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     err = lfs_dir_get(lfs, &dir, sizeof(dir.d), &entry.d, sizeof(entry.d)); |     lfs_entry_t entry = {.off = sizeof(dir.d)}; | ||||||
|  |     err = lfs_dir_get(lfs, &dir, entry.off, &entry.d, sizeof(entry.d)); | ||||||
|     if (err) { |     if (err) { | ||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     lfs_superblock_t superblock; | ||||||
|     memset(&superblock.d, 0, sizeof(superblock.d)); |     memset(&superblock.d, 0, sizeof(superblock.d)); | ||||||
|     err = lfs_dir_get(lfs, &dir, |     err = lfs_dir_get(lfs, &dir, | ||||||
|             sizeof(dir.d)+4, &superblock.d, |             sizeof(dir.d)+4, &superblock.d, | ||||||
| @@ -2672,6 +2755,7 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | |||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     char magic[8]; | ||||||
|     err = lfs_dir_get(lfs, &dir, |     err = lfs_dir_get(lfs, &dir, | ||||||
|             sizeof(dir.d)+lfs_entry_size(&entry)-entry.d.nlen, magic, |             sizeof(dir.d)+lfs_entry_size(&entry)-entry.d.nlen, magic, | ||||||
|             lfs_min(sizeof(magic), entry.d.nlen)); |             lfs_min(sizeof(magic), entry.d.nlen)); | ||||||
| @@ -2733,7 +2817,7 @@ int lfs_unmount(lfs_t *lfs) { | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| /// Littlefs specific operations /// | /// Internal filesystem filesystem operations /// | ||||||
| int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) { | int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) { | ||||||
|     if (lfs_pairisnull(lfs->root)) { |     if (lfs_pairisnull(lfs->root)) { | ||||||
|         return 0; |         return 0; | ||||||
| @@ -3064,3 +3148,38 @@ int lfs_deorphan(lfs_t *lfs) { | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /// External filesystem filesystem operations /// | ||||||
|  | int lfs_fs_getattrs(lfs_t *lfs, const struct lfs_attr *attrs, int count) { | ||||||
|  |     lfs_dir_t dir; | ||||||
|  |     int err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs_entry_t entry = {.off = sizeof(dir.d)}; | ||||||
|  |     err = lfs_dir_get(lfs, &dir, entry.off, &entry.d, sizeof(entry.d)); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |     entry.size = lfs_entry_size(&entry); | ||||||
|  |  | ||||||
|  |     return lfs_dir_getattrs(lfs, &dir, &entry, attrs, count); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int lfs_fs_setattrs(lfs_t *lfs, const struct lfs_attr *attrs, int count) { | ||||||
|  |     lfs_dir_t dir; | ||||||
|  |     int err = lfs_dir_fetch(lfs, &dir, (const lfs_block_t[2]){0, 1}); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     lfs_entry_t entry = {.off = sizeof(dir.d)}; | ||||||
|  |     err = lfs_dir_get(lfs, &dir, entry.off, &entry.d, sizeof(entry.d)); | ||||||
|  |     if (err) { | ||||||
|  |         return err; | ||||||
|  |     } | ||||||
|  |     entry.size = lfs_entry_size(&entry); | ||||||
|  |  | ||||||
|  |     return lfs_dir_setattrs(lfs, &dir, &entry, attrs, count); | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -283,7 +283,7 @@ typedef struct lfs_cache { | |||||||
| typedef struct lfs_file { | typedef struct lfs_file { | ||||||
|     struct lfs_file *next; |     struct lfs_file *next; | ||||||
|     lfs_block_t pair[2]; |     lfs_block_t pair[2]; | ||||||
|     lfs_off_t poff; |     lfs_off_t pairoff; | ||||||
|  |  | ||||||
|     lfs_block_t head; |     lfs_block_t head; | ||||||
|     lfs_size_t size; |     lfs_size_t size; | ||||||
| @@ -294,6 +294,9 @@ typedef struct lfs_file { | |||||||
|     lfs_block_t block; |     lfs_block_t block; | ||||||
|     lfs_off_t off; |     lfs_off_t off; | ||||||
|     lfs_cache_t cache; |     lfs_cache_t cache; | ||||||
|  |  | ||||||
|  |     const struct lfs_attr *attrs; | ||||||
|  |     int attrcount; | ||||||
| } lfs_file_t; | } lfs_file_t; | ||||||
|  |  | ||||||
| typedef struct lfs_dir { | typedef struct lfs_dir { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user