mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-11-01 00:38:29 +01:00 
			
		
		
		
	WIP Clumsy setattrs/getattrs
This commit is contained in:
		
							
								
								
									
										232
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										232
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -515,6 +515,7 @@ struct lfs_region { | ||||
|         LFS_FROM_DROP, | ||||
|         LFS_FROM_MEM, | ||||
|         LFS_FROM_REGION, | ||||
|         LFS_FROM_ATTRS, | ||||
|     } type; | ||||
|  | ||||
|     lfs_off_t off; | ||||
| @@ -522,6 +523,12 @@ struct lfs_region { | ||||
|     lfs_ssize_t size; | ||||
| }; | ||||
|  | ||||
| struct lfs_attrs_region { | ||||
|     const struct lfs_attr *attrs; | ||||
|     int count; | ||||
|     lfs_size_t len; | ||||
| }; | ||||
|  | ||||
| struct lfs_region_region { | ||||
|     lfs_block_t block; | ||||
|     lfs_off_t off; | ||||
| @@ -568,6 +575,73 @@ static int lfs_commit_region(lfs_t *lfs, | ||||
|                     newoff += regions[i].size; | ||||
|                     break; | ||||
|                 } | ||||
|                 case LFS_FROM_ATTRS: { | ||||
|                     const struct lfs_attrs_region *attrs = regions[i].buffer; | ||||
|  | ||||
|                     // order doesn't matter, so we write new attrs first. this | ||||
|                     // is still O(n^2) but only O(n) disk access | ||||
|                     for (int j = 0; j < attrs->count; j++) { | ||||
|                         if (attrs->attrs[j].size == 0) { | ||||
|                             continue; | ||||
|                         } | ||||
|  | ||||
|                         lfs_entry_attr_t attr = { | ||||
|                             .d.type = attrs->attrs[j].type, | ||||
|                             .d.len = attrs->attrs[j].size, | ||||
|                         }; | ||||
|  | ||||
|                         lfs_crc(crc, &attr.d, sizeof(attr.d)); | ||||
|                         int err = lfs_bd_prog(lfs, newblock, newoff, | ||||
|                                 &attr.d, sizeof(attr.d)); | ||||
|                         if (err) { | ||||
|                             return err; | ||||
|                         } | ||||
|  | ||||
|                         lfs_crc(crc, | ||||
|                                 attrs->attrs[j].buffer, attrs->attrs[j].size); | ||||
|                         err = lfs_bd_prog(lfs, newblock, newoff+sizeof(attr.d), | ||||
|                                 attrs->attrs[j].buffer, attrs->attrs[j].size); | ||||
|                         if (err) { | ||||
|                             return err; | ||||
|                         } | ||||
|  | ||||
|                         newoff += sizeof(attr.d) + attrs->attrs[j].size; | ||||
|                     } | ||||
|  | ||||
|                     // copy over attributes without updates | ||||
|                     lfs_entry_attr_t attr; | ||||
|                     for (lfs_off_t k = 0; k < attrs->len; k += 2+attr.d.len) { | ||||
|                         int err = lfs_bd_read(lfs, oldblock, oldoff, | ||||
|                                 &attr.d, sizeof(attr.d)); | ||||
|                         if (err) { | ||||
|                             return err; | ||||
|                         } | ||||
|  | ||||
|                         bool updating = false; | ||||
|                         for (int j = 0; j < attrs->count; j++) { | ||||
|                             if (attr.d.type == attrs->attrs[j].type) { | ||||
|                                 updating = true; | ||||
|                             } | ||||
|                         } | ||||
|  | ||||
|                         if (!updating) { | ||||
|                             err = lfs_commit_region(lfs, | ||||
|                                     oldblock, oldoff, | ||||
|                                     newblock, newoff, | ||||
|                                     0, NULL, 0, | ||||
|                                     attr.d.len, crc); | ||||
|                             if (err) { | ||||
|                                 return err; | ||||
|                             } | ||||
|  | ||||
|                             newoff += 2+attr.d.len; | ||||
|                         } | ||||
|  | ||||
|                         oldoff += 2+attr.d.len; | ||||
|                     } | ||||
|  | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             i += 1; | ||||
| @@ -590,6 +664,8 @@ static int lfs_commit_region(lfs_t *lfs, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // sanity check our commit math | ||||
|     LFS_ASSERT(newoff == end); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| @@ -1044,117 +1120,101 @@ static int lfs_dir_getinfo(lfs_t *lfs, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int lfs_dir_getattr(lfs_t *lfs, | ||||
| static int lfs_dir_getattrs(lfs_t *lfs, | ||||
|         lfs_dir_t *dir, const lfs_entry_t *entry, | ||||
|         uint8_t type, void *buffer, lfs_size_t size) { | ||||
|         const struct lfs_attr *attrs, int count) { | ||||
|     // set to zero in case we can't find the attributes or size mismatch | ||||
|     for (int j = 0; j < count; j++) { | ||||
|         memset(attrs[j].buffer, 0, attrs[j].size); | ||||
|     } | ||||
|  | ||||
|     // search for attribute in attribute region | ||||
|     lfs_off_t off = sizeof(dir->d) + lfs_entry_elen(entry); | ||||
|     lfs_off_t i = 0; | ||||
|     while (i < lfs_entry_alen(entry)) { | ||||
|         lfs_attr_t attr; | ||||
|     lfs_off_t off = 4+lfs_entry_elen(entry); | ||||
|     lfs_entry_attr_t attr; | ||||
|     for (lfs_off_t i = 0; i < lfs_entry_alen(entry); i += 2+attr.d.len) { | ||||
|         int err = lfs_dir_get(lfs, dir, | ||||
|                 entry->off+off+i, &attr.d, sizeof(attr.d)); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         if (attr.d.type != type) { | ||||
|             i += attr.d.len; | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         if (attr.d.len > size) { | ||||
|         for (int j = 0; j < count; j++) { | ||||
|             if (attr.d.type == attrs[j].type) { | ||||
|                 if (attr.d.len > attrs[j].size) { | ||||
|                     return LFS_ERR_RANGE; | ||||
|                 } | ||||
|  | ||||
|                 err = lfs_dir_get(lfs, dir, | ||||
|                 entry->off+off+i+sizeof(attr.d), buffer, attr.d.len); | ||||
|                         entry->off+off+i+sizeof(attr.d), | ||||
|                         attrs[j].buffer, attr.d.len); | ||||
|                 if (err) { | ||||
|                     return err; | ||||
|                 } | ||||
|  | ||||
|         return attr.d.len; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return LFS_ERR_NODATA; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int lfs_dir_setattr(lfs_t *lfs, | ||||
| static lfs_ssize_t lfs_dir_checkattrs(lfs_t *lfs, | ||||
|         lfs_dir_t *dir, lfs_entry_t *entry, | ||||
|         uint8_t type, const void *buffer, lfs_size_t size) { | ||||
|     // search for attribute in attribute region | ||||
|     lfs_off_t off = sizeof(dir->d) + lfs_entry_elen(entry); | ||||
|     lfs_off_t i = 0; | ||||
|     lfs_size_t oldlen = 0; | ||||
|     while (i < lfs_entry_alen(entry)) { | ||||
|         lfs_attr_t attr; | ||||
|         const struct lfs_attr *attrs, int count) { | ||||
|     // check that attributes fit | ||||
|     lfs_size_t nsize = 0; | ||||
|     for (int j = 0; j < count; j++) { | ||||
|         nsize += 2+attrs[j].size; | ||||
|     } | ||||
|  | ||||
|     lfs_off_t off = 4+lfs_entry_elen(entry); | ||||
|     lfs_entry_attr_t attr; | ||||
|     for (lfs_off_t i = 0; i < lfs_entry_alen(entry); i += 2+attr.d.len) { | ||||
|         int err = lfs_dir_get(lfs, dir, | ||||
|                 entry->off+off+i, &attr.d, sizeof(attr.d)); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         if (attr.d.type != type) { | ||||
|             i += attr.d.len; | ||||
|             continue; | ||||
|         bool updated = false; | ||||
|         for (int j = 0; j < count; j++) { | ||||
|             if (attr.d.type == attrs[j].type) { | ||||
|                 updated = true; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         oldlen = attr.d.len; | ||||
|         break; | ||||
|         if (!updated) { | ||||
|             nsize += 2+attr.d.len; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // make sure the attribute fits | ||||
|     if (lfs_entry_elen(entry) - oldlen + size > lfs->attrs_size || | ||||
|         (0x7fffffff & dir->d.size) - oldlen + size > lfs->cfg->block_size) { | ||||
|     if (nsize > lfs->attrs_size || ( | ||||
|             (0x7fffffff & dir->d.size) + lfs_entry_size(entry) - | ||||
|             lfs_entry_alen(entry) + nsize > lfs->cfg->block_size)) { | ||||
|         return LFS_ERR_NOSPC; | ||||
|     } | ||||
|  | ||||
|     lfs_attr_t attr; | ||||
|     attr.d.type = type; | ||||
|     attr.d.len = size; | ||||
|     int err = lfs_dir_set(lfs, dir, entry, (struct lfs_region[]){ | ||||
|             {LFS_FROM_MEM, off+i, &attr.d, sizeof(attr.d)}, | ||||
|             {LFS_FROM_MEM, off+i, buffer, size}, | ||||
|             {LFS_FROM_DROP, off+i, NULL, -oldlen}}, 3); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     return nsize; | ||||
| } | ||||
|  | ||||
|     return 0; | ||||
| static int lfs_dir_setattrs(lfs_t *lfs, | ||||
|         lfs_dir_t *dir, lfs_entry_t *entry, | ||||
|         const struct lfs_attr *attrs, int count) { | ||||
|     // make sure attributes fit | ||||
|     lfs_ssize_t nsize = lfs_dir_checkattrs(lfs, dir, entry, attrs, count); | ||||
|     if (nsize < 0) { | ||||
|         return nsize; | ||||
|     } | ||||
|  | ||||
| static int lfs_dir_removeattr(lfs_t *lfs, | ||||
|         lfs_dir_t *dir, lfs_entry_t *entry, uint8_t type) { | ||||
|     // search for attribute in attribute region | ||||
|     lfs_off_t off = sizeof(dir->d) + lfs_entry_elen(entry); | ||||
|     lfs_off_t i = 0; | ||||
|     while (i < lfs_entry_alen(entry)) { | ||||
|         lfs_attr_t attr; | ||||
|         int err = lfs_dir_get(lfs, dir, | ||||
|                 entry->off+off+i, &attr.d, sizeof(attr.d)); | ||||
|         if (err) { | ||||
|             return err; | ||||
|     // commit to entry, majority of work is in LFS_FROM_ATTRS | ||||
|     lfs_size_t oldlen = lfs_entry_alen(entry); | ||||
|     entry->d.alen = (0xc0 & entry->d.alen) | nsize; | ||||
|     return lfs_dir_set(lfs, dir, entry, (struct lfs_region[]){ | ||||
|             {LFS_FROM_MEM, 0, &entry->d, 4}, | ||||
|             {LFS_FROM_DROP, 0, NULL, -4}, | ||||
|             {LFS_FROM_ATTRS, 4+lfs_entry_elen(entry), | ||||
|                 &(struct lfs_attrs_region){attrs, count, oldlen}, nsize}}, 3); | ||||
| } | ||||
|  | ||||
|         if (attr.d.type != type) { | ||||
|             i += attr.d.len; | ||||
|             continue; | ||||
|         } | ||||
|  | ||||
|         err = lfs_dir_set(lfs, dir, entry, (struct lfs_region[]){ | ||||
|                 {LFS_FROM_DROP, off+i, | ||||
|                     NULL, -(sizeof(attr.d)+attr.d.len)}}, 1); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     return LFS_ERR_NODATA; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| /// Top level directory operations /// | ||||
| int lfs_mkdir(lfs_t *lfs, const char *path) { | ||||
| @@ -2372,8 +2432,8 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|  | ||||
|  | ||||
| /// Attribute operations /// | ||||
| int lfs_getattr(lfs_t *lfs, const char *path, | ||||
|         uint8_t type, void *buffer, lfs_size_t size) { | ||||
| int lfs_getattrs(lfs_t *lfs, const char *path, | ||||
|         const struct lfs_attr *attrs, int count) { | ||||
|     lfs_dir_t cwd; | ||||
|     int err = lfs_dir_fetch(lfs, &cwd, lfs->root); | ||||
|     if (err) { | ||||
| @@ -2386,11 +2446,11 @@ int lfs_getattr(lfs_t *lfs, const char *path, | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     return lfs_dir_getattr(lfs, &cwd, &entry, type, buffer, size); | ||||
|     return lfs_dir_getattrs(lfs, &cwd, &entry, attrs, count); | ||||
| } | ||||
|  | ||||
| int lfs_setattr(lfs_t *lfs, const char *path, | ||||
|         uint8_t type, const void *buffer, lfs_size_t size) { | ||||
| int lfs_setattrs(lfs_t *lfs, const char *path, | ||||
|         const struct lfs_attr *attrs, int count) { | ||||
|     lfs_dir_t cwd; | ||||
|     int err = lfs_dir_fetch(lfs, &cwd, lfs->root); | ||||
|     if (err) { | ||||
| @@ -2403,23 +2463,7 @@ int lfs_setattr(lfs_t *lfs, const char *path, | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     return lfs_dir_setattr(lfs, &cwd, &entry, type, buffer, size); | ||||
| } | ||||
|  | ||||
| int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type) { | ||||
|     lfs_dir_t cwd; | ||||
|     int err = lfs_dir_fetch(lfs, &cwd, lfs->root); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     lfs_entry_t entry; | ||||
|     err = lfs_dir_find(lfs, &cwd, &entry, &path); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     return lfs_dir_removeattr(lfs, &cwd, &entry, type); | ||||
|     return lfs_dir_setattrs(lfs, &cwd, &entry, attrs, count); | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										94
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										94
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -234,6 +234,18 @@ struct lfs_info { | ||||
|     char name[LFS_NAME_MAX+1]; | ||||
| }; | ||||
|  | ||||
| // Custom attribute structure | ||||
| struct lfs_attr { | ||||
|     // Type of attribute, provided by user and used to identify the attribute | ||||
|     uint8_t type; | ||||
|  | ||||
|     // Pointer to buffer containing the attribute | ||||
|     void *buffer; | ||||
|  | ||||
|     // Size of attribute in bytes, limited to LFS_ATTRS_MAX | ||||
|     lfs_size_t size; | ||||
| }; | ||||
|  | ||||
|  | ||||
| /// littlefs data structures /// | ||||
| typedef struct lfs_entry { | ||||
| @@ -255,12 +267,12 @@ typedef struct lfs_entry { | ||||
|     } d; | ||||
| } lfs_entry_t; | ||||
|  | ||||
| typedef struct lfs_attr { | ||||
|     struct lfs_disk_attr { | ||||
| typedef struct lfs_entry_attr { | ||||
|     struct lfs_disk_entry_attr { | ||||
|         uint8_t type; | ||||
|         uint8_t len; | ||||
|     } d; | ||||
| } lfs_attr_t; | ||||
| } lfs_entry_attr_t; | ||||
|  | ||||
| typedef struct lfs_cache { | ||||
|     lfs_block_t block; | ||||
| @@ -388,28 +400,25 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath); | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info); | ||||
|  | ||||
| // Get a custom attribute | ||||
| // Get custom attributes | ||||
| // | ||||
| // Attributes are identified by an 8-bit type and are limited to at | ||||
| // most LFS_ATTRS_SIZE bytes. | ||||
| // Returns the size of the attribute, or a negative error code on failure. | ||||
| int lfs_getattr(lfs_t *lfs, const char *path, | ||||
|         uint8_t type, void *buffer, lfs_size_t size); | ||||
|  | ||||
| // Set a custom attribute | ||||
| // Attributes are looked up based on the type id. If the stored attribute is | ||||
| // smaller than the buffer, it is padded with zeros. It the stored attribute | ||||
| // is larger than the buffer, LFS_ERR_RANGE is returned. | ||||
| // | ||||
| // Attributes are identified by an 8-bit type and are limited to at | ||||
| // most LFS_ATTRS_SIZE bytes. | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_setattr(lfs_t *lfs, const char *path, | ||||
|         uint8_t type, const void *buffer, lfs_size_t size); | ||||
| int lfs_getattrs(lfs_t *lfs, const char *path, | ||||
|         const struct lfs_attr *attrs, int count); | ||||
|  | ||||
| // Remove a custom attribute | ||||
| // Set custom attributes | ||||
| // | ||||
| // The array of attributes will be used to update the attributes stored on | ||||
| // disk based on their type id. Unspecified attributes are left unmodified. | ||||
| // Specifying an attribute with zero size deletes the attribute. | ||||
| // | ||||
| // Attributes are identified by an 8-bit type and are limited to at | ||||
| // most LFS_ATTRS_SIZE bytes. | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_removeattr(lfs_t *lfs, const char *path, uint8_t type); | ||||
| int lfs_setattrs(lfs_t *lfs, const char *path, | ||||
|         const struct lfs_attr *attrs, int count); | ||||
|  | ||||
|  | ||||
| /// File operations /// | ||||
| @@ -484,6 +493,30 @@ int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file); | ||||
| // Returns the size of the file, or a negative error code on failure. | ||||
| lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file); | ||||
|  | ||||
| // Get custom attributes attached to a file | ||||
| // | ||||
| // Attributes are looked up based on the type id. If the stored attribute is | ||||
| // smaller than the buffer, it is padded with zeros. It the stored attribute | ||||
| // is larger than the buffer, LFS_ERR_RANGE is returned. | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_file_getattrs(lfs_t *lfs, lfs_file_t *file, | ||||
|         const struct lfs_attr *attrs, int count); | ||||
|  | ||||
| // Set custom attributes on a file | ||||
| // | ||||
| // The array of attributes will be used to update the attributes stored on | ||||
| // disk based on their type id. Unspecified attributes are left unmodified. | ||||
| // Specifying an attribute with zero size deletes the attribute. | ||||
| // | ||||
| // Note: Attributes are not written out until a call to lfs_file_sync | ||||
| // or lfs_file_close and must be allocated until the file is closed or | ||||
| // lfs_file_setattrs is called with a count of zero. | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_file_setattrs(lfs_t *lfs, lfs_file_t *file, | ||||
|         const struct lfs_attr *attrs, int count); | ||||
|  | ||||
|  | ||||
| /// Directory operations /// | ||||
|  | ||||
| @@ -532,6 +565,29 @@ lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir); | ||||
| int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir); | ||||
|  | ||||
|  | ||||
| /// Filesystem filesystem operations /// | ||||
|  | ||||
| // Get custom attributes on the filesystem | ||||
| // | ||||
| // Attributes are looked up based on the type id. If the stored attribute is | ||||
| // smaller than the buffer, it is padded with zeros. It the stored attribute | ||||
| // is larger than the buffer, LFS_ERR_RANGE is returned. | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_fs_getattrs(lfs_t *lfs, const struct lfs_attr *attrs, int count); | ||||
|  | ||||
| // Set custom attributes on the filesystem | ||||
| // | ||||
| // The array of attributes will be used to update the attributes stored on | ||||
| // disk based on their type id. Unspecified attributes are left unmodified. | ||||
| // Specifying an attribute with zero size deletes the attribute. | ||||
| // | ||||
| // Note: Filesystem level attributes are not available for wear-leveling | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_fs_setattrs(lfs_t *lfs, const struct lfs_attr *attrs, int count); | ||||
|  | ||||
|  | ||||
| /// Miscellaneous littlefs specific operations /// | ||||
|  | ||||
| // Traverse through all blocks in use by the filesystem | ||||
|   | ||||
		Reference in New Issue
	
	Block a user