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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
111
fs/dir-uclass.c
Normal 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),
|
||||
};
|
||||
@@ -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
129
include/dir.h
Normal 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
|
||||
@@ -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 */
|
||||
|
||||
21
include/fs.h
21
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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user