From: Heinrich Schuchardt Date: Sat, 26 Oct 2024 06:40:46 +0000 (+0200) Subject: fs: ext4: implement opendir, readdir, closedir X-Git-Url: http://git.dujemihanovic.xyz/posts?a=commitdiff_plain;h=22fdac381f98b55d7dba55010f8fe9fab58d5556;p=u-boot.git fs: ext4: implement opendir, readdir, closedir For accessing directories from the EFI sub-system a file system must implement opendir, readdir, closedir. Provide the missing implementation. With this patch the eficonfig command can be used to define load options for the ext4 file system. Signed-off-by: Heinrich Schuchardt Reviewed-by: Simon Glass --- diff --git a/fs/ext4/ext4fs.c b/fs/ext4/ext4fs.c index 21714149ef..32693198ae 100644 --- a/fs/ext4/ext4fs.c +++ b/fs/ext4/ext4fs.c @@ -21,17 +21,36 @@ */ #include +#include +#include #include #include -#include "ext4_common.h" -#include #include #include #include +#include "ext4_common.h" int ext4fs_symlinknest; struct ext_filesystem ext_fs; +/** + * struct ext4_dir_stream - ext4 directory stream + * + * @parent: partition data used by fs layer. + * This field must be at the beginning of the structure. + * All other fields are private to the ext4 driver. + * @root: root directory node + * @dir: directory node + * @dirent: directory stream entry + * @fpos: file position in directory + */ +struct ext4_dir_stream { + struct fs_dir_stream parent; + char *dirname; + struct fs_dirent dirent; + unsigned int fpos; +}; + struct ext_filesystem *get_fs(void) { return &ext_fs; @@ -205,6 +224,144 @@ int ext4fs_ls(const char *dirname) return 0; } +int ext4fs_opendir(const char *dirname, struct fs_dir_stream **dirsp) +{ + struct ext4_dir_stream *dirs; + struct ext2fs_node *dir = NULL; + int ret; + + *dirsp = NULL; + + dirs = calloc(1, sizeof(struct ext4_dir_stream)); + if (!dirs) + return -ENOMEM; + dirs->dirname = strdup(dirname); + if (!dirs) { + free(dirs); + return -ENOMEM; + } + + ret = ext4fs_find_file(dirname, &ext4fs_root->diropen, &dir, + FILETYPE_DIRECTORY); + if (ret == 1) { + ret = 0; + *dirsp = (struct fs_dir_stream *)dirs; + } else { + ret = -ENOENT; + } + + if (dir) + ext4fs_free_node(dir, &ext4fs_root->diropen); + + return ret; +} + +int ext4fs_readdir(struct fs_dir_stream *fs_dirs, struct fs_dirent **dentp) +{ + struct ext4_dir_stream *dirs = (struct ext4_dir_stream *)fs_dirs; + struct fs_dirent *dent = &dirs->dirent; + struct ext2fs_node *dir = NULL; + int ret; + loff_t actread; + struct ext2fs_node fdiro; + int len; + struct ext2_dirent dirent; + + *dentp = NULL; + + ret = ext4fs_find_file(dirs->dirname, &ext4fs_root->diropen, &dir, + FILETYPE_DIRECTORY); + if (ret != 1) { + ret = -ENOENT; + goto out; + } + if (!dir->inode_read) { + ret = ext4fs_read_inode(dir->data, dir->ino, &dir->inode); + if (!ret) { + ret = -EIO; + goto out; + } + } + + if (dirs->fpos >= le32_to_cpu(dir->inode.size)) + return -ENOENT; + + memset(dent, 0, sizeof(struct fs_dirent)); + + while (dirs->fpos < le32_to_cpu(dir->inode.size)) { + ret = ext4fs_read_file(dir, dirs->fpos, + sizeof(struct ext2_dirent), + (char *)&dirent, &actread); + if (ret < 0) + return -ret; + + if (!dirent.direntlen) + return -EIO; + + if (dirent.namelen) + break; + + dirs->fpos += le16_to_cpu(dirent.direntlen); + } + + len = min(FS_DIRENT_NAME_LEN - 1, (int)dirent.namelen); + + ret = ext4fs_read_file(dir, dirs->fpos + sizeof(struct ext2_dirent), + len, dent->name, &actread); + if (ret < 0) + goto out; + dent->name[len] = '\0'; + + fdiro.data = dir->data; + fdiro.ino = le32_to_cpu(dirent.inode); + + ret = ext4fs_read_inode(dir->data, fdiro.ino, &fdiro.inode); + if (!ret) { + ret = -EIO; + goto out; + } + + switch (le16_to_cpu(fdiro.inode.mode) & FILETYPE_INO_MASK) { + case FILETYPE_INO_DIRECTORY: + dent->type = FS_DT_DIR; + break; + case FILETYPE_INO_SYMLINK: + dent->type = FS_DT_LNK; + break; + case FILETYPE_INO_REG: + dent->type = FS_DT_REG; + break; + default: + dent->type = FILETYPE_UNKNOWN; + } + + rtc_to_tm(fdiro.inode.atime, &dent->access_time); + rtc_to_tm(fdiro.inode.ctime, &dent->create_time); + rtc_to_tm(fdiro.inode.mtime, &dent->change_time); + + dirs->fpos += le16_to_cpu(dirent.direntlen); + dent->size = fdiro.inode.size; + *dentp = dent; + ret = 0; + +out: + if (dir) + ext4fs_free_node(dir, &ext4fs_root->diropen); + + return ret; +} + +void ext4fs_closedir(struct fs_dir_stream *fs_dirs) +{ + struct ext4_dir_stream *dirs = (struct ext4_dir_stream *)fs_dirs; + + if (!dirs) + return; + + free(dirs->dirname); + free(dirs); +} + int ext4fs_exists(const char *filename) { struct ext2fs_node *dirnode = NULL; diff --git a/fs/fs.c b/fs/fs.c index e2915e7cf7..a515905c4c 100644 --- a/fs/fs.c +++ b/fs/fs.c @@ -232,7 +232,9 @@ static struct fstype_info fstypes[] = { .ln = fs_ln_unsupported, #endif .uuid = ext4fs_uuid, - .opendir = fs_opendir_unsupported, + .opendir = ext4fs_opendir, + .readdir = ext4fs_readdir, + .closedir = ext4fs_closedir, .unlink = fs_unlink_unsupported, .mkdir = fs_mkdir_unsupported, }, diff --git a/include/ext4fs.h b/include/ext4fs.h index 41f9eb8bd3..fe3fb301ec 100644 --- a/include/ext4fs.h +++ b/include/ext4fs.h @@ -27,6 +27,7 @@ #ifndef __EXT4__ #define __EXT4__ #include +#include struct disk_partition; @@ -218,4 +219,7 @@ int ext4fs_uuid(char *uuid_str); void ext_cache_init(struct ext_block_cache *cache); void ext_cache_fini(struct ext_block_cache *cache); int ext_cache_read(struct ext_block_cache *cache, lbaint_t block, int size); +int ext4fs_opendir(const char *dirname, struct fs_dir_stream **dirsp); +int ext4fs_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp); +void ext4fs_closedir(struct fs_dir_stream *dirs); #endif