fs: add fs_readdir()
authorRob Clark <robdclark@gmail.com>
Sat, 9 Sep 2017 17:15:55 +0000 (13:15 -0400)
committerTom Rini <trini@konsulko.com>
Fri, 15 Sep 2017 13:03:11 +0000 (09:03 -0400)
Needed to support efi file protocol.  The fallback.efi loader wants
to be able to read the contents of the /EFI directory to find an OS
to boot.

Modelled after POSIX opendir()/readdir()/closedir().  Unlike the other
fs APIs, this is stateful (ie. state is held in the FS_DIR "directory
stream"), to avoid re-traversing of the directory structure at each
step.  The directory stream must be released with closedir() when it
is no longer needed.

Signed-off-by: Rob Clark <robdclark@gmail.com>
Reviewed-by: Ɓukasz Majewski <lukma@denx.de>
Reviewed-by: Simon Glass <sjg@chromium.org>
disk/part.c
fs/fs.c
include/fs.h
include/part.h

index c67fdacc79257f12b64583de2bd845cb98445151..aa9183d696406ab28c882100907e603d1e5d71aa 100644 (file)
@@ -331,6 +331,24 @@ int part_get_info(struct blk_desc *dev_desc, int part,
        return -1;
 }
 
+int part_get_info_whole_disk(struct blk_desc *dev_desc, disk_partition_t *info)
+{
+       info->start = 0;
+       info->size = dev_desc->lba;
+       info->blksz = dev_desc->blksz;
+       info->bootable = 0;
+       strcpy((char *)info->type, BOOT_PART_TYPE);
+       strcpy((char *)info->name, "Whole Disk");
+#if CONFIG_IS_ENABLED(PARTITION_UUIDS)
+       info->uuid[0] = 0;
+#endif
+#ifdef CONFIG_PARTITION_TYPE_GUID
+       info->type_guid[0] = 0;
+#endif
+
+       return 0;
+}
+
 int blk_get_device_by_str(const char *ifname, const char *dev_hwpart_str,
                          struct blk_desc **dev_desc)
 {
@@ -523,18 +541,7 @@ int blk_get_device_part_str(const char *ifname, const char *dev_part_str,
 
                (*dev_desc)->log2blksz = LOG2((*dev_desc)->blksz);
 
-               info->start = 0;
-               info->size = (*dev_desc)->lba;
-               info->blksz = (*dev_desc)->blksz;
-               info->bootable = 0;
-               strcpy((char *)info->type, BOOT_PART_TYPE);
-               strcpy((char *)info->name, "Whole Disk");
-#if CONFIG_IS_ENABLED(PARTITION_UUIDS)
-               info->uuid[0] = 0;
-#endif
-#ifdef CONFIG_PARTITION_TYPE_GUID
-               info->type_guid[0] = 0;
-#endif
+               part_get_info_whole_disk(*dev_desc, info);
 
                ret = 0;
                goto cleanup;
diff --git a/fs/fs.c b/fs/fs.c
index 13cd3626c6f811248553ef625b7f64e02e27dfc0..fc0c953fcbd13a6089a6dbdaade234c104710261 100644 (file)
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -21,6 +21,7 @@
 DECLARE_GLOBAL_DATA_PTR;
 
 static struct blk_desc *fs_dev_desc;
+static int fs_dev_part;
 static disk_partition_t fs_partition;
 static int fs_type = FS_TYPE_ANY;
 
@@ -69,6 +70,12 @@ static inline int fs_uuid_unsupported(char *uuid_str)
        return -1;
 }
 
+static inline int fs_opendir_unsupported(const char *filename,
+                                        struct fs_dir_stream **dirs)
+{
+       return -EACCES;
+}
+
 struct fstype_info {
        int fstype;
        char *name;
@@ -92,6 +99,20 @@ struct fstype_info {
                     loff_t len, loff_t *actwrite);
        void (*close)(void);
        int (*uuid)(char *uuid_str);
+       /*
+        * Open a directory stream.  On success return 0 and directory
+        * stream pointer via 'dirsp'.  On error, return -errno.  See
+        * fs_opendir().
+        */
+       int (*opendir)(const char *filename, struct fs_dir_stream **dirsp);
+       /*
+        * Read next entry from directory stream.  On success return 0
+        * and directory entry pointer via 'dentp'.  On error return
+        * -errno.  See fs_readdir().
+        */
+       int (*readdir)(struct fs_dir_stream *dirs, struct fs_dirent **dentp);
+       /* see fs_closedir() */
+       void (*closedir)(struct fs_dir_stream *dirs);
 };
 
 static struct fstype_info fstypes[] = {
@@ -112,6 +133,7 @@ static struct fstype_info fstypes[] = {
                .write = fs_write_unsupported,
 #endif
                .uuid = fs_uuid_unsupported,
+               .opendir = fs_opendir_unsupported,
        },
 #endif
 #ifdef CONFIG_FS_EXT4
@@ -131,6 +153,7 @@ static struct fstype_info fstypes[] = {
                .write = fs_write_unsupported,
 #endif
                .uuid = ext4fs_uuid,
+               .opendir = fs_opendir_unsupported,
        },
 #endif
 #ifdef CONFIG_SANDBOX
@@ -146,6 +169,7 @@ static struct fstype_info fstypes[] = {
                .read = fs_read_sandbox,
                .write = fs_write_sandbox,
                .uuid = fs_uuid_unsupported,
+               .opendir = fs_opendir_unsupported,
        },
 #endif
 #ifdef CONFIG_CMD_UBIFS
@@ -161,6 +185,7 @@ static struct fstype_info fstypes[] = {
                .read = ubifs_read,
                .write = fs_write_unsupported,
                .uuid = fs_uuid_unsupported,
+               .opendir = fs_opendir_unsupported,
        },
 #endif
        {
@@ -175,6 +200,7 @@ static struct fstype_info fstypes[] = {
                .read = fs_read_unsupported,
                .write = fs_write_unsupported,
                .uuid = fs_uuid_unsupported,
+               .opendir = fs_opendir_unsupported,
        },
 };
 
@@ -226,6 +252,31 @@ int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype)
                if (!fs_dev_desc && !info->null_dev_desc_ok)
                        continue;
 
+               if (!info->probe(fs_dev_desc, &fs_partition)) {
+                       fs_type = info->fstype;
+                       fs_dev_part = part;
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+/* set current blk device w/ blk_desc + partition # */
+int fs_set_blk_dev_with_part(struct blk_desc *desc, int part)
+{
+       struct fstype_info *info;
+       int ret, i;
+
+       if (part >= 1)
+               ret = part_get_info(desc, part, &fs_partition);
+       else
+               ret = part_get_info_whole_disk(desc, &fs_partition);
+       if (ret)
+               return ret;
+       fs_dev_desc = desc;
+
+       for (i = 0, info = fstypes; i < ARRAY_SIZE(fstypes); i++, info++) {
                if (!info->probe(fs_dev_desc, &fs_partition)) {
                        fs_type = info->fstype;
                        return 0;
@@ -334,6 +385,59 @@ int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len,
        return ret;
 }
 
+struct fs_dir_stream *fs_opendir(const char *filename)
+{
+       struct fstype_info *info = fs_get_info(fs_type);
+       struct fs_dir_stream *dirs = NULL;
+       int ret;
+
+       ret = info->opendir(filename, &dirs);
+       fs_close();
+       if (ret) {
+               errno = -ret;
+               return NULL;
+       }
+
+       dirs->desc = fs_dev_desc;
+       dirs->part = fs_dev_part;
+
+       return dirs;
+}
+
+struct fs_dirent *fs_readdir(struct fs_dir_stream *dirs)
+{
+       struct fstype_info *info;
+       struct fs_dirent *dirent;
+       int ret;
+
+       fs_set_blk_dev_with_part(dirs->desc, dirs->part);
+       info = fs_get_info(fs_type);
+
+       ret = info->readdir(dirs, &dirent);
+       fs_close();
+       if (ret) {
+               errno = -ret;
+               return NULL;
+       }
+
+       return dirent;
+}
+
+void fs_closedir(struct fs_dir_stream *dirs)
+{
+       struct fstype_info *info;
+
+       if (!dirs)
+               return;
+
+       fs_set_blk_dev_with_part(dirs->desc, dirs->part);
+       info = fs_get_info(fs_type);
+
+       info->closedir(dirs);
+       fs_close();
+}
+
+
 int do_size(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],
                int fstype)
 {
index 2f2aca8378be15a92b8082e0872a9b3a3829a4f9..0869ad6e80cf117799686cf1790ca61aa01f3826 100644 (file)
  */
 int fs_set_blk_dev(const char *ifname, const char *dev_part_str, int fstype);
 
+/*
+ * fs_set_blk_dev_with_part - Set current block device + partition
+ *
+ * Similar to fs_set_blk_dev(), but useful for cases where you already
+ * know the blk_desc and part number.
+ *
+ * Returns 0 on success.
+ * Returns non-zero if invalid partition or error accessing the disk.
+ */
+int fs_set_blk_dev_with_part(struct blk_desc *desc, int part);
+
 /*
  * Print the list of files on the partition previously set by fs_set_blk_dev(),
  * in directory "dirname".
@@ -78,6 +89,62 @@ int fs_read(const char *filename, ulong addr, loff_t offset, loff_t len,
 int fs_write(const char *filename, ulong addr, loff_t offset, loff_t len,
             loff_t *actwrite);
 
+/*
+ * Directory entry types, matches the subset of DT_x in posix readdir()
+ * which apply to u-boot.
+ */
+#define FS_DT_DIR  4         /* directory */
+#define FS_DT_REG  8         /* regular file */
+#define FS_DT_LNK  10        /* symbolic link */
+
+/*
+ * A directory entry, returned by fs_readdir().  Returns information
+ * about the file/directory at the current directory entry position.
+ */
+struct fs_dirent {
+       unsigned type;       /* one of FS_DT_x (not a mask) */
+       loff_t size;         /* size in bytes */
+       char name[256];
+};
+
+/* Note: fs_dir_stream should be treated as opaque to the user of fs layer */
+struct fs_dir_stream {
+       /* private to fs. layer: */
+       struct blk_desc *desc;
+       int part;
+};
+
+/*
+ * fs_opendir - Open a directory
+ *
+ * @filename: the path to directory to open
+ * @return a pointer to the directory stream or NULL on error and errno
+ *    set appropriately
+ */
+struct fs_dir_stream *fs_opendir(const char *filename);
+
+/*
+ * fs_readdir - Read the next directory entry in the directory stream.
+ *
+ * Works in an analogous way to posix readdir().  The previously returned
+ * directory entry is no longer valid after calling fs_readdir() again.
+ * After fs_closedir() is called, the returned directory entry is no
+ * longer valid.
+ *
+ * @dirs: the directory stream
+ * @return the next directory entry (only valid until next fs_readdir() or
+ *    fs_closedir() call, do not attempt to free()) or NULL if the end of
+ *    the directory is reached.
+ */
+struct fs_dirent *fs_readdir(struct fs_dir_stream *dirs);
+
+/*
+ * fs_closedir - close a directory stream
+ *
+ * @dirs: the directory stream
+ */
+void fs_closedir(struct fs_dir_stream *dirs);
+
 /*
  * Common implementation for various filesystem commands, optionally limited
  * to a specific filesystem type via the fstype parameter.
index 0d5c99836b2594efc3385c72a404eb4d2ac2bad7..86117a7ce5d3c57e80c1ec9dc8293718b5c78c32 100644 (file)
@@ -98,6 +98,12 @@ int host_get_dev_err(int dev, struct blk_desc **blk_devp);
 
 /* disk/part.c */
 int part_get_info(struct blk_desc *dev_desc, int part, disk_partition_t *info);
+/**
+ * part_get_info_whole_disk() - get partition info for the special case of
+ * a partition occupying the entire disk.
+ */
+int part_get_info_whole_disk(struct blk_desc *dev_desc, disk_partition_t *info);
+
 void part_print(struct blk_desc *dev_desc);
 void part_init(struct blk_desc *dev_desc);
 void dev_print(struct blk_desc *dev_desc);
@@ -203,6 +209,9 @@ static inline struct blk_desc *mg_disk_get_dev(int dev) { return NULL; }
 
 static inline int part_get_info(struct blk_desc *dev_desc, int part,
                                disk_partition_t *info) { return -1; }
+static inline int part_get_info_whole_disk(struct blk_desc *dev_desc,
+                                          disk_partition_t *info)
+{ return -1; }
 static inline void part_print(struct blk_desc *dev_desc) {}
 static inline void part_init(struct blk_desc *dev_desc) {}
 static inline void dev_print(struct blk_desc *dev_desc) {}