At present a new directory device is created for every access to a directory. This means that a new device is always created when 'ls' is used. Where the directory being accessed matches an existing device, this is not necessary. Check that path against existing directories and reuse the device if possible. Signed-off-by: Simon Glass <sjg@chromium.org>
176 lines
3.4 KiB
C
176 lines
3.4 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Implementation of a filesystem, e.g. on a partition
|
|
*
|
|
* Copyright 2025 Simon Glass <sjg@chromium.org>
|
|
*/
|
|
|
|
#define LOG_CATEGORY UCLASS_FS
|
|
|
|
#include <bootdev.h>
|
|
#include <bootmeth.h>
|
|
#include <dir.h>
|
|
#include <dm.h>
|
|
#include <fs.h>
|
|
#include <dm/device-internal.h>
|
|
|
|
int fs_split_path(const char *fname, char **subdirp, const char **leafp)
|
|
{
|
|
char *subdir, *p;
|
|
|
|
if (!*fname)
|
|
return log_msg_ret("fsp", -EINVAL);
|
|
|
|
/* allocate space for the whole filename, for simplicity */
|
|
subdir = strdup(fname);
|
|
if (!subdir)
|
|
return log_msg_ret("fsp", -ENOMEM);
|
|
|
|
p = strrchr(subdir, '/');
|
|
if (p) {
|
|
*leafp = p + 1;
|
|
*p = '\0';
|
|
} else {
|
|
*leafp = fname;
|
|
strcpy(subdir, "/");
|
|
}
|
|
*subdirp = subdir;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fs_lookup_dir(struct udevice *dev, const char *path, struct udevice **dirp)
|
|
{
|
|
struct fs_ops *ops = fs_get_ops(dev);
|
|
struct udevice *dir;
|
|
int ret;
|
|
|
|
if (!path || !strcmp("/", path))
|
|
path = "";
|
|
|
|
/* see if we already have this directory */
|
|
device_foreach_child(dir, dev) {
|
|
struct dir_uc_priv *priv;
|
|
|
|
if (!device_active(dir))
|
|
continue;
|
|
|
|
priv = dev_get_uclass_priv(dir);
|
|
log_debug("dir %s '%s' '%s'\n", dir->name, path, priv->path);
|
|
if (!strcmp(path, priv->path)) {
|
|
*dirp = dir;
|
|
log_debug("found: dev '%s'\n", dir->name);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ret = ops->lookup_dir(dev, path, &dir);
|
|
if (ret)
|
|
return log_msg_ret("fld", ret);
|
|
|
|
*dirp = dir;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fs_mount(struct udevice *dev)
|
|
{
|
|
struct fs_ops *ops = fs_get_ops(dev);
|
|
int ret;
|
|
|
|
ret = ops->mount(dev);
|
|
if (ret)
|
|
return log_msg_ret("fsm", ret);
|
|
|
|
ret = bootdev_setup_for_dev(dev, "fs_bootdev");
|
|
if (ret)
|
|
return log_msg_ret("fss", ret);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fs_unmount(struct udevice *dev)
|
|
{
|
|
struct fs_ops *ops = fs_get_ops(dev);
|
|
|
|
return ops->unmount(dev);
|
|
}
|
|
|
|
static int fs_get_bootflow(struct udevice *dev, struct bootflow_iter *iter,
|
|
struct bootflow *bflow)
|
|
{
|
|
struct udevice *fsdev = dev_get_parent(dev);
|
|
int ret;
|
|
|
|
log_debug("get_bootflow fs '%s'\n", fsdev->name);
|
|
|
|
/* for now, always fail here as we don't have FS support in bootmeths */
|
|
return -ENOENT;
|
|
|
|
ret = bootmeth_check(bflow->method, iter);
|
|
if (ret)
|
|
return log_msg_ret("check", ret);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fs_bootdev_bind(struct udevice *dev)
|
|
{
|
|
struct bootdev_uc_plat *ucp = dev_get_uclass_plat(dev);
|
|
|
|
/*
|
|
* we don't know what priority to give this, so pick something a little
|
|
* slow for now
|
|
*/
|
|
ucp->prio = BOOTDEVP_3_INTERNAL_SLOW;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int fs_bootdev_hunt(struct bootdev_hunter *info, bool show)
|
|
{
|
|
struct udevice *dev;
|
|
int ret;
|
|
|
|
/* mount all filesystems, which will create bootdevs for each */
|
|
uclass_foreach_dev_probe(UCLASS_FS, dev) {
|
|
ret = fs_mount(dev);
|
|
if (ret)
|
|
log_warning("Failed to mount filesystem '%s'\n",
|
|
dev->name);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct bootdev_ops fs_bootdev_ops = {
|
|
.get_bootflow = fs_get_bootflow,
|
|
};
|
|
|
|
static const struct udevice_id fs_bootdev_ids[] = {
|
|
{ .compatible = "u-boot,bootdev-fs" },
|
|
{ }
|
|
};
|
|
|
|
U_BOOT_DRIVER(fs_bootdev) = {
|
|
.name = "fs_bootdev",
|
|
.id = UCLASS_BOOTDEV,
|
|
.ops = &fs_bootdev_ops,
|
|
.bind = fs_bootdev_bind,
|
|
.of_match = fs_bootdev_ids,
|
|
};
|
|
|
|
BOOTDEV_HUNTER(fs_bootdev_hunter) = {
|
|
.prio = BOOTDEVP_3_INTERNAL_SLOW,
|
|
.uclass = UCLASS_FS,
|
|
.hunt = fs_bootdev_hunt,
|
|
.drv = DM_DRIVER_REF(fs_bootdev),
|
|
};
|
|
|
|
UCLASS_DRIVER(fs) = {
|
|
.name = "fs",
|
|
.id = UCLASS_FS,
|
|
.per_device_auto = sizeof(struct fs_priv),
|
|
.per_device_plat_auto = sizeof(struct fs_plat),
|
|
};
|