It is easier to use an empty string when the root directory is intended. Adjust the code to drop use of "/" and NULL and just use and empty string. Signed-off-by: Simon Glass <sjg@chromium.org>
196 lines
4.3 KiB
C
196 lines
4.3 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* U-Boot Virtio-FS directories
|
|
*
|
|
* Copyright 2025 Simon Glass <sjg@chromium.org>
|
|
*
|
|
* Supports access to directories in virtio-fs
|
|
*/
|
|
|
|
#define LOG_CATEGORY UCLASS_VIRTIO
|
|
|
|
#include <dm.h>
|
|
#include <dir.h>
|
|
#include <fs.h>
|
|
#include <malloc.h>
|
|
#include <virtio_fs.h>
|
|
#include <dm/device-internal.h>
|
|
#include <dm/lists.h>
|
|
#include <linux/fuse.h>
|
|
#include "fs_internal.h"
|
|
|
|
static int virtio_fs_dir_open(struct udevice *dev, struct fs_dir_stream *strm)
|
|
{
|
|
struct virtio_fs_dir_priv *dir_priv = dev_get_priv(dev);
|
|
struct udevice *fs = dev_get_parent(dev);
|
|
int ret;
|
|
|
|
log_debug("opening inode %lld\n", dir_priv->inode);
|
|
ret = virtio_fs_opendir(fs, dir_priv->inode, &strm->fh);
|
|
log_debug("2 open ret %d strm %p fh %llx\n", ret, strm, strm->fh);
|
|
if (ret) {
|
|
log_err("Failed to open directory: %d\n", ret);
|
|
return ret;
|
|
}
|
|
strm->dev = dev;
|
|
strm->offset = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int virtio_fs_dir_read(struct udevice *dev, struct fs_dir_stream *strm,
|
|
struct fs_dirent *dent)
|
|
{
|
|
struct virtio_fs_dir_priv *dir_priv = dev_get_priv(dev);
|
|
struct udevice *fs = dev_get_parent(dev);
|
|
struct fuse_direntplus *ent;
|
|
size_t reclen;
|
|
struct fuse_attr *attr;
|
|
char buf[0x200];
|
|
int ret, size;
|
|
|
|
log_debug("start %lld strm %p fh %llx\n", dir_priv->inode, strm,
|
|
strm->fh);
|
|
log_debug("offset %lld\n", strm->offset);
|
|
ret = virtio_fs_readdir(fs, dir_priv->inode, strm->fh, strm->offset,
|
|
buf, sizeof(buf), &size);
|
|
if (ret) {
|
|
log_err("Failed to read directory: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (!size)
|
|
return log_msg_ret("vde", -ENOENT);
|
|
|
|
log_debug("virtio-fs: size %x\n", size);
|
|
|
|
ent = (struct fuse_direntplus *)buf;
|
|
|
|
/* this shouldn't happen, but just to be sure... */
|
|
if (size < FUSE_NAME_OFFSET)
|
|
return -ENOSPC;
|
|
|
|
reclen = FUSE_DIRENTPLUS_SIZE(ent);
|
|
attr = &ent->entry_out.attr;
|
|
strm->offset = ent->dirent.off;
|
|
|
|
dent->type = ent->dirent.type;
|
|
dent->size = attr->size;
|
|
dent->attr = attr->flags;
|
|
strlcpy(dent->name, ent->dirent.name,
|
|
min((int)ent->dirent.namelen + 1, FS_DIRENT_NAME_LEN));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int virtio_fs_dir_close(struct udevice *dev, struct fs_dir_stream *strm)
|
|
{
|
|
struct virtio_fs_dir_priv *dir_priv = dev_get_priv(dev);
|
|
struct udevice *fs = dev_get_parent(dev);
|
|
int ret;
|
|
|
|
log_debug("close\n");
|
|
ret = virtio_fs_releasedir(fs, dir_priv->inode, strm->fh);
|
|
log_debug("ret %d\n", ret);
|
|
if (ret) {
|
|
log_err("Failed to release directory: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
log_debug("close done\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int open_file(struct udevice *dev, const char *leaf,
|
|
enum dir_open_flags_t oflags, struct udevice **filp)
|
|
{
|
|
struct udevice *fil;
|
|
int ret;
|
|
|
|
log_debug("start '%s'\n", leaf);
|
|
ret = virtio_fs_setup_file(dev, leaf, oflags, &fil);
|
|
log_debug("ret %d\n", ret);
|
|
if (ret)
|
|
return log_msg_ret("dof", ret);
|
|
*filp = fil;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int virtio_fs_dir_remove(struct udevice *dev)
|
|
{
|
|
struct virtio_fs_dir_priv *dir_priv = dev_get_priv(dev);
|
|
|
|
if (*dir_priv->path) {
|
|
int ret;
|
|
|
|
ret = virtio_fs_forget(dev, dir_priv->inode);
|
|
if (ret)
|
|
return log_msg_ret("vfr", ret);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct dir_ops virtio_fs_dir_ops = {
|
|
.open = virtio_fs_dir_open,
|
|
.read = virtio_fs_dir_read,
|
|
.close = virtio_fs_dir_close,
|
|
.open_file = open_file,
|
|
};
|
|
|
|
static const struct udevice_id dir_ids[] = {
|
|
{ .compatible = "virtio-fs,directory" },
|
|
{ }
|
|
};
|
|
|
|
U_BOOT_DRIVER(virtio_fs_dir) = {
|
|
.name = "virtio_fs_dir",
|
|
.id = UCLASS_DIR,
|
|
.of_match = dir_ids,
|
|
.remove = virtio_fs_dir_remove,
|
|
.ops = &virtio_fs_dir_ops,
|
|
.priv_auto = sizeof(struct virtio_fs_dir_priv),
|
|
.flags = DM_FLAG_ACTIVE_DMA,
|
|
};
|
|
|
|
int virtio_fs_setup_dir(struct udevice *fsdev, const char *path,
|
|
struct udevice **devp)
|
|
{
|
|
struct virtio_fs_dir_priv *dir_priv;
|
|
struct udevice *dir;
|
|
u64 inode;
|
|
int ret;
|
|
|
|
log_debug("looking up path '%s'\n", path);
|
|
inode = FUSE_ROOT_ID;
|
|
if (*path) {
|
|
ret = virtio_fs_lookup(fsdev, path, &inode);
|
|
if (ret) {
|
|
log_err("Failed to lookup directory '%s': %d\n", path,
|
|
ret);
|
|
return ret;
|
|
}
|
|
log_debug("got inode %lld\n", inode);
|
|
}
|
|
|
|
ret = dir_add_probe(fsdev, DM_DRIVER_REF(virtio_fs_dir), path, &dir);
|
|
if (ret)
|
|
goto no_add;
|
|
|
|
dir_priv = dev_get_priv(dir);
|
|
dir_priv->inode = inode;
|
|
log_debug("added new dir '%s' inode %llx\n", path, inode);
|
|
|
|
*devp = dir;
|
|
|
|
return 0;
|
|
|
|
no_add:
|
|
if (*path)
|
|
ret = virtio_fs_forget(fsdev, inode);
|
|
|
|
return ret;
|
|
}
|