diff --git a/lfs.c b/lfs.c index d35d5d6db..3647c72fd 100644 --- a/lfs.c +++ b/lfs.c @@ -6235,6 +6235,38 @@ lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) { return res; } +lfs_ssize_t lfs_file_getattr(lfs_t *lfs, lfs_file_t *file, + uint8_t type, void *buffer, lfs_size_t size) +{ + int err = LFS_LOCK(lfs->cfg); + if (err) { + return err; + } + LFS_TRACE("lfs_file_setattr(%p, %p)", (void*)lfs, (void*)file); + LFS_TRACE("lfs_file_setattr(%"PRIu8", %p, %"PRIu32")", + type, buffer, size); + LFS_ASSERT(lfs_mlist_isopen(lfs->mlist, (struct lfs_mlist*)file)); + + if (file->cfg && file->cfg->attrs) { + for (unsigned i = 0; i < file->cfg->attr_count; i++) { + const struct lfs_attr *attr = &file->cfg->attrs[i]; + if (attr->type == LFS_TYPE_USERATTR + type) { + lfs_size_t attr_size = lfs_min(size, attr->size); + memcpy(buffer, attr->buffer, attr_size); + LFS_UNLOCK(lfs->cfg); + return 0; + } + } + } + + err = lfs_dir_get(lfs, &file->m, + LFS_MKTAG(0x7ff, 0x3ff, 0), + LFS_MKTAG(LFS_TYPE_USERATTR + type, + file->id, lfs_min(size, lfs->attr_max)), buffer); + LFS_UNLOCK(lfs->cfg); + return (err < 0) ? LFS_ERR_NOATTR : 0; +} + #ifndef LFS_READONLY int lfs_mkdir(lfs_t *lfs, const char *path) { int err = LFS_LOCK(lfs->cfg); diff --git a/lfs.h b/lfs.h index 847389737..e7fb6084e 100644 --- a/lfs.h +++ b/lfs.h @@ -656,6 +656,13 @@ 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 the attribute of a file +// +// Retrieves the value of the attribute specified by `type` for the given `file`. +// Copies up to `size` bytes into `buffer`. Returns 0 on success, or a negative +// error code (e.g., LFS_ERR_NOATTR if not found). +lfs_ssize_t lfs_file_getattr(lfs_t *lfs, lfs_file_t *file, + uint8_t type, void *buffer, lfs_size_t size); /// Directory operations /// diff --git a/tests/test_attrs.toml b/tests/test_attrs.toml index 3c69001cd..a5f8e0b61 100644 --- a/tests/test_attrs.toml +++ b/tests/test_attrs.toml @@ -314,3 +314,51 @@ code = ''' lfs_file_close(&lfs, &file) => 0; lfs_unmount(&lfs) => 0; ''' + +[cases.test_file_getattr] +code = ''' + struct lfs_attr_s { + uint32_t at_ver; + uint32_t at_mode; + uint32_t at_uid; + uint32_t at_gid; + uint64_t at_atim; + uint64_t at_mtim; + uint64_t at_ctim; + }; + + lfs_t lfs; + lfs_format(&lfs, cfg) => 0; + lfs_mount(&lfs, cfg) => 0; + + lfs_file_t file; + struct lfs_attr_s attr_get[1024]; + memset(attr_get, 0, sizeof(attr_get)); + lfs_file_open(&lfs, &file, "hello", LFS_O_WRONLY | LFS_O_CREAT) => 0; + lfs_file_write(&lfs, &file, "world", strlen("world")) => strlen("world"); + lfs_file_getattr(&lfs, &file, 0, attr_get, sizeof(struct lfs_attr_s)) => -61; + + struct lfs_attr_s attr_set = { + .at_ver = 1, + .at_mode = 0644, + .at_uid = 1000, + .at_gid = 1000, + .at_atim = 1693123456, + .at_mtim = 1693123456, + .at_ctim = 1693123456, + }; + lfs_setattr(&lfs, "hello", 0, &attr_set, sizeof(attr_set)) => 0; + memset(attr_get, 0, sizeof(attr_get)); + lfs_file_getattr(&lfs, &file, 0, attr_get, sizeof(struct lfs_attr_s)) => 0; + attr_get->at_ver => attr_set.at_ver; + attr_get->at_mode => attr_set.at_mode; + attr_get->at_uid => attr_set.at_uid; + attr_get->at_gid => attr_set.at_gid; + attr_get->at_atim => attr_set.at_atim; + attr_get->at_mtim => attr_set.at_mtim; + attr_get->at_ctim => attr_set.at_ctim; + + lfs_file_sync(&lfs, &file) => 0; + lfs_file_close(&lfs, &file) => 0; + lfs_unmount(&lfs) => 0; +'''