Add find() and link resolution to VFS

This commit is contained in:
Mark
2020-01-12 12:57:31 +02:00
parent 2e795d2ebf
commit fa399280fd
15 changed files with 1301 additions and 838 deletions
+1 -1
View File
@@ -61,6 +61,7 @@ OBJS+=$(O)/sys/debug.o \
$(O)/sys/net/netdev.o \
$(O)/sys/vfs/pseudo.o \
$(O)/sys/vfs/ext2/ext2.o \
$(O)/sys/vfs/ext2/ext2vnop.o \
$(O)/sys/vfs/ext2/ext2blk.o \
$(O)/sys/vfs/tar.o \
$(O)/sys/blk/ram.o \
@@ -72,7 +73,6 @@ OBJS+=$(O)/sys/debug.o \
$(O)/sys/vfs/pty.o \
$(O)/sys/vfs/ext2/ext2alloc.o \
$(O)/sys/vfs/ext2/ext2dir.o \
$(O)/sys/vfs/ext2/ext2vnop.o \
ifeq ($(VESA_ENABLE),1)
OBJS+=$(O)/sys/psf.o \
+5 -1
View File
@@ -2,9 +2,13 @@
#include "sys/types.h"
#include "sys/dev.h"
#define BLK_BUSY (1 << 0)
struct vnode;
struct blkdev {
uint32_t flags;
void *dev_data;
struct dev_entry *ent;
struct dev_entry *ent_parent;
@@ -20,4 +24,4 @@ ssize_t blk_write(struct blkdev *blk, const void *buf, size_t off, size_t count)
int blk_mount_auto(struct vnode *at, struct blkdev *blkdev, const char *opt);
struct blkdev *blk_by_name(const char *name);
int blk_enumerate_partitions(struct blkdev *blk);
int blk_enumerate_partitions(struct blkdev *blk, struct vnode *node);
+2
View File
@@ -35,6 +35,8 @@
#define EDOM 33
#define ERANGE 34
#define ELOOP 40
#if defined(__KERNEL__)
const char *kstrerror(int e);
#endif
+11 -3
View File
@@ -8,7 +8,7 @@
#include "dirent.h"
#include "sys/stat.h"
#define NODE_MAXLEN 256
#define NODE_MAXLEN 64
// Means the node has no physical storage and resides only
// in memory
@@ -24,13 +24,19 @@ enum vnode_type {
VN_DIR,
VN_BLK,
VN_CHR,
VN_LNK,
VN_UNK,
VN_MNT,
};
struct vnode_operations {
int (*find) (struct vnode *at, const char *name, struct vnode **node);
int (*open) (struct ofile *fd);
void (*close) (struct ofile *fd);
int (*readlink) (struct vnode *at, char *buf, size_t lim);
ssize_t (*read) (struct ofile *fd, void *buf, size_t count);
ssize_t (*write) (struct ofile *fd, const void *buf, size_t count);
};
@@ -45,6 +51,10 @@ struct vnode {
struct vnode *next_child;
struct vnode *parent;
// For symlinks, this is target vnode
// For mountpoints, this is filesystem root
struct vnode *target;
uint32_t open_count;
uint64_t ino;
@@ -72,5 +82,3 @@ void vnode_detach(struct vnode *node);
// Tree traversal
void vnode_dump_tree(int level, struct vnode *node, int depth);
int vnode_lookup_child(struct vnode *of, const char *name, struct vnode **child);
int vnode_lookup_tree(struct vnode *at, const char *rel_path, struct vnode **child);
+10
View File
@@ -9,11 +9,21 @@
#include "sys/stat.h"
#include "sys/statvfs.h"
#define PATH_MAX 256
#define LINK_MAX 16
struct vfs_ioctx {
// Process' current working directory
char cwd_path[PATH_MAX];
struct vnode *cwd_vnode;
uid_t uid;
gid_t gid;
};
void vfs_init(void);
int vfs_link_resolve(struct vfs_ioctx *ctx, struct vnode *lnk, struct vnode **res);
int vfs_find(struct vfs_ioctx *ctx, struct vnode *rel, const char *path, struct vnode **node);
int vfs_mount(struct vfs_ioctx *ctx, const char *at, void *blk, const char *fs, const char *opt);
int vfs_open(struct vfs_ioctx *ctx, struct ofile *fd, const char *path, int mode, int flags);
+1
View File
@@ -68,6 +68,7 @@ static int ahci_add_port_dev(struct ahci_port_registers *port) {
blk->dev_data = port;
blk->read = ahci_blk_read;
blk->write = ahci_blk_write;
blk->flags = 0;
return dev_add(DEV_CLASS_BLOCK, DEV_BLOCK_SDx, blk, NULL);
}
+14 -31
View File
@@ -85,42 +85,25 @@ void init_func(void *arg) {
// Find _ramblk0
struct vnode *ramblk0;
struct vnode *dev1;
if ((res = dev_find(DEV_CLASS_BLOCK, "ram0", &ramblk0)) != 0) {
panic("Failed to find root device\n");
}
if ((res = dev_find(DEV_CLASS_BLOCK, "dev1_0", &dev1)) != 0) {
panic("Failed to find dev1: %s\n", kstrerror(res));
}
kinfo("Found root device: %s\n", ramblk0->name);
// Mount /dev/ram0 using ustar on /
if ((res = vfs_mount(&ioctx, "/", ramblk0->dev, "ustar", NULL)) != 0) {
panic("Failed to mount root device: %s\n", kstrerror(res));
}
// Create a filesystem instance at this device
struct fs *fs_instance = fs_create(tarfs_class, ramblk0->dev);
_assert(fs_instance);
// Get tarfs root node
struct vnode *root_node = tarfs_class->get_root(fs_instance);
_assert(root_node);
vnode_dump_tree(DEBUG_INFO, root_node, 0);
// Find some file in rootfs
struct vnode *etc_file_txt;
_assert(vnode_lookup_tree(root_node, "etc/file.txt", &etc_file_txt) == 0);
_assert(etc_file_txt);
// Open it
_assert(etc_file_txt->op);
_assert(etc_file_txt->op->read);
struct ofile fd = {
.vnode = etc_file_txt,
.pos = 0,
};
char buf[512];
ssize_t bread;
_assert((bread = etc_file_txt->op->read(&fd, buf, sizeof(buf))) > 0);
buf[bread] = 0;
kdebug("\n%s\n", buf);
// Mount /dev/dev1 using ext2 on /mnt
// TODO: make some flag for block devices to indicate
// they have already been mounted
if ((res = vfs_mount(&ioctx, "/mnt", dev1->dev, "ext2", NULL)) != 0) {
panic("Failed to mount some shit: %s\n", kstrerror(res));
}
kinfo("Done\n");
+137
View File
@@ -9,6 +9,40 @@
// Taken as-is from MBR partition types
#define PART_HINT_LINUX 0x83
const char GUID_EMPTY[16] = {0};
struct blk_part {
struct blkdev *device;
uint64_t lba_start;
uint64_t size;
};
struct gpt_header {
char signature[8];
uint32_t revision;
uint32_t header_size;
uint32_t header_crc32;
uint32_t __res0;
uint64_t my_lba;
uint64_t alt_lba;
uint64_t first_lba;
uint64_t last_lba;
char disk_guid[16];
uint64_t partition_entry_lba;
uint32_t partition_count;
uint32_t partition_entry_size;
uint32_t partition_array_crc32;
} __attribute__((packed));
struct gpt_part_entry {
char type_guid[16];
char part_guid[16];
uint64_t start_lba;
uint64_t end_lba;
uint64_t attr;
char part_name[72];
};
ssize_t blk_read(struct blkdev *blk, void *buf, size_t off, size_t lim) {
_assert(blk);
@@ -28,3 +62,106 @@ ssize_t blk_write(struct blkdev *blk, const void *buf, size_t off, size_t lim) {
return -EINVAL;
}
}
static ssize_t blk_part_write(struct blkdev *dev, const void *buf, size_t off, size_t count) {
struct blk_part *part = (struct blk_part *) dev->dev_data;
size_t part_size_bytes = part->size * 512;
size_t part_off_bytes = part->lba_start * 512;
if (off >= part_size_bytes) {
return -1;
}
size_t l = MIN(part_size_bytes - off, count);
return blk_write(part->device, buf, off + part_off_bytes, l);
}
static ssize_t blk_part_read(struct blkdev *dev, void *buf, size_t off, size_t count) {
struct blk_part *part = (struct blk_part *) dev->dev_data;
size_t part_size_bytes = part->size * 512;
size_t part_off_bytes = part->lba_start * 512;
if (off >= part_size_bytes) {
return -1;
}
size_t l = MIN(part_size_bytes - off, count);
return blk_read(part->device, buf, off + part_off_bytes, l);
}
static int blk_add_part(struct vnode *of, int n, uint64_t lba, uint64_t size) {
// Allocate both a device and partition details
char *buf = kmalloc(sizeof(struct blk_part) + sizeof(struct blkdev));
_assert(buf);
struct blk_part *part = (struct blk_part *) (buf + sizeof(struct blkdev));
struct blkdev *dev = (struct blkdev *) buf;
dev->dev_data = part;
dev->read = blk_part_read;
dev->write = blk_part_write;
dev->flags = 0;
part->device = of->dev;
part->lba_start = lba;
part->size = size;
char name[16];
strcpy(name, of->name);
strcat(name, "_");
size_t l = strlen(name);
name[l] = n + '0';
name[l + 1] = 0;
dev_add(DEV_CLASS_BLOCK, DEV_BLOCK_PART, dev, name);
return 0;
}
static int blk_enumerate_gpt(struct blkdev *dev, struct vnode *of, uint8_t *head) {
// LBA1 - GPT header
struct gpt_header *hdr = (struct gpt_header *) &head[512];
// Read a single block for partition table entries
char buf[512];
_assert(blk_read(dev, buf, hdr->partition_entry_lba * 512, 512) >= 0);
size_t offset = 0;
size_t part_n = 0;
int res;
for (size_t i = 0; i < hdr->partition_count; ++i) {
struct gpt_part_entry *ent = (struct gpt_part_entry *) &buf[offset];
if (strncmp(ent->type_guid, GUID_EMPTY, 16)) {
// Maybe some known filesystem here, ignore partition type guid
if ((res = blk_add_part(of, i, ent->start_lba, ent->end_lba - ent->start_lba)) != 0) {
kdebug("Failed to add partition: %s\n", kstrerror(res));
}
}
offset += hdr->partition_entry_size;
if (offset >= 512) {
break;
}
}
return 0;
}
int blk_enumerate_partitions(struct blkdev *blk, struct vnode *node) {
uint8_t buf[1024];
ssize_t res;
if ((res = blk_read(blk, buf, 0, 1024)) < 0) {
return res;
}
if (!strncmp((const char *) (buf + 512), "EFI PART", 8)) {
return blk_enumerate_gpt(blk, node, buf);
}
return 0;
}
+10 -7
View File
@@ -37,9 +37,12 @@ int dev_add(enum dev_class cls, int subcls, void *dev, const char *name) {
// Use inode number to store full device class:subclass
node->ino = ((uint32_t) cls) | ((uint64_t) subcls << 32);
node->flags |= VN_MEMORY;
// If root does not yet exist, make one
if (!devfs_root) {
devfs_root = vnode_create(VN_DIR, NULL);
devfs_root->flags |= VN_MEMORY;
}
// TODO: some devices are located in subdirs
@@ -47,12 +50,12 @@ int dev_add(enum dev_class cls, int subcls, void *dev, const char *name) {
kinfo("Created device node: %s\n", node->name);
// Self-test
struct vnode *test_node = NULL;
_assert(vnode_lookup_child(devfs_root, name, &test_node) == 0);
_assert(test_node == node);
_assert(vnode_lookup_tree(devfs_root, name, &test_node) == 0);
_assert(test_node == node);
vnode_dump_tree(DEBUG_DEFAULT, devfs_root, 0);
if (cls == DEV_CLASS_BLOCK && subcls < DEV_BLOCK_PART) {
// Find partitions of block devices
blk_enumerate_partitions(dev, node);
}
return 0;
}
@@ -63,7 +66,7 @@ int dev_find(enum dev_class cls, const char *name, struct vnode **node) {
if (!devfs_root) {
return -ENODEV;
}
if ((res = vnode_lookup_tree(devfs_root, name, node)) != 0) {
if ((res = vnode_lookup_child(devfs_root, name, node)) != 0) {
return res;
}
+4
View File
@@ -2,6 +2,10 @@
const char *kstrerror(int e) {
switch (e) {
case -ENOTDIR:
return "Not a directory";
case -ENODEV:
return "No such device";
case -ENOENT:
return "No such file or directory";
case -EINVAL:
+17 -19
View File
@@ -12,8 +12,8 @@ enum vnode_type ext2_inode_type(struct ext2_inode *i) {
return VN_DIR;
case EXT2_TYPE_REG:
return VN_REG;
//case EXT2_TYPE_LNK:
// return VN_LNK;
case EXT2_TYPE_LNK:
return VN_LNK;
default:
// fprintf(stderr, "Unknown file type: %x\n", v);
// abort();
@@ -95,27 +95,25 @@ static int ext2_fs_umount(struct fs *fs) {
static struct vnode *ext2_fs_get_root(struct fs *fs) {
// TODO: make sure get_root is only called once per filesystem
//struct ext2_extsb *sb = fs->fs_private;
//kdebug("ext2_fs_get_root()\n");
struct ext2_extsb *sb = fs->fs_private;
kdebug("ext2_fs_get_root()\n");
//struct ext2_inode *inode = (struct ext2_inode *) kmalloc(sb->inode_struct_size);
//// Read root inode (2)
//if (ext2_read_inode(fs, inode, EXT2_ROOTINO) != 0) {
// kfree(inode);
// return NULL;
//}
struct ext2_inode *inode = (struct ext2_inode *) kmalloc(sb->inode_struct_size);
// Read root inode (2)
if (ext2_read_inode(fs, inode, EXT2_ROOTINO) != 0) {
kfree(inode);
return NULL;
}
//vnode_t *res = (vnode_t *) kmalloc(sizeof(vnode_t));
struct vnode *res = vnode_create(VN_DIR, NULL);
//res->fs = fs;
//res->fs_data = inode;
//res->fs_number = EXT2_ROOTINO;
//res->op = &ext2_vnode_ops;
//res->type = ext2_inode_type(inode);
res->ino = EXT2_ROOTINO;
res->fs = fs;
res->fs_data = inode;
res->op = &ext2_vnode_ops;
res->type = ext2_inode_type(inode);
//return res;
panic("ext2 get root\n");
return NULL;
return res;
}
static int ext2_fs_statvfs(struct fs *fs, struct statvfs *st) {
+695 -693
View File
File diff suppressed because it is too large Load Diff
+54 -83
View File
@@ -9,27 +9,6 @@
#include "sys/string.h"
#include "sys/heap.h"
static const char *path_element(const char *path, char *element) {
const char *sep = strchr(path, '/');
if (!sep) {
strcpy(element, path);
return NULL;
} else {
_assert(sep - path < NODE_MAXLEN);
strncpy(element, path, sep - path);
element[sep - path] = 0;
while (*sep == '/') {
++sep;
}
if (!*sep) {
return NULL;
}
return sep;
}
}
struct vnode *vnode_create(enum vnode_type t, const char *name) {
struct vnode *node = (struct vnode *) kmalloc(sizeof(struct vnode));
_assert(node);
@@ -41,6 +20,8 @@ struct vnode *vnode_create(enum vnode_type t, const char *name) {
node->first_child = NULL;
node->next_child = NULL;
node->target = NULL;
node->fs = NULL;
node->fs_data = NULL;
@@ -99,6 +80,11 @@ void vnode_dump_tree(int level, struct vnode *node, int depth) {
for (int i = 0; i < depth; ++i) {
debugs(level, " ");
}
if (node->flags & VN_MEMORY) {
debugc(level, 'M');
}
switch (node->type) {
case VN_DIR:
debugs(level, "\033[33m");
@@ -106,6 +92,12 @@ void vnode_dump_tree(int level, struct vnode *node, int depth) {
case VN_REG:
debugs(level, "\033[1m");
break;
case VN_MNT:
debugs(level, "\033[35m");
break;
case VN_LNK:
debugs(level, "\033[36m");
break;
case VN_BLK:
case VN_CHR:
debugs(level, "\033[32m");
@@ -120,9 +112,49 @@ void vnode_dump_tree(int level, struct vnode *node, int depth) {
} else {
debugs(level, "[root]");
}
if (node->type == VN_MNT) {
_assert(node->target);
debugs(level, " => [root]");
node = node->target;
}
while (node && node->type == VN_LNK) {
if (node->target) {
debugs(level, "\033[0m -> ");
switch (node->target->type) {
case VN_DIR:
debugs(level, "\033[33m");
break;
case VN_REG:
debugs(level, "\033[1m");
break;
case VN_MNT:
debugs(level, "\033[35m");
break;
case VN_LNK:
debugs(level, "\033[36m");
break;
case VN_BLK:
case VN_CHR:
debugs(level, "\033[32m");
break;
default:
debugs(level, "\033[41m");
break;
}
debugs(level, node->target->name);
} else {
debugs(level, "\033[0m -> \033[41m???");
}
node = node->target;
}
debugs(level, "\033[0m");
if (node->first_child) {
if (node && node->first_child) {
// Only dirs have children
_assert(node->type == VN_DIR);
debugs(level, " {\n");
for (struct vnode *ch = node->first_child; ch; ch = ch->next_child) {
@@ -157,64 +189,3 @@ int vnode_lookup_child(struct vnode *of, const char *name, struct vnode **child)
return -ENOENT;
}
int vnode_lookup_tree(struct vnode *at, const char *path, struct vnode **child) {
char name[NODE_MAXLEN];
const char *child_path;
struct vnode *node;
int err;
_assert(at);
_assert(child);
if (!path || !*path) {
*child = at;
kdebug("empty hit-final %s\n", at->name);
return 0;
}
// The paths are always assumed to be relative
while (*path == '/') {
++path;
}
while (1) {
child_path = path_element(path, name);
if (!strcmp(name, ".")) {
// Refers to this node
kdebug(". hit %s\n", at->name);
continue;
} else if (!strcmp(name, "..")) {
// Refers to node's parent
// And this won't work for filesystem mountpoints:
// Path like a/mnt/.. would still resolve the same point as
// a/mnt
struct vnode *parent = at->parent;
if (!parent) {
parent = at;
}
kdebug(".. hit %s\n", parent->name);
return vnode_lookup_tree(parent, child_path, child);
}
break;
}
// TODO: lookup_or_load
if ((err = vnode_lookup_child(at, name, &node)) != 0) {
kdebug("miss %s\n", name);
return err;
}
_assert(node);
if (!child_path) {
kdebug("hit-final %s\n", name);
*child = node;
return 0;
} else {
kdebug("hit %s\n", name);
return vnode_lookup_tree(node, child_path, child);
}
}
+1
View File
@@ -121,6 +121,7 @@ static int tar_init(struct fs *tar, const char *opt) {
struct vnode *tar_root = vnode_create(VN_DIR, NULL);
tar->fs_private = tar_root;
tar_root->fs = tar;
tar_root->flags |= VN_MEMORY;
struct vnode *node;
+339
View File
@@ -1,4 +1,343 @@
#include "sys/fs/vfs.h"
#include "sys/fs/fs.h"
#include "sys/string.h"
#include "sys/assert.h"
#include "sys/panic.h"
#include "sys/debug.h"
#include "sys/errno.h"
static int vfs_find_internal(struct vfs_ioctx *ctx, struct vnode *at, const char *path, struct vnode **child);
static struct vnode *vfs_root = NULL;
static const char *path_element(const char *path, char *element) {
const char *sep = strchr(path, '/');
if (!sep) {
strcpy(element, path);
return NULL;
} else {
_assert(sep - path < NODE_MAXLEN);
strncpy(element, path, sep - path);
element[sep - path] = 0;
while (*sep == '/') {
++sep;
}
if (!*sep) {
return NULL;
}
return sep;
}
}
static int vfs_link_resolve_internal(struct vfs_ioctx *ctx, struct vnode *lnk, struct vnode **to, int cnt) {
_assert(lnk);
if (lnk->type != VN_LNK) {
*to = lnk;
return 0;
}
if (!lnk->target && !(lnk->flags & VN_MEMORY)) {
// Try to load link contents from storage
_assert(lnk->op);
_assert(lnk->op->readlink);
_assert(lnk->parent);
char path[PATH_MAX];
int res;
if ((res = lnk->op->readlink(lnk, path, PATH_MAX)) != 0) {
return res;
}
// Try to load that path
if ((res = vfs_find(ctx, lnk->parent, path, &lnk->target)) != 0) {
lnk->target = NULL;
return res;
}
}
if (lnk->target) {
if (lnk->target->type == VN_LNK) {
if (cnt <= 1) {
return -ELOOP;
}
return vfs_link_resolve_internal(ctx, lnk->target, to, cnt - 1);
} else {
*to = lnk->target;
return 0;
}
}
return -ENOENT;
}
int vfs_link_resolve(struct vfs_ioctx *ctx, struct vnode *lnk, struct vnode **to) {
return vfs_link_resolve_internal(ctx, lnk, to, LINK_MAX);
}
static int vfs_lookup_or_load(struct vnode *at, const char *name, struct vnode **child) {
int res;
if ((res = vnode_lookup_child(at, name, child)) == 0) {
return 0;
}
if (res != -ENOENT) {
return res;
}
// Try to load vnode if possible
if (!(at->flags & VN_MEMORY)) {
_assert(at->op);
_assert(at->op->find);
struct vnode *node;
if ((res = at->op->find(at, name, &node)) != 0) {
return res;
}
// Attach found node to its parent
vnode_attach(at, node);
*child = node;
return 0;
}
return -ENOENT;
}
static int vfs_find_internal(struct vfs_ioctx *ctx, struct vnode *at, const char *path, struct vnode **child) {
// FIXME: trailing slash should result in symlink resolution in cases like:
// /dir
// /lnk -> /dir
// And path is /lnk/ should result in /dir
char name[NODE_MAXLEN];
const char *child_path;
struct vnode *node;
int err;
if (!at) {
return -ENOENT;
}
_assert(child);
if (!path || !*path) {
// TODO: if mnt
*child = at;
kdebug("empty hit-final %s\n", at->name);
return 0;
}
if (at->type == VN_LNK) {
if ((err = vfs_link_resolve(ctx, at, &node)) != 0) {
kdebug("miss: link resolution: %s\n", at->name);
return err;
} else {
at = node;
}
}
// TODO: links to links
_assert(at->type != VN_LNK);
if (at->type == VN_MNT) {
at = at->target;
_assert(at);
}
if (at->type != VN_DIR) {
// Only directories contain files
return -ENOTDIR;
}
// TODO: check access to "at" here
// The paths are always assumed to be relative
while (*path == '/') {
++path;
}
while (1) {
child_path = path_element(path, name);
if (!strcmp(name, ".")) {
// Refers to this node
kdebug(". hit %s\n", at->name);
if (!child_path) {
*child = at;
return 0;
}
continue;
} else if (!strcmp(name, "..")) {
// Refers to node's parent
// And this won't work for filesystem mountpoints:
// Path like a/mnt/.. would still resolve the same point as
// a/mnt
struct vnode *parent = at->parent;
if (!parent) {
parent = at;
}
kdebug(".. hit %s\n", parent->name);
return vfs_find_internal(ctx, parent, child_path, child);
}
break;
}
// TODO: lookup_or_load
if ((err = vfs_lookup_or_load(at, name, &node)) != 0) {
kdebug("miss %s in %s\n", name, at->name);
return err;
}
_assert(node);
if (!child_path) {
kdebug("hit-final %s\n", name);
*child = node;
return 0;
} else {
kdebug("hit %s\n", name);
return vfs_find_internal(ctx, node, child_path, child);
}
return -ENOENT;
}
void vfs_init(void) {
if (vfs_root) {
vnode_dump_tree(DEBUG_INFO, vfs_root, 0);
}
}
static int vfs_mount_internal(struct vnode *at, void *blk, struct fs_class *cls, const char *opt) {
int res;
struct fs *fs;
struct vnode *fs_root;
struct blkdev *dev = blk;
// Device checks
if (blk) {
if (dev->flags & BLK_BUSY) {
return -EBUSY;
}
}
// Mountpoint checks
if (at) {
if (at->type != VN_DIR) {
return -ENOTDIR;
}
}
if (!(fs = fs_create(cls, blk))) {
kwarn("Filesystem creation failed\n");
return -EINVAL;
}
_assert(cls->get_root);
if (!(fs_root = cls->get_root(fs))) {
kwarn("Filesystem has no root\n");
// TODO: cleanup
return -EINVAL;
}
// Attach root to mountpoint
if (!at) {
// Make filesystem root new VFS root
vfs_root = fs_root;
} else {
at->type = VN_MNT;
at->target = fs_root;
// TODO: this prevents root nodes from being mounted at
// multiple targets
fs_root->parent = at->parent;
}
if (blk) {
dev->flags |= BLK_BUSY;
}
vnode_dump_tree(DEBUG_DEFAULT, vfs_root, 0);
// TODO: call cls->mount
return 0;
}
int vfs_mount(struct vfs_ioctx *ctx, const char *at, void *blk, const char *fs, const char *opt) {
int res;
struct fs_class *fs_class;
struct vnode *mountpoint;
// NOTE: Mount uses only absolute paths
if (at[0] != '/') {
kwarn("Non-absolute path used for mounting\n");
return -EINVAL;
}
if (!fs || !strcmp(fs, "auto")) {
panic("TODO: support filesystem guessing\n");
} else {
fs_class = fs_class_by_name(fs);
if (!fs_class) {
kwarn("Unknown fs: %s\n", fs);
return -EINVAL;
}
}
// Find mount point
if (!strcmp(at, "/")) {
mountpoint = NULL;
} else {
if ((res = vfs_find(ctx, ctx->cwd_vnode, at, &mountpoint)) != 0) {
return res;
}
}
return vfs_mount_internal(mountpoint, blk, fs_class, opt);
}
int vfs_find(struct vfs_ioctx *ctx, struct vnode *rel, const char *path, struct vnode **node) {
int res;
struct vnode *r;
if (!vfs_root) {
return -ENOENT;
}
if (path[0] == '/') {
// Absolute lookup
while (*path == '/') {
++path;
}
res = vfs_find_internal(ctx, vfs_root, path, &r);
} else {
if (!rel) {
rel = ctx->cwd_vnode;
}
if (!rel) {
rel = vfs_root;
}
res = vfs_find_internal(ctx, rel, path, &r);
}
if (res) {
return res;
}
if (r->type == VN_MNT) {
r = r->target;
}
*node = r;
return 0;
}