Add find() and link resolution to VFS
This commit is contained in:
+1
-1
@@ -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
@@ -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);
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
#define EDOM 33
|
||||
#define ERANGE 34
|
||||
|
||||
#define ELOOP 40
|
||||
|
||||
#if defined(__KERNEL__)
|
||||
const char *kstrerror(int e);
|
||||
#endif
|
||||
|
||||
+11
-3
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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");
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+54
-83
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user