]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
fs: ext4: implement opendir, readdir, closedir
authorHeinrich Schuchardt <heinrich.schuchardt@canonical.com>
Sat, 26 Oct 2024 06:40:46 +0000 (08:40 +0200)
committerTom Rini <trini@konsulko.com>
Fri, 1 Nov 2024 19:37:58 +0000 (13:37 -0600)
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 <heinrich.schuchardt@canonical.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
fs/ext4/ext4fs.c
fs/fs.c
include/ext4fs.h

index 21714149ef59d524c1c39377229ae6ec4c1ef9ba..32693198aebada0c7d4ae6217593505dd2000afd 100644 (file)
  */
 
 #include <blk.h>
+#include <div64.h>
+#include <errno.h>
 #include <ext_common.h>
 #include <ext4fs.h>
-#include "ext4_common.h"
-#include <div64.h>
 #include <malloc.h>
 #include <part.h>
 #include <u-boot/uuid.h>
+#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 e2915e7cf7992d9e84dd4caf2fe694c370cae7f6..a515905c4c95a47d13f7cb35b5d8858d79a5b9e1 100644 (file)
--- 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,
        },
index 41f9eb8bd336747a54e3b5a3eb971fd8b3640ed6..fe3fb301ec88ece5286b87f56be180bbdc5cf87c 100644 (file)
@@ -27,6 +27,7 @@
 #ifndef __EXT4__
 #define __EXT4__
 #include <ext_common.h>
+#include <fs.h>
 
 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