fs: Add support for a directory

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>
This commit is contained in:
Simon Glass
2025-06-25 07:54:20 -06:00
parent d347548467
commit 4ae46c7db4
9 changed files with 285 additions and 0 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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/

111
fs/dir-uclass.c Normal file
View File

@@ -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),
};

View File

@@ -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);

129
include/dir.h Normal file
View File

@@ -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

View File

@@ -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 */

View File

@@ -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

View File

@@ -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;
};