
From: Simon Glass <sjg@chromium.org> Filesystems can have a number of directories within them. U-Boot only worries about directories that have been accessed. Add the concept of a directory, with the filesystem as parent. Note that this is not a hierarchical device structure, so UCLASS_DIR devices always have a UCLASS_FS as the parent. Signed-off-by: Simon Glass <sjg@chromium.org> --- MAINTAINERS | 3 + fs/Kconfig | 8 +++ fs/Makefile | 1 + fs/dir-uclass.c | 111 +++++++++++++++++++++++++++++++++++ fs/fs-uclass.c | 7 +++ include/dir.h | 129 +++++++++++++++++++++++++++++++++++++++++ include/dm/uclass-id.h | 1 + include/fs.h | 21 +++++++ include/fs_common.h | 4 ++ 9 files changed, 285 insertions(+) create mode 100644 fs/dir-uclass.c create mode 100644 include/dir.h diff --git a/MAINTAINERS b/MAINTAINERS index 14a0e2a9de3..6af09911bdd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1196,7 +1196,10 @@ S: Maintained T: git https://source.denx.de/u-boot/custodians/u-boot-microblaze.git F: drivers/fpga/ F: cmd/fpga.c +F: fs/dir-uclass.c +F: include/dir.h F: include/fpga.h +F: include/fs.h F: test/dm/fpga.c FLATTENED DEVICE TREE diff --git a/fs/Kconfig b/fs/Kconfig index f7951b483aa..6a3b3d8218e 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -12,6 +12,14 @@ config FS Provides an interface for filesystems, allowing them to be persistently mounted. Filesystem can contain files and directory. +config DIR + bool "Support for directories" + depends on FS + default y if SANDBOX || VENDOR_EMULATION || ARCH_QEMU + help + Provides an interface for directories within filesystems, allowing + them to be listed. + config FS_LEGACY def_bool y help diff --git a/fs/Makefile b/fs/Makefile index 75959d06f12..4e33f312caf 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_$(PHASE_)FS_LEGACY) += fs_legacy.o fs_internal.o obj-$(CONFIG_$(PHASE_)FS) += fs-uclass.o +obj-$(CONFIG_$(PHASE_)DIR) += dir-uclass.o ifdef CONFIG_XPL_BUILD obj-$(CONFIG_SPL_FS_FAT) += fat/ diff --git a/fs/dir-uclass.c b/fs/dir-uclass.c new file mode 100644 index 00000000000..dcbef8dd9ff --- /dev/null +++ b/fs/dir-uclass.c @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Implementation of a directory on a filesystem + * + * Copyright 2025 Simon Glass <sjg@chromium.org> + */ + +#define LOG_CATEGORY UCLASS_DIR + +#include <dm.h> +#include <dir.h> +#include <fs.h> +#include <malloc.h> +#include <dm/device-internal.h> + +int dir_add_probe(struct udevice *fsdev, struct driver *drv, const char *path, + struct udevice **devp) +{ + char dev_name[30], *str, *dup_path; + struct dir_uc_priv *uc_priv; + struct udevice *dev; + int ret; + + snprintf(dev_name, sizeof(dev_name), "%s.dir", fsdev->name); + ret = -ENOMEM; + str = strdup(dev_name); + if (!str) + goto no_dev_name; + dup_path = strdup(path && strcmp("/", path) ? path : ""); + if (!str) + goto no_dev_path; + + ret = device_bind_with_driver_data(fsdev, drv, str, 0 /* data */, + ofnode_null(), &dev); + if (ret) + goto no_bind; + device_set_name_alloced(dev); + + ret = device_probe(dev); + if (ret) + goto no_probe; + uc_priv = dev_get_uclass_priv(dev); + uc_priv->path = dup_path; + + *devp = dev; + + return 0; + +no_probe: + device_unbind(dev); +no_bind: + free(dup_path); +no_dev_path: + free(str); +no_dev_name: + + return ret; +} + +int dir_open(struct udevice *dev, struct fs_dir_stream **strmp) +{ + struct dir_ops *ops = dir_get_ops(dev); + struct fs_dir_stream *strm; + int ret; + + strm = calloc(1, sizeof(struct fs_dir_stream)); + if (!strm) + return log_msg_ret("dom", -ENOMEM); + + strm->dev = dev; + ret = ops->open(dev, strm); + if (ret) { + free(strm); + return log_msg_ret("doo", ret); + } + *strmp = strm; + + return 0; +} + +int dir_read(struct udevice *dev, struct fs_dir_stream *strm, + struct fs_dirent *dent) +{ + struct dir_ops *ops = dir_get_ops(dev); + + log_debug("start %s\n", dev->name); + memset(dent, '\0', sizeof(struct fs_dirent)); + + return ops->read(dev, strm, dent); +} + +int dir_close(struct udevice *dev, struct fs_dir_stream *strm) +{ + struct dir_ops *ops = dir_get_ops(dev); + int ret; + + log_debug("start %s\n", dev->name); + + ret = ops->close(dev, strm); + if (ret) + return log_msg_ret("dcs", ret); + free(strm); + + return 0; +} + +UCLASS_DRIVER(dir) = { + .name = "dir", + .id = UCLASS_DIR, + .per_device_auto = sizeof(struct dir_uc_priv), +}; diff --git a/fs/fs-uclass.c b/fs/fs-uclass.c index d92b0e24538..7311bc72e04 100644 --- a/fs/fs-uclass.c +++ b/fs/fs-uclass.c @@ -13,6 +13,13 @@ #include <fs.h> #include <dm/device-internal.h> +int fs_lookup_dir(struct udevice *dev, const char *path, struct udevice **dirp) +{ + struct fs_ops *ops = fs_get_ops(dev); + + return ops->lookup_dir(dev, path, dirp); +} + int fs_mount(struct udevice *dev) { struct fs_ops *ops = fs_get_ops(dev); diff --git a/include/dir.h b/include/dir.h new file mode 100644 index 00000000000..8e883a6c6b2 --- /dev/null +++ b/include/dir.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * U-Boot Filesystem directory + * + * Copyright 2025 Simon Glass <sjg@chromium.org> + */ + +#ifndef __DIR_H +#define __DIR_H + +struct driver; +struct fs_dirent; +struct fs_dir_stream; +struct udevice; + +/** + * enum dir_open_flags_t - Flags to control the open mode of files + * + * @DIR_O_RDONLY: Open the file read-only + * @DIR_O_WRONLY: Open the file write-only, overwriting existing file contents + * @DIR_O_RDWR: Open the file for read/write, allowing the file to be updated + */ +enum dir_open_flags_t { + DIR_O_RDONLY, + DIR_O_WRONLY, + DIR_O_RDWR, +}; + +/** + * struct dir_uc_priv - Uclass information for each directory + * + * @path: Absolute path to directory, "" for root + */ +struct dir_uc_priv { + char *path; +}; + +/** + * struct dir_ops - Operations on directories + */ +struct dir_ops { + /** + * open() - Open a directory for reading + * + * @dev: Directory device (UCLASS_DIR) + * @strm: Stream information to fill in, on success (zeroed on entry) + */ + int (*open)(struct udevice *dev, struct fs_dir_stream *strm); + + /** + * read() - Read a single directory entry + * + * @dev: Directory device (UCLASS_DIR) + * @strm: Directory stream as created by open() + * @dent: Directory entry to fill in (zeroed on entry) + * Return: 0 if OK, -ENOENT if no more entries, other -ve value on error + */ + int (*read)(struct udevice *dev, struct fs_dir_stream *strm, + struct fs_dirent *dent); + + /** + * close() - Stop reading the directory + * + * @dev: Directory device (UCLASS_DIR) + * @strm: Directory stream as created by dir_opendir() + * Return: 0 if OK, -ve on error + */ + int (*close)(struct udevice *dev, struct fs_dir_stream *strm); + + /** + * open_file() - Create a new file device for a file + * + * @dev: Directory device (UCLASS_DIR) + * @leaf: Filename within the directory + * @flags: Open-mode flags to use + * @filp: Returns the UCLASS_FILE device + * Return: 0 if OK, -ve on error + */ + int (*open_file)(struct udevice *dev, const char *leaf, + enum dir_open_flags_t oflags, struct udevice **filp); +}; + +/* Get access to a directory's operations */ +#define dir_get_ops(dev) ((struct dir_ops *)(dev)->driver->ops) + +/** + * dir_open() - Open a directory for reading + * + * @dev: Directory device (UCLASS_DIR) + * @strm: Returns allocated pointer to stream information, on success + */ +int dir_open(struct udevice *dev, struct fs_dir_stream **strmp); + +/** + * dir_read() - Read a single directory entry + * + * @dev: Directory device (UCLASS_DIR) + * @strm: Directory stream as created by open() + * @dent: Directory entry to fill in + * Return: 0 if OK, -ENOENT if no more entries, other -ve value on other + * error + */ +int dir_read(struct udevice *dev, struct fs_dir_stream *strm, + struct fs_dirent *dent); + +/** + * dir_close() - Stop reading the directory + * + * Frees @strm and releases the directory + * + * @dev: Directory device (UCLASS_DIR) + * @strm: Directory stream as created by dir_opendir() + * Return: 0 if OK, -ve on error + */ +int dir_close(struct udevice *dev, struct fs_dir_stream *strm); + +/** + * dir_add_probe() - Add a new directory and probe it + * + * @fsdev: Filesystem containing the directory + * @drv: Driver to use + * @path Absolute path to directory (within the filesystem), or NULL/"/" for + * root + * @devp: Returns the new device, probed ready for use * + */ +int dir_add_probe(struct udevice *fsdev, struct driver *drv, const char *path, + struct udevice **devp); + +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 45e9abfce32..247c6322c76 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -54,6 +54,7 @@ enum uclass_id { UCLASS_CLK, /* Clock source, e.g. used by peripherals */ UCLASS_CPU, /* CPU, typically part of an SoC */ UCLASS_CROS_EC, /* Chrome OS EC */ + UCLASS_DIR, /* Directory on a filesystem */ UCLASS_DISPLAY, /* Display (e.g. DisplayPort, HDMI) */ UCLASS_DMA, /* Direct Memory Access */ UCLASS_DSA, /* Distributed (Ethernet) Switch Architecture */ diff --git a/include/fs.h b/include/fs.h index 05a2ac844b8..e682ff7fa6a 100644 --- a/include/fs.h +++ b/include/fs.h @@ -54,6 +54,17 @@ struct fs_ops { * Return 0 if OK, -ENOTCONN if not mounted, other -ve on error */ int (*unmount)(struct udevice *dev); + + /** + * lookup_dir() - Look up a directory on a filesystem + * + * @dev: Filesystem device + * @path: Path to look up, empty or "/" for the root + * @dirp: Returns associated directory device, creating if necessary + * Return 0 if OK, -ENOENT, other -ve on error + */ + int (*lookup_dir)(struct udevice *dev, const char *path, + struct udevice **dirp); }; /* Get access to a filesystem's operations */ @@ -75,4 +86,14 @@ int fs_mount(struct udevice *dev); */ int fs_unmount(struct udevice *dev); +/** + * fs_lookup_dir() - Look up a directory on a filesystem + * + * @dev: Filesystem device + * @path: Path to look up, empty or "/" for the root + * @dirp: Returns associated directory device, creating if necessary + * Return 0 if OK, -ENOENT, other -ve on error + */ +int fs_lookup_dir(struct udevice *dev, const char *path, struct udevice **dirp); + #endif diff --git a/include/fs_common.h b/include/fs_common.h index 30e8b6737b3..ac613d0862b 100644 --- a/include/fs_common.h +++ b/include/fs_common.h @@ -61,10 +61,14 @@ struct fs_dirent { * File system drivers pass additional private fields with the pointers * to this structure. * + * @dev: dir device (UCLASS_DIR) * @desc: block device descriptor * @part: partition number */ struct fs_dir_stream { +#ifdef CONFIG_FS + struct udevice *dev; +#endif struct blk_desc *desc; int part; }; -- 2.43.0