-
Notifications
You must be signed in to change notification settings - Fork 866
Manage generic attributes #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
This is very cool! So this adds the modification time as an attribute to the info structure? I wonder if it would be possible to make the API more generic for different types of attributes. One concern is if this adds to the code size. It'd be interesting to see if it's possible to add attributes without changing the existing functions, since that would allow those functions to be completely gced at compile time if they aren't used. |
Exactely with this modification I have the last modification time for each entry, which was mandatory for me. In order to get a generic API, It is exactely what I tried to do when I modified the littlefs lib |
Would love to see some more examples/documentation on how to implement these attributes. Timestamp would be very nice for a file. |
Here is an example: #31 |
:D I've seen your example I was just wondering if there's a documented example of exactly what/why the changes are! Because, well, lazy ;) |
These changes implement the attributes planned in littlefs but were not implemented and force to 0 by default. |
Sorry I haven't had time yet to add actual support for custom attributes. I'm still impressed you managed to get the timestamp attributes working with the implementation as is. Instead of adding attributes specific to timestamps, what if we added generic functions that could be used for any type of attribute? (roughly based on getxattr and friends). // note, attributes types are limited to 8-bits,
int lfs_attr_get(lfs_t *lfs, const char *path, uint8_t type, void *buffer, size_t size);
int lfs_attr_set(lfs_t *lfs, const char *path, uint8_t type, const void *buffer, size_t size);
int lfs_attr_remove(lfs_t *lfs, const char *path, uint8_t type); For example, a system level wrapper could add attributes as needed (sorry for my pseduocode): #define SYSTEM_ATTR_TIME 0x10
int system_file_open(const char *path) {
lfs_file_open(&lfs, &file, path, O_RDWR);
struct system_time current_time = get_time();
lfs_attr_set(&lfs, path, SYSTEM_ATTR_TIME, ¤t_time, sizeof(current_time));
}
int system_file_stat(const char *path, struct system_info *sysinfo) {
struct lfs_info info;
lfs_stat(path, &info);
copy_to_system_info(&info, sysinfo);
lfs_attr_get(&lfs, path, SYSTEM_ATTR_TIME, &sysinfo.create_time, sizeof(sysinfo.create_time));
} Could these work as a replacement for building timestamps into littlefs? What do you think? |
I like! The easier it is to add/retrieve these attributes the better from my point of view. |
Any news about implemening generic attributes ? |
Hi, sorry I've been silent for a while. I didn't want to make too much noise until I actually had something to show, and I still don't quite yet. Adding custom attribute has been surprisingly tricky because right now the littlefs driver doesn't currently support resizing metadata entries. This required some fundamental changes to the internals of littlefs, so it's required a decent bit of work. I've been working on adding resizable entries on these two branches as a part of adding inline files: After getting resizable entries in, custom attributes should come in shortly after. |
Thank you, I hope it will be ready soon. In the meantime, I have implemented littlefs with atributes support (for timestamp) based on guillaumerems's PR in in my MicroPython for ESP32. It works great, I've had no issues so far. |
@loboris good to hear ;) |
Finally got around to implementing custom attributes. Unfortunately hit an interesting problem. The attr get/set/remove functions I outlined above work well until you care about atomic operations (prototype here). If you call setattr with a timestamp and then close the file, if power is lost you might get only the timestamp update. For timestamps this might not be a big deal, but if custom attributes are used for, say, encryption, the user might lose their file. littlefs could buffer setattr calls, and write them out atomically, but this would require more RAM... So here's what I'm thinking. littlefs provides an lfs_file_setattrs function that takes an array of attributes which all get written out with the file update. The tricky part here is that it's up to the user to keep this memory backing these attributes allocated until the file is closed. Here's what the API might look like: // Custom attribute structure
struct lfs_attr {
// Type of attribute, provided by user and used to identify attribute
uint8_t type;
// Pointer to buffer containing the attribute
void *data;
// Size of attribute in bytes, limited to LFS_ATTRS_MAX
lfs_size_t size;
};
// 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, 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.
//
// Note: Attributes are not actually 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, struct lfs_attr *attrs, int count); And here's what it could look like to add timestamps with your own file type: typedef struct time_file {
lfs_file_t file;
uint32_t timestamp;
struct lfs_attr attrs[1];
} time_file_t;
int time_file_open(lfs_t *lfs, time_file_t *file, char *path, int flags) {
lfs_file_open(lfs, &file->file, path, flags);
file->attrs[0].type = LFS_ATTR_TIMESTAMP;
file->attrs[0].data = &file->timestamp;
file->attrs[0].size = sizeof(file->timestamp);
lfs_file_getattrs(lfs, &file->file, file->attrs, 1); // load attrs from disk
lfs_file_setattrs(lfs, &file->file, file->attrs, 1); // setup file attrs to be used when file is written
}
int time_file_write(lfs_t *lfs, struct time_file *file, void *buffer, size_t size) {
file->timestamp = now(NULL);
lfs_file_write(file->lfs, &file->file, buffer, size);
} There should probably also be standalone lfs_getattrs and lfs_setattrs functions using just a path: uint32_t timestamp;
lfs_getattrs(&lfs, "/hi", (struct lfs_attr[]){
{LFS_ATTR_TIMESTAMP, ×tamp, sizeof(timestamp)}}, 1); It's a bit complicated, but gets the job done. What do you guys think? |
It still needs a lot of work (mostly testing, I wouldn't try it out yet). But here's an pr for the lfs_setattrs/lfs_getattrs functions: #48 |
Thanks @geky I will test it |
Oh, I hadn't thought about that... Hold on, right now it will probably break. I should add a check that ignores malformed attributes. (I added a length to for each attribute). Would it work if your existing attributes are just ignored? |
Of course we have to manage incorrect attributes. I read quickly the code and it looks good, I will try to run it in the next days. |
Yeah, my bad. Thanks for pointing it out. Unfortunately this does break what's documented in the SPEC.md. Documenting the attribute region early was a mistake on my part. Not storing the length on each attribute will make it difficult in the future for two systems to share a filesystem if they have different attributes. (for example mounting an embedded device on a PC). Do you think it's not worth breaking the SPEC? I don't know how many people are using custom attributes at the moment. At least it's easy to make the system ignore the old attributes. Though you may need to change the id you're using to identify the timestamp. |
No problem I will handle it, it is not critical for the moment. |
Hello! |
Hi @husigeza, sorry for the delay on this. At the moment I would guess it's roughly a month out. #48 is nearly ready to go, but it required a large change to the internal implementation that increased the complexity and code size. I'm currently looking into an alternative design that may improve this (it's a mess, but currently the prototype is over here). Unfortunately it's taken longer than I had hoped to see if it's a feasible solution. That's the main reason for the stagnation. I'm trying to avoid merging new code until I'm sure it's the best path forward. Sorry for any inconveniences this is causing. |
Ok! Sorry about the delay on this, but now #85 is looking close to merging. It's a big change, so it's probably going to take another week at least before it's ready to go in. It also comes as a breaking change for various reasons. At least for you @guillaumerems this isn't that much worse than breaking the SPEC. Since you took the time to follow it, the least I can do is make it as bad an experience for everyone else as for you. I am hoping to make this jump as smooth as it can be. I'm going to create an upgrade function before bringing #85 in. Though this may have a significant code cost. There were also a few changes to the API if you all have any feedback on them:
|
Thanks @geky on the hard job you have done, Do you expect next littlefs (release 2) retro-compatible with current version without breaking the low-level existing storage ? |
Unfortunately no, #85 (v2) is going to break disk compatibility. This is a big downside, but it allows us to fix several limitations of the initial design. Some of the goal is to get these changes in while littlefs is in its infancy, and hopefully v2 will be able to last much longer than v1. One thing I have planned is to create an "upgrade" function before the release. Because this update only changes the metadata, it should be possible to upgrade in-place without additional storage. However, this will require both versions to be compiled into the codebase to upgrade. |
Ok, it's gone through a few iterations, but custom attributes are now available on master as a part of v2 (#85). Here's how you might create timestamps with a custom file wrapper: #define ATTR_TIMESTAMP 0x74
typedef struct time_file {
lfs_file_t file;
uint32_t timestamp;
struct lfs_attr attrs[1];
struct lfs_file_config cfg;
} time_file_t;
int time_file_open(lfs_t *lfs, time_file_t *file, char *path, int flags) {
// set up description of timestamp attribute
file->attrs[0].type = ATTR_TIMESTAMP;
file->attrs[0].buffer = &file->timestamp;
file->attrs[0].size = sizeof(&file->timestamp);
// set up config to indicate file has custom attributes
memset(&file->cfg, 0, sizeof(file->cfg));
file->cfg->attrs = &file->attrs;
file->cfg->attr_count = 1;
// attributes will be automatically populated during open call
return lfs_file_opencfg(lfs, &file->file, path, flags, &file->cfg);
}
int time_file_write(lfs_t *lfs, struct time_file *file, void *buffer, size_t size) {
// update timestamp
file->timestamp = now(NULL);
lfs_file_write(file->lfs, &file->file, buffer, size);
// note that like file data, custom attributes are not actually written to disk until file sync or file close
} Let me know any questions/concerns anyone has. And i'd be happy to hear if anyone uses this successfully. |
Use 't' for LITTLEFS_ATTR_MTIME to match example: littlefs-project/littlefs#23 (comment) And to match other external tools such as: https://github.com/earlephilhower/mklittlefs
Hi all Thanks |
As the attributes are not used by default in littlefs but it is intended to be used depending on the implementation.
In my project I use the attributes to store the modification date of an entry.
Here is my modification I have made on lfs to implement it:
In lfs_mkdir:
In lfs_dir_read
In lfs_file_open
In lfs_file_sync
And then the 2 function to generate and extract the attributes
What do you think to include this in littlefs by default, to manage the attributes and let the developer to implement its custom attributes ?
The text was updated successfully, but these errors were encountered: