From fa399280fdb04c17159902eb0fb06cce03945ee4 Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 12 Jan 2020 12:57:31 +0200 Subject: [PATCH] Add find() and link resolution to VFS --- etc/make/conf.mk | 2 +- include/sys/blk.h | 6 +- include/sys/errno.h | 2 + include/sys/fs/node.h | 14 +- include/sys/fs/vfs.h | 10 + sys/amd64/hw/ide/ahci.c | 1 + sys/amd64/sys/sched.c | 45 +- sys/blk.c | 137 ++++ sys/dev.c | 17 +- sys/errno.c | 4 + sys/vfs/ext2/ext2.c | 36 +- sys/vfs/ext2/ext2vnop.c | 1388 ++++++++++++++++++++------------------- sys/vfs/node.c | 137 ++-- sys/vfs/tar.c | 1 + sys/vfs/vfs.c | 339 ++++++++++ 15 files changed, 1301 insertions(+), 838 deletions(-) diff --git a/etc/make/conf.mk b/etc/make/conf.mk index 2864759..4304a37 100644 --- a/etc/make/conf.mk +++ b/etc/make/conf.mk @@ -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 \ diff --git a/include/sys/blk.h b/include/sys/blk.h index ed20b32..1ea7b4b 100644 --- a/include/sys/blk.h +++ b/include/sys/blk.h @@ -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); diff --git a/include/sys/errno.h b/include/sys/errno.h index c62d8f6..c8c3002 100644 --- a/include/sys/errno.h +++ b/include/sys/errno.h @@ -35,6 +35,8 @@ #define EDOM 33 #define ERANGE 34 +#define ELOOP 40 + #if defined(__KERNEL__) const char *kstrerror(int e); #endif diff --git a/include/sys/fs/node.h b/include/sys/fs/node.h index 6e9def6..7ae7b20 100644 --- a/include/sys/fs/node.h +++ b/include/sys/fs/node.h @@ -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); - diff --git a/include/sys/fs/vfs.h b/include/sys/fs/vfs.h index 5f70ec2..773db6f 100644 --- a/include/sys/fs/vfs.h +++ b/include/sys/fs/vfs.h @@ -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); diff --git a/sys/amd64/hw/ide/ahci.c b/sys/amd64/hw/ide/ahci.c index 90dc71c..0cab9ee 100644 --- a/sys/amd64/hw/ide/ahci.c +++ b/sys/amd64/hw/ide/ahci.c @@ -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); } diff --git a/sys/amd64/sys/sched.c b/sys/amd64/sys/sched.c index 7f92458..2fffc4d 100644 --- a/sys/amd64/sys/sched.c +++ b/sys/amd64/sys/sched.c @@ -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"); diff --git a/sys/blk.c b/sys/blk.c index ee03d50..f46be1c 100644 --- a/sys/blk.c +++ b/sys/blk.c @@ -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; +} diff --git a/sys/dev.c b/sys/dev.c index 71de196..731657c 100644 --- a/sys/dev.c +++ b/sys/dev.c @@ -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; } diff --git a/sys/errno.c b/sys/errno.c index bbe2d43..2b0c318 100644 --- a/sys/errno.c +++ b/sys/errno.c @@ -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: diff --git a/sys/vfs/ext2/ext2.c b/sys/vfs/ext2/ext2.c index db8f61f..cadbdd6 100644 --- a/sys/vfs/ext2/ext2.c +++ b/sys/vfs/ext2/ext2.c @@ -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) { diff --git a/sys/vfs/ext2/ext2vnop.c b/sys/vfs/ext2/ext2vnop.c index 829055f..56d2e35 100644 --- a/sys/vfs/ext2/ext2vnop.c +++ b/sys/vfs/ext2/ext2vnop.c @@ -19,52 +19,52 @@ // #include // Forward declaration of ext2 vnode functions -static int ext2_vnode_find(vnode_t *vn, const char *name, vnode_t **resvn); -static int ext2_vnode_creat(vnode_t *at, struct vfs_ioctx *ctx, const char *name, mode_t mode, int opt, vnode_t **resvn); -static int ext2_vnode_mkdir(vnode_t *at, const char *name, mode_t mode); -static int ext2_vnode_open(vnode_t *vn, int opt); -static int ext2_vnode_opendir(vnode_t *vn, int opt); -static ssize_t ext2_vnode_read(struct ofile *fd, void *buf, size_t count); -static ssize_t ext2_vnode_write(struct ofile *fd, const void *buf, size_t count); -static int ext2_vnode_truncate(struct ofile *fd, size_t length); -static int ext2_vnode_readdir(struct ofile *fd); -static void ext2_vnode_destroy(vnode_t *vn); -static int ext2_vnode_stat(vnode_t *vn, struct stat *st); -static int ext2_vnode_chmod(vnode_t *vn, mode_t mode); -static int ext2_vnode_chown(vnode_t *vn, uid_t uid, gid_t gid); -static int ext2_vnode_unlink(vnode_t *at, vnode_t *vn, const char *name); -static int ext2_vnode_access(vnode_t *vn, uid_t *uid, gid_t *gid, mode_t *mode); -static int ext2_vnode_readlink(vnode_t *vn, char *dst); -static int ext2_vnode_symlink(vnode_t *at, struct vfs_ioctx *ctx, const char *name, const char *dst); +static int ext2_vnode_find(struct vnode *vn, const char *name, struct vnode **resvn); +//static int ext2_vnode_creat(vnode_t *at, struct vfs_ioctx *ctx, const char *name, mode_t mode, int opt, vnode_t **resvn); +//static int ext2_vnode_mkdir(vnode_t *at, const char *name, mode_t mode); +//static int ext2_vnode_open(vnode_t *vn, int opt); +//static int ext2_vnode_opendir(vnode_t *vn, int opt); +//static ssize_t ext2_vnode_read(struct ofile *fd, void *buf, size_t count); +//static ssize_t ext2_vnode_write(struct ofile *fd, const void *buf, size_t count); +//static int ext2_vnode_truncate(struct ofile *fd, size_t length); +//static int ext2_vnode_readdir(struct ofile *fd); +//static void ext2_vnode_destroy(vnode_t *vn); +//static int ext2_vnode_stat(vnode_t *vn, struct stat *st); +//static int ext2_vnode_chmod(vnode_t *vn, mode_t mode); +//static int ext2_vnode_chown(vnode_t *vn, uid_t uid, gid_t gid); +//static int ext2_vnode_unlink(vnode_t *at, vnode_t *vn, const char *name); +//static int ext2_vnode_access(vnode_t *vn, uid_t *uid, gid_t *gid, mode_t *mode); +static int ext2_vnode_readlink(struct vnode *vn, char *dst, size_t lim); +//static int ext2_vnode_symlink(vnode_t *at, struct vfs_ioctx *ctx, const char *name, const char *dst); struct vnode_operations ext2_vnode_ops = { .find = ext2_vnode_find, - .creat = ext2_vnode_creat, - .mkdir = ext2_vnode_mkdir, - .destroy = ext2_vnode_destroy, - +// .creat = ext2_vnode_creat, +// .mkdir = ext2_vnode_mkdir, +// .destroy = ext2_vnode_destroy, +// .readlink = ext2_vnode_readlink, - .symlink = ext2_vnode_symlink, - - .chmod = ext2_vnode_chmod, - .chown = ext2_vnode_chown, - .stat = ext2_vnode_stat, - .unlink = ext2_vnode_unlink, - .access = ext2_vnode_access, - - .opendir = ext2_vnode_opendir, - .readdir = ext2_vnode_readdir, - - .open = ext2_vnode_open, - .read = ext2_vnode_read, - .write = ext2_vnode_write, - .truncate = ext2_vnode_truncate, +// .symlink = ext2_vnode_symlink, +// +// .chmod = ext2_vnode_chmod, +// .chown = ext2_vnode_chown, +// .stat = ext2_vnode_stat, +// .unlink = ext2_vnode_unlink, +// .access = ext2_vnode_access, +// +// .opendir = ext2_vnode_opendir, +// .readdir = ext2_vnode_readdir, +// +// .open = ext2_vnode_open, +// .read = ext2_vnode_read, +// .write = ext2_vnode_write, +// .truncate = ext2_vnode_truncate, }; //// vnode function implementation -static int ext2_vnode_find(vnode_t *vn, const char *name, vnode_t **res) { - fs_t *ext2 = vn->fs; +static int ext2_vnode_find(struct vnode *vn, const char *name, struct vnode **res) { + struct fs *ext2 = vn->fs; struct ext2_extsb *sb = vn->fs->fs_private; struct ext2_inode *inode = vn->fs_data; @@ -90,21 +90,19 @@ static int ext2_vnode_find(vnode_t *vn, const char *name, vnode_t **res) { if (dirent->ino) { if (strlen(name) == dirent->name_len && !strncmp(dirent->name, name, dirent->name_len)) { // Found the entry - vnode_t *out = (vnode_t *) kmalloc(sizeof(vnode_t)); - out->op = &ext2_vnode_ops; - out->fs = ext2; - - struct ext2_inode *result_inode = (struct ext2_inode *) kmalloc(sb->inode_struct_size); - if (ext2_read_inode(ext2, result_inode, dirent->ino) != 0) { + inode = kmalloc(sb->inode_struct_size); + if (ext2_read_inode(ext2, inode, dirent->ino) != 0) { return -EIO; } - out->fs_data = result_inode; - out->fs_number = dirent->ino; - out->type = ext2_inode_type(result_inode); + struct vnode *node = vnode_create(ext2_inode_type(inode), name); + node->fs = ext2; + node->fs_data = inode; + node->ino = dirent->ino; + node->op = &ext2_vnode_ops; + + *res = node; - *res = out; - //printf("Lookup %s in ino %d = %d\n", name, vn->fs_number, out->fs_number); return 0; } } @@ -119,572 +117,576 @@ static int ext2_vnode_find(vnode_t *vn, const char *name, vnode_t **res) { return -ENOENT; } - -static int ext2_vnode_opendir(vnode_t *vn, int opt) { - _assert(vn->type == VN_DIR); - return 0; -} - -static int ext2_vnode_open(vnode_t *vn, int opt) { - _assert(vn->type == VN_REG); - return 0; -} - -static int ext2_vnode_mkdir(vnode_t *at, const char *name, mode_t mode) { - fs_t *ext2 = at->fs; - _assert(at->type == VN_DIR); - struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private; - char block_buffer[sb->block_size]; - - uint32_t new_ino, new_block_no; - int res; - - // Allocate a new inode for the directory - if ((res = ext2_alloc_inode(ext2, &new_ino)) != 0) { - kerror("ext2: Failed to allocate an inode\n"); - return res; - } - - // Allocate a block for "." and ".." entries - if ((res = ext2_alloc_block(ext2, &new_block_no)) < 0) { - kerror("ext2: Failed to allocate a block\n"); - return res; - } - - struct ext2_inode *ent_inode = (struct ext2_inode *) kmalloc(sb->inode_struct_size); - - // Now create an entry in parents dirent list - if ((res = ext2_dir_add_inode(ext2, at, name, new_ino)) < 0) { - return res; - } - - // Fill the inode - ent_inode->flags = 0; - ent_inode->dir_acl = 0; - ent_inode->frag_block_addr = 0; - ent_inode->gen_number = 0; - ent_inode->hard_link_count = 1; - ent_inode->acl = 0; - ent_inode->os_value_1 = 0; - memset(ent_inode->os_value_2, 0, sizeof(ent_inode->os_value_2)); - // TODO: time support in kernel - ent_inode->atime = 0; - ent_inode->mtime = 0; - ent_inode->ctime = 0; - ent_inode->dtime = 0; - - memset(ent_inode->direct_blocks, 0, sizeof(ent_inode->direct_blocks)); - ent_inode->direct_blocks[0] = new_block_no; - ent_inode->l1_indirect_block = 0; - ent_inode->l2_indirect_block = 0; - ent_inode->l3_indirect_block = 0; - - ent_inode->type_perm = (mode & 0x1FF) | EXT2_TYPE_DIR; - // TODO: obtain these from process context in kernel - ent_inode->uid = 0; - ent_inode->gid = 0; - ent_inode->disk_sector_count = 0; - ent_inode->size_lower = sb->block_size; - - memset(block_buffer, 0, sb->block_size); - // "." - struct ext2_dirent *dirent = (struct ext2_dirent *) block_buffer; - dirent->ino = new_ino; - dirent->name_len = 1; - dirent->len = (sizeof(struct ext2_dirent) + 4) & ~3; - dirent->name[0] = '.'; - dirent->type_ind = 0; - - // ".." - dirent = (struct ext2_dirent *) &block_buffer[dirent->len]; - dirent->ino = at->fs_number; - dirent->name_len = 2; - dirent->len = sb->block_size - ((sizeof(struct ext2_dirent) + 4) & ~3); - dirent->name[0] = '.'; - dirent->name[1] = '.'; - dirent->type_ind = 0; - - // Write directory's first block - if ((res = ext2_write_block(ext2, new_block_no, block_buffer)) < 0) { - return res; - } - - // Write directory inode - if ((res = ext2_write_inode(ext2, ent_inode, new_ino)) < 0) { - return res; - } - - return 0; -} - -static int ext2_vnode_creat(vnode_t *at, struct vfs_ioctx *ctx, const char *name, mode_t mode, int opt, vnode_t **resvn) { - fs_t *ext2 = at->fs; - _assert(at->type == VN_DIR); - _assert(/* Don't support making directories like this */ !(mode & O_DIRECTORY)); - struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private; - - uint32_t new_ino; - int res; - - // Allocate new inode number - if ((res = ext2_alloc_inode(ext2, &new_ino)) != 0) { - kerror("Failed to allocate inode\n"); - return res; - } - - kdebug("Allocated inode %d\n", new_ino); - - // Create an inode struct in memory - struct ext2_inode *ent_inode = (struct ext2_inode *) kmalloc(sb->inode_struct_size); - - // Now create an entry in parents dirent list - if ((res = ext2_dir_add_inode(ext2, at, name, new_ino)) < 0) { - return res; - } - - // Fill the inode - ent_inode->flags = 0; - ent_inode->dir_acl = 0; - ent_inode->frag_block_addr = 0; - ent_inode->gen_number = 0; - ent_inode->hard_link_count = 1; - ent_inode->acl = 0; - ent_inode->os_value_1 = 0; - memset(ent_inode->os_value_2, 0, sizeof(ent_inode->os_value_2)); - // TODO: time support in kernel - ent_inode->atime = 0; - ent_inode->mtime = 0; - ent_inode->ctime = 0; - ent_inode->dtime = 0; - - memset(ent_inode->direct_blocks, 0, sizeof(ent_inode->direct_blocks)); - ent_inode->l1_indirect_block = 0; - ent_inode->l2_indirect_block = 0; - ent_inode->l3_indirect_block = 0; - - ent_inode->uid = ctx->uid; - ent_inode->gid = ctx->gid; - // TODO: only regular files can be created this way now - ent_inode->type_perm = (mode & 0x1FF) | (EXT2_TYPE_REG); - ent_inode->disk_sector_count = 0; - ent_inode->size_lower = 0; - - // Write the inode - if ((res = ext2_write_inode(ext2, ent_inode, new_ino)) < 0) { - return res; - } - - // Create the resulting vnode - vnode_t *vn = (vnode_t *) kmalloc(sizeof(vnode_t)); - vn->fs = ext2; - vn->fs_data = ent_inode; - vn->fs_number = new_ino; - vn->op = &ext2_vnode_ops; - vn->type = VN_REG; - - *resvn = vn; - - return 0; -} - -static ssize_t ext2_vnode_read(struct ofile *fd, void *buf, size_t count) { - vnode_t *vn = fd->vnode; - struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data; - struct ext2_extsb *sb = vn->fs->fs_private; - - size_t nread = MIN(inode->size_lower - fd->pos, count); - - if (nread == 0) { - return -1; - } - - size_t block_number = fd->pos / sb->block_size; - size_t nblocks = (nread + sb->block_size - 1) / sb->block_size; - char block_buffer[sb->block_size]; - - for (size_t i = 0; i < nblocks; ++i) { - if (ext2_read_inode_block(vn->fs, inode, i + block_number, block_buffer) < 0) { - kerror("Failed to read inode %d block #%zu\n", vn->fs_number, i + block_number); - return -EIO; - } - if (i == 0) { - size_t ncpy = MIN(sb->block_size - fd->pos % sb->block_size, nread); - memcpy(buf, block_buffer + fd->pos % sb->block_size, ncpy); - } else { - size_t ncpy = MIN(sb->block_size, nread - sb->block_size * i); - memcpy((void *) (((uintptr_t) buf) + sb->block_size * i), block_buffer, ncpy); - } - } - - return nread; -} - -static ssize_t ext2_vnode_write(struct ofile *fd, const void *buf, size_t count) { - vnode_t *vn = fd->vnode; - _assert(vn); - struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data; - fs_t *ext2 = vn->fs; - struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private; - char block_buffer[sb->block_size]; - int res; - - if (fd->pos > inode->size_lower) { - // This shouldn't be possible, yeah? - return -ESPIPE; - } - - // How many bytes can we write into the blocks already allocated - size_t size_blocks = (inode->size_lower + sb->block_size - 1) / sb->block_size; - size_t can_write = size_blocks * sb->block_size - inode->size_lower; - size_t current_block = fd->pos / sb->block_size; - size_t written = 0; - size_t remaining = count; - - if (can_write) { - size_t can_write_blocks = (can_write + sb->block_size - 1) / sb->block_size; - - for (size_t i = 0; i < can_write_blocks; ++i) { - size_t block_index = current_block + i; - size_t pos_in_block = fd->pos % sb->block_size; - size_t need_write = MIN(remaining, sb->block_size - pos_in_block); - - kdebug("Write %zuB to block %zu offset %zu\n", need_write, block_index, pos_in_block); - if (need_write == sb->block_size) { - // Can write block without reading it - // TODO: implement this - panic("Not implemented\n"); - } else { - // Read the block to change its contents - // and write it back again - if ((res = ext2_read_inode_block(ext2, inode, block_index, block_buffer)) < 0) { - break; - } - - memcpy(block_buffer + pos_in_block, (void *) (((uintptr_t) buf) + written), need_write); - - if ((res = ext2_write_inode_block(ext2, inode, block_index, block_buffer)) < 0) { - break; - } - } - - written += need_write; - fd->pos += need_write; - remaining -= need_write; - } - - inode->size_lower = MAX(fd->pos, inode->size_lower); - current_block += can_write_blocks; - } - - if (remaining) { - // Need to allocate additional blocks - size_t need_blocks = (remaining + sb->block_size - 1) / sb->block_size; - - for (size_t i = 0; i < need_blocks; ++i) { - size_t block_index = current_block + i; - size_t need_write = MIN(remaining, sb->block_size); - - // Update the size here so it gets written when the block is allocated - // and inode struct is flushed - inode->size_lower += need_write; - // Allocate a block for the index - if ((res = ext2_inode_alloc_block(ext2, inode, vn->fs_number, block_index)) < 0) { - kerror("Could not allocate a block for writing\n"); - break; - } - - - if (need_write == sb->block_size) { - // TODO: implement this - panic("Not implemented\n"); - } else { - // Writing the last block - memcpy(block_buffer, (void *) (((uintptr_t) buf) + written), need_write); - - if ((res = ext2_write_inode_block(ext2, inode, block_index, block_buffer)) < 0) { - break; - } - } - - written += need_write; - fd->pos += need_write; - remaining -= need_write; - } - } else { - if (written) { - // Flush inode struct to disk - size has changed - ext2_write_inode(ext2, inode, vn->fs_number); - } - } - - return written; -} - -static int ext2_vnode_truncate(struct ofile *fd, size_t length) { - vnode_t *vn = fd->vnode; - fs_t *ext2 = vn->fs; - struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data; - struct ext2_extsb *sb = vn->fs->fs_private; - int res; - - if (length == inode->size_lower) { - // Already good - return 0; - } - - size_t was_blocks = (inode->size_lower + sb->block_size - 1) / sb->block_size; - size_t now_blocks = (length + sb->block_size - 1) / sb->block_size; - ssize_t delta_blocks = now_blocks - was_blocks; - - if (delta_blocks < 0) { - // Free truncated blocks - // XXX: reverse the loop - for (size_t i = now_blocks; i < was_blocks; ++i) { - // Modify inode right here because ext2_free_inode_block will - // flush these changes to disk so we don't have to write it - // twice - inode->size_lower -= sb->block_size; - if ((res = ext2_free_inode_block(ext2, inode, vn->fs_number, i)) < 0) { - // Put the block size back, couldn't free it - inode->size_lower += sb->block_size; - return res; - } - } - - // All the blocks were successfully freed, can set proper file length - if (inode->size_lower != length) { - // If requested size is not block-aligned, we need to write inode - // struct to disk once again - inode->size_lower = length; - - if ((res = ext2_write_inode(ext2, inode, vn->fs_number)) < 0) { - return res; - } - } - - return 0; - } else { - kerror("Not implemented: upwards truncation (ext2)\n"); - return -EINVAL; - } -} - -// TODO: replace this with getdents -static int ext2_vnode_readdir(struct ofile *fd) { - vnode_t *vn = fd->vnode; - struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data; - struct ext2_extsb *sb = vn->fs->fs_private; - ssize_t res; - - if (fd->pos >= inode->size_lower) { - return -1; - } - - size_t block_number = fd->pos / sb->block_size; - char block_buffer[sb->block_size]; - - if ((res = ext2_read_inode_block(vn->fs, inode, block_number, block_buffer)) < 0) { - return res; - } - - size_t block_offset = fd->pos % sb->block_size; - struct ext2_dirent *ext2dir = (struct ext2_dirent *) &block_buffer[block_offset]; - - if (ext2dir->len == 0) { - // If entry size is zero, guess we're finished - align the fd->pos up to block size - fd->pos = (fd->pos + sb->block_size - 1) / sb->block_size; - return ext2_vnode_readdir(fd); - } - - struct dirent *vfsdir = (struct dirent *) fd->dirent_buf; - - vfsdir->d_ino = ext2dir->ino; - strncpy(vfsdir->d_name, ext2dir->name, ext2dir->name_len); - vfsdir->d_name[ext2dir->name_len] = 0; - vfsdir->d_reclen = ext2dir->len; - if (sb->required_features & 2 /* Directory entries contain type field */) { - switch (ext2dir->type_ind) { - case 1: - // Regular file - vfsdir->d_type = DT_REG; - break; - case 2: - // Directory - vfsdir->d_type = DT_DIR; - break; - // XXX: Don't know if I should have ANY devices in ext2, we have devfs for that - // kind of shit - case 0: - default: - vfsdir->d_type = DT_UNKNOWN; - break; - } - } else { - // Have to read each fucking inode to sort this mess out - struct ext2_inode ent_inode_buf; - - if ((res = ext2_read_inode(vn->fs, &ent_inode_buf, ext2dir->ino)) < 0) { - return res; - } - - switch (ent_inode_buf.type_perm & 0xF000) { - case EXT2_TYPE_REG: - vfsdir->d_type = DT_REG; - break; - case EXT2_TYPE_DIR: - vfsdir->d_type = DT_DIR; - break; - default: - vfsdir->d_type = DT_UNKNOWN; - break; - } - } - // Not implemented, I guess - vfsdir->d_off = 0; - - fd->pos += ext2dir->len; - - return 0; -} - -static void ext2_vnode_destroy(vnode_t *vn) { - // Release inode struct - kfree(vn->fs_data); -} - -static int ext2_vnode_stat(vnode_t *vn, struct stat *st) { - _assert(vn && vn->fs); - struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data; - _assert(inode); - struct ext2_extsb *sb = (struct ext2_extsb *) vn->fs->fs_private; - _assert(sb); - - st->st_atime = inode->atime; - st->st_ctime = inode->ctime; - st->st_mtime = inode->mtime; - st->st_dev = 0; // Not implemented - st->st_rdev = 0; // Not implemented - st->st_gid = inode->gid; - st->st_uid = inode->uid; - st->st_mode = inode->type_perm; - st->st_size = inode->size_lower; - st->st_blocks = (inode->size_lower + sb->block_size - 1) / sb->block_size; - st->st_blksize = sb->block_size; - st->st_nlink = 0; - st->st_ino = vn->fs_number; - - return 0; -} - -static int ext2_vnode_chmod(vnode_t *vn, mode_t mode) { - _assert(vn && vn->fs && vn->fs_data); - struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data; - - // Update only access mode - inode->type_perm &= ~0x1FF; - inode->type_perm |= mode & 0x1FF; - - // Write the inode back - return ext2_write_inode(vn->fs, inode, vn->fs_number); -} - -static int ext2_vnode_chown(vnode_t *vn, uid_t uid, gid_t gid) { - _assert(vn && vn->fs && vn->fs_data); - struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data; - - inode->gid = gid; - inode->uid = uid; - - // Write the inode back - return ext2_write_inode(vn->fs, inode, vn->fs_number); -} - -static int ext2_vnode_unlink(vnode_t *at, vnode_t *vn, const char *name) { - struct ext2_inode *inode = vn->fs_data; - struct ext2_inode *at_inode = at->fs_data; - fs_t *ext2 = vn->fs; - struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private; - uint32_t ino = vn->fs_number; - int res; - - if (vn->type == VN_DIR) { - // Check if the directory we're unlinking has any entries besides - // . and .. - // Can tell this just by looking at the first block - if (inode->size_lower > sb->block_size) { - // Directory size is more than one block - totally - // has something inside - return -EISDIR; - } - char block_buffer[sb->block_size]; - size_t off = 0; - - if ((res = ext2_read_inode_block(ext2, inode, 0, block_buffer)) < 0) { - return res; - } - - while (off < sb->block_size) { - struct ext2_dirent *ent = (struct ext2_dirent *) &block_buffer[off]; - if (!ent->ino) { - break; - } - if (ent->name_len == 1 && ent->name[0] == '.') { - off += ent->len; - continue; - } - if (ent->name_len == 2 && ent->name[1] == '.' && ent->name[0] == '.') { - off += ent->len; - continue; - } - - return -EISDIR; - } - } - - // Free blocks used by the inode - truncate the file to zero - size_t nblocks = (inode->size_lower + sb->block_size - 1) / sb->block_size; - - inode->size_lower = nblocks * sb->block_size; - for (ssize_t i = nblocks - 1; i >= 0; --i) { - inode->size_lower -= sb->block_size; - if ((res = ext2_free_inode_block(ext2, inode, ino, i)) < 0) { - return res; - } - } - - // inode->size_lower is now 0 - _assert(inode->size_lower == 0); - - // Free the inode itself - if ((res = ext2_free_inode(ext2, ino)) < 0) { - return res; - } - - // Now remove the entry from directory - if ((res = ext2_dir_remove_inode(ext2, at, name, ino)) < 0) { - return res; - } - - return 0; -} - -static int ext2_vnode_access(vnode_t *vn, uid_t *uid, gid_t *gid, mode_t *mode) { +// +//static int ext2_vnode_opendir(vnode_t *vn, int opt) { +// _assert(vn->type == VN_DIR); +// return 0; +//} +// +//static int ext2_vnode_open(vnode_t *vn, int opt) { +// _assert(vn->type == VN_REG); +// return 0; +//} +// +//static int ext2_vnode_mkdir(vnode_t *at, const char *name, mode_t mode) { +// fs_t *ext2 = at->fs; +// _assert(at->type == VN_DIR); +// struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private; +// char block_buffer[sb->block_size]; +// +// uint32_t new_ino, new_block_no; +// int res; +// +// // Allocate a new inode for the directory +// if ((res = ext2_alloc_inode(ext2, &new_ino)) != 0) { +// kerror("ext2: Failed to allocate an inode\n"); +// return res; +// } +// +// // Allocate a block for "." and ".." entries +// if ((res = ext2_alloc_block(ext2, &new_block_no)) < 0) { +// kerror("ext2: Failed to allocate a block\n"); +// return res; +// } +// +// struct ext2_inode *ent_inode = (struct ext2_inode *) kmalloc(sb->inode_struct_size); +// +// // Now create an entry in parents dirent list +// if ((res = ext2_dir_add_inode(ext2, at, name, new_ino)) < 0) { +// return res; +// } +// +// // Fill the inode +// ent_inode->flags = 0; +// ent_inode->dir_acl = 0; +// ent_inode->frag_block_addr = 0; +// ent_inode->gen_number = 0; +// ent_inode->hard_link_count = 1; +// ent_inode->acl = 0; +// ent_inode->os_value_1 = 0; +// memset(ent_inode->os_value_2, 0, sizeof(ent_inode->os_value_2)); +// // TODO: time support in kernel +// ent_inode->atime = 0; +// ent_inode->mtime = 0; +// ent_inode->ctime = 0; +// ent_inode->dtime = 0; +// +// memset(ent_inode->direct_blocks, 0, sizeof(ent_inode->direct_blocks)); +// ent_inode->direct_blocks[0] = new_block_no; +// ent_inode->l1_indirect_block = 0; +// ent_inode->l2_indirect_block = 0; +// ent_inode->l3_indirect_block = 0; +// +// ent_inode->type_perm = (mode & 0x1FF) | EXT2_TYPE_DIR; +// // TODO: obtain these from process context in kernel +// ent_inode->uid = 0; +// ent_inode->gid = 0; +// ent_inode->disk_sector_count = 0; +// ent_inode->size_lower = sb->block_size; +// +// memset(block_buffer, 0, sb->block_size); +// // "." +// struct ext2_dirent *dirent = (struct ext2_dirent *) block_buffer; +// dirent->ino = new_ino; +// dirent->name_len = 1; +// dirent->len = (sizeof(struct ext2_dirent) + 4) & ~3; +// dirent->name[0] = '.'; +// dirent->type_ind = 0; +// +// // ".." +// dirent = (struct ext2_dirent *) &block_buffer[dirent->len]; +// dirent->ino = at->fs_number; +// dirent->name_len = 2; +// dirent->len = sb->block_size - ((sizeof(struct ext2_dirent) + 4) & ~3); +// dirent->name[0] = '.'; +// dirent->name[1] = '.'; +// dirent->type_ind = 0; +// +// // Write directory's first block +// if ((res = ext2_write_block(ext2, new_block_no, block_buffer)) < 0) { +// return res; +// } +// +// // Write directory inode +// if ((res = ext2_write_inode(ext2, ent_inode, new_ino)) < 0) { +// return res; +// } +// +// return 0; +//} +// +//static int ext2_vnode_creat(vnode_t *at, struct vfs_ioctx *ctx, const char *name, mode_t mode, int opt, vnode_t **resvn) { +// fs_t *ext2 = at->fs; +// _assert(at->type == VN_DIR); +// _assert(/* Don't support making directories like this */ !(mode & O_DIRECTORY)); +// struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private; +// +// uint32_t new_ino; +// int res; +// +// // Allocate new inode number +// if ((res = ext2_alloc_inode(ext2, &new_ino)) != 0) { +// kerror("Failed to allocate inode\n"); +// return res; +// } +// +// kdebug("Allocated inode %d\n", new_ino); +// +// // Create an inode struct in memory +// struct ext2_inode *ent_inode = (struct ext2_inode *) kmalloc(sb->inode_struct_size); +// +// // Now create an entry in parents dirent list +// if ((res = ext2_dir_add_inode(ext2, at, name, new_ino)) < 0) { +// return res; +// } +// +// // Fill the inode +// ent_inode->flags = 0; +// ent_inode->dir_acl = 0; +// ent_inode->frag_block_addr = 0; +// ent_inode->gen_number = 0; +// ent_inode->hard_link_count = 1; +// ent_inode->acl = 0; +// ent_inode->os_value_1 = 0; +// memset(ent_inode->os_value_2, 0, sizeof(ent_inode->os_value_2)); +// // TODO: time support in kernel +// ent_inode->atime = 0; +// ent_inode->mtime = 0; +// ent_inode->ctime = 0; +// ent_inode->dtime = 0; +// +// memset(ent_inode->direct_blocks, 0, sizeof(ent_inode->direct_blocks)); +// ent_inode->l1_indirect_block = 0; +// ent_inode->l2_indirect_block = 0; +// ent_inode->l3_indirect_block = 0; +// +// ent_inode->uid = ctx->uid; +// ent_inode->gid = ctx->gid; +// // TODO: only regular files can be created this way now +// ent_inode->type_perm = (mode & 0x1FF) | (EXT2_TYPE_REG); +// ent_inode->disk_sector_count = 0; +// ent_inode->size_lower = 0; +// +// // Write the inode +// if ((res = ext2_write_inode(ext2, ent_inode, new_ino)) < 0) { +// return res; +// } +// +// // Create the resulting vnode +// vnode_t *vn = (vnode_t *) kmalloc(sizeof(vnode_t)); +// vn->fs = ext2; +// vn->fs_data = ent_inode; +// vn->fs_number = new_ino; +// vn->op = &ext2_vnode_ops; +// vn->type = VN_REG; +// +// *resvn = vn; +// +// return 0; +//} +// +//static ssize_t ext2_vnode_read(struct ofile *fd, void *buf, size_t count) { +// vnode_t *vn = fd->vnode; +// struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data; +// struct ext2_extsb *sb = vn->fs->fs_private; +// +// size_t nread = MIN(inode->size_lower - fd->pos, count); +// +// if (nread == 0) { +// return -1; +// } +// +// size_t block_number = fd->pos / sb->block_size; +// size_t nblocks = (nread + sb->block_size - 1) / sb->block_size; +// char block_buffer[sb->block_size]; +// +// for (size_t i = 0; i < nblocks; ++i) { +// if (ext2_read_inode_block(vn->fs, inode, i + block_number, block_buffer) < 0) { +// kerror("Failed to read inode %d block #%zu\n", vn->fs_number, i + block_number); +// return -EIO; +// } +// if (i == 0) { +// size_t ncpy = MIN(sb->block_size - fd->pos % sb->block_size, nread); +// memcpy(buf, block_buffer + fd->pos % sb->block_size, ncpy); +// } else { +// size_t ncpy = MIN(sb->block_size, nread - sb->block_size * i); +// memcpy((void *) (((uintptr_t) buf) + sb->block_size * i), block_buffer, ncpy); +// } +// } +// +// return nread; +//} +// +//static ssize_t ext2_vnode_write(struct ofile *fd, const void *buf, size_t count) { +// vnode_t *vn = fd->vnode; +// _assert(vn); +// struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data; +// fs_t *ext2 = vn->fs; +// struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private; +// char block_buffer[sb->block_size]; +// int res; +// +// if (fd->pos > inode->size_lower) { +// // This shouldn't be possible, yeah? +// return -ESPIPE; +// } +// +// // How many bytes can we write into the blocks already allocated +// size_t size_blocks = (inode->size_lower + sb->block_size - 1) / sb->block_size; +// size_t can_write = size_blocks * sb->block_size - inode->size_lower; +// size_t current_block = fd->pos / sb->block_size; +// size_t written = 0; +// size_t remaining = count; +// +// if (can_write) { +// size_t can_write_blocks = (can_write + sb->block_size - 1) / sb->block_size; +// +// for (size_t i = 0; i < can_write_blocks; ++i) { +// size_t block_index = current_block + i; +// size_t pos_in_block = fd->pos % sb->block_size; +// size_t need_write = MIN(remaining, sb->block_size - pos_in_block); +// +// kdebug("Write %zuB to block %zu offset %zu\n", need_write, block_index, pos_in_block); +// if (need_write == sb->block_size) { +// // Can write block without reading it +// // TODO: implement this +// panic("Not implemented\n"); +// } else { +// // Read the block to change its contents +// // and write it back again +// if ((res = ext2_read_inode_block(ext2, inode, block_index, block_buffer)) < 0) { +// break; +// } +// +// memcpy(block_buffer + pos_in_block, (void *) (((uintptr_t) buf) + written), need_write); +// +// if ((res = ext2_write_inode_block(ext2, inode, block_index, block_buffer)) < 0) { +// break; +// } +// } +// +// written += need_write; +// fd->pos += need_write; +// remaining -= need_write; +// } +// +// inode->size_lower = MAX(fd->pos, inode->size_lower); +// current_block += can_write_blocks; +// } +// +// if (remaining) { +// // Need to allocate additional blocks +// size_t need_blocks = (remaining + sb->block_size - 1) / sb->block_size; +// +// for (size_t i = 0; i < need_blocks; ++i) { +// size_t block_index = current_block + i; +// size_t need_write = MIN(remaining, sb->block_size); +// +// // Update the size here so it gets written when the block is allocated +// // and inode struct is flushed +// inode->size_lower += need_write; +// // Allocate a block for the index +// if ((res = ext2_inode_alloc_block(ext2, inode, vn->fs_number, block_index)) < 0) { +// kerror("Could not allocate a block for writing\n"); +// break; +// } +// +// +// if (need_write == sb->block_size) { +// // TODO: implement this +// panic("Not implemented\n"); +// } else { +// // Writing the last block +// memcpy(block_buffer, (void *) (((uintptr_t) buf) + written), need_write); +// +// if ((res = ext2_write_inode_block(ext2, inode, block_index, block_buffer)) < 0) { +// break; +// } +// } +// +// written += need_write; +// fd->pos += need_write; +// remaining -= need_write; +// } +// } else { +// if (written) { +// // Flush inode struct to disk - size has changed +// ext2_write_inode(ext2, inode, vn->fs_number); +// } +// } +// +// return written; +//} +// +//static int ext2_vnode_truncate(struct ofile *fd, size_t length) { +// vnode_t *vn = fd->vnode; +// fs_t *ext2 = vn->fs; +// struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data; +// struct ext2_extsb *sb = vn->fs->fs_private; +// int res; +// +// if (length == inode->size_lower) { +// // Already good +// return 0; +// } +// +// size_t was_blocks = (inode->size_lower + sb->block_size - 1) / sb->block_size; +// size_t now_blocks = (length + sb->block_size - 1) / sb->block_size; +// ssize_t delta_blocks = now_blocks - was_blocks; +// +// if (delta_blocks < 0) { +// // Free truncated blocks +// // XXX: reverse the loop +// for (size_t i = now_blocks; i < was_blocks; ++i) { +// // Modify inode right here because ext2_free_inode_block will +// // flush these changes to disk so we don't have to write it +// // twice +// inode->size_lower -= sb->block_size; +// if ((res = ext2_free_inode_block(ext2, inode, vn->fs_number, i)) < 0) { +// // Put the block size back, couldn't free it +// inode->size_lower += sb->block_size; +// return res; +// } +// } +// +// // All the blocks were successfully freed, can set proper file length +// if (inode->size_lower != length) { +// // If requested size is not block-aligned, we need to write inode +// // struct to disk once again +// inode->size_lower = length; +// +// if ((res = ext2_write_inode(ext2, inode, vn->fs_number)) < 0) { +// return res; +// } +// } +// +// return 0; +// } else { +// kerror("Not implemented: upwards truncation (ext2)\n"); +// return -EINVAL; +// } +//} +// +//// TODO: replace this with getdents +//static int ext2_vnode_readdir(struct ofile *fd) { +// vnode_t *vn = fd->vnode; +// struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data; +// struct ext2_extsb *sb = vn->fs->fs_private; +// ssize_t res; +// +// if (fd->pos >= inode->size_lower) { +// return -1; +// } +// +// size_t block_number = fd->pos / sb->block_size; +// char block_buffer[sb->block_size]; +// +// if ((res = ext2_read_inode_block(vn->fs, inode, block_number, block_buffer)) < 0) { +// return res; +// } +// +// size_t block_offset = fd->pos % sb->block_size; +// struct ext2_dirent *ext2dir = (struct ext2_dirent *) &block_buffer[block_offset]; +// +// if (ext2dir->len == 0) { +// // If entry size is zero, guess we're finished - align the fd->pos up to block size +// fd->pos = (fd->pos + sb->block_size - 1) / sb->block_size; +// return ext2_vnode_readdir(fd); +// } +// +// struct dirent *vfsdir = (struct dirent *) fd->dirent_buf; +// +// vfsdir->d_ino = ext2dir->ino; +// strncpy(vfsdir->d_name, ext2dir->name, ext2dir->name_len); +// vfsdir->d_name[ext2dir->name_len] = 0; +// vfsdir->d_reclen = ext2dir->len; +// if (sb->required_features & 2 /* Directory entries contain type field */) { +// switch (ext2dir->type_ind) { +// case 1: +// // Regular file +// vfsdir->d_type = DT_REG; +// break; +// case 2: +// // Directory +// vfsdir->d_type = DT_DIR; +// break; +// // XXX: Don't know if I should have ANY devices in ext2, we have devfs for that +// // kind of shit +// case 0: +// default: +// vfsdir->d_type = DT_UNKNOWN; +// break; +// } +// } else { +// // Have to read each fucking inode to sort this mess out +// struct ext2_inode ent_inode_buf; +// +// if ((res = ext2_read_inode(vn->fs, &ent_inode_buf, ext2dir->ino)) < 0) { +// return res; +// } +// +// switch (ent_inode_buf.type_perm & 0xF000) { +// case EXT2_TYPE_REG: +// vfsdir->d_type = DT_REG; +// break; +// case EXT2_TYPE_DIR: +// vfsdir->d_type = DT_DIR; +// break; +// default: +// vfsdir->d_type = DT_UNKNOWN; +// break; +// } +// } +// // Not implemented, I guess +// vfsdir->d_off = 0; +// +// fd->pos += ext2dir->len; +// +// return 0; +//} +// +//static void ext2_vnode_destroy(vnode_t *vn) { +// // Release inode struct +// kfree(vn->fs_data); +//} +// +//static int ext2_vnode_stat(vnode_t *vn, struct stat *st) { +// _assert(vn && vn->fs); +// struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data; +// _assert(inode); +// struct ext2_extsb *sb = (struct ext2_extsb *) vn->fs->fs_private; +// _assert(sb); +// +// st->st_atime = inode->atime; +// st->st_ctime = inode->ctime; +// st->st_mtime = inode->mtime; +// st->st_dev = 0; // Not implemented +// st->st_rdev = 0; // Not implemented +// st->st_gid = inode->gid; +// st->st_uid = inode->uid; +// st->st_mode = inode->type_perm; +// st->st_size = inode->size_lower; +// st->st_blocks = (inode->size_lower + sb->block_size - 1) / sb->block_size; +// st->st_blksize = sb->block_size; +// st->st_nlink = 0; +// st->st_ino = vn->fs_number; +// +// return 0; +//} +// +//static int ext2_vnode_chmod(vnode_t *vn, mode_t mode) { +// _assert(vn && vn->fs && vn->fs_data); +// struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data; +// +// // Update only access mode +// inode->type_perm &= ~0x1FF; +// inode->type_perm |= mode & 0x1FF; +// +// // Write the inode back +// return ext2_write_inode(vn->fs, inode, vn->fs_number); +//} +// +//static int ext2_vnode_chown(vnode_t *vn, uid_t uid, gid_t gid) { +// _assert(vn && vn->fs && vn->fs_data); +// struct ext2_inode *inode = (struct ext2_inode *) vn->fs_data; +// +// inode->gid = gid; +// inode->uid = uid; +// +// // Write the inode back +// return ext2_write_inode(vn->fs, inode, vn->fs_number); +//} +// +//static int ext2_vnode_unlink(vnode_t *at, vnode_t *vn, const char *name) { +// struct ext2_inode *inode = vn->fs_data; +// struct ext2_inode *at_inode = at->fs_data; +// fs_t *ext2 = vn->fs; +// struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private; +// uint32_t ino = vn->fs_number; +// int res; +// +// if (vn->type == VN_DIR) { +// // Check if the directory we're unlinking has any entries besides +// // . and .. +// // Can tell this just by looking at the first block +// if (inode->size_lower > sb->block_size) { +// // Directory size is more than one block - totally +// // has something inside +// return -EISDIR; +// } +// char block_buffer[sb->block_size]; +// size_t off = 0; +// +// if ((res = ext2_read_inode_block(ext2, inode, 0, block_buffer)) < 0) { +// return res; +// } +// +// while (off < sb->block_size) { +// struct ext2_dirent *ent = (struct ext2_dirent *) &block_buffer[off]; +// if (!ent->ino) { +// break; +// } +// if (ent->name_len == 1 && ent->name[0] == '.') { +// off += ent->len; +// continue; +// } +// if (ent->name_len == 2 && ent->name[1] == '.' && ent->name[0] == '.') { +// off += ent->len; +// continue; +// } +// +// return -EISDIR; +// } +// } +// +// // Free blocks used by the inode - truncate the file to zero +// size_t nblocks = (inode->size_lower + sb->block_size - 1) / sb->block_size; +// +// inode->size_lower = nblocks * sb->block_size; +// for (ssize_t i = nblocks - 1; i >= 0; --i) { +// inode->size_lower -= sb->block_size; +// if ((res = ext2_free_inode_block(ext2, inode, ino, i)) < 0) { +// return res; +// } +// } +// +// // inode->size_lower is now 0 +// _assert(inode->size_lower == 0); +// +// // Free the inode itself +// if ((res = ext2_free_inode(ext2, ino)) < 0) { +// return res; +// } +// +// // Now remove the entry from directory +// if ((res = ext2_dir_remove_inode(ext2, at, name, ino)) < 0) { +// return res; +// } +// +// return 0; +//} +// +//static int ext2_vnode_access(vnode_t *vn, uid_t *uid, gid_t *gid, mode_t *mode) { +// _assert(vn && vn->fs_data); +// struct ext2_inode *inode = vn->fs_data; +// +// *uid = inode->uid; +// *gid = inode->gid; +// *mode = inode->type_perm & 0x1FF; +// +// return 0; +//} +// +static int ext2_vnode_readlink(struct vnode *vn, char *dst, size_t lim) { _assert(vn && vn->fs_data); struct ext2_inode *inode = vn->fs_data; - - *uid = inode->uid; - *gid = inode->gid; - *mode = inode->type_perm & 0x1FF; - - return 0; -} - -static int ext2_vnode_readlink(vnode_t *vn, char *dst) { - _assert(vn && vn->fs_data); - struct ext2_inode *inode = vn->fs_data; - fs_t *ext2 = vn->fs; + struct fs *ext2 = vn->fs; struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private; + if (lim <= inode->size_lower) { + return -1; + } + if (inode->size_lower >= 60) { char block_buffer[sb->block_size]; int res; @@ -703,85 +705,85 @@ static int ext2_vnode_readlink(vnode_t *vn, char *dst) { return 0; } - -static int ext2_vnode_symlink(vnode_t *at, struct vfs_ioctx *ctx, const char *name, const char *dst) { - _assert(at && at->fs && at->fs_data); - struct ext2_inode *inode = at->fs_data; - fs_t *ext2 = at->fs; - struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private; - - uint32_t new_ino; - int res; - - // Allocate new inode number - if ((res = ext2_alloc_inode(ext2, &new_ino)) != 0) { - kerror("Failed to allocate inode\n"); - return res; - } - - kdebug("Allocated inode %d\n", new_ino); - - // Create an inode struct in memory - struct ext2_inode *ent_inode = (struct ext2_inode *) kmalloc(sb->inode_struct_size); - - // Now create an entry in parents dirent list - if ((res = ext2_dir_add_inode(ext2, at, name, new_ino)) < 0) { - return res; - } - - // Fill the inode - ent_inode->flags = 0; - ent_inode->dir_acl = 0; - ent_inode->frag_block_addr = 0; - ent_inode->gen_number = 0; - ent_inode->hard_link_count = 1; - ent_inode->acl = 0; - ent_inode->os_value_1 = 0; - memset(ent_inode->os_value_2, 0, sizeof(ent_inode->os_value_2)); - // TODO: time support in kernel - ent_inode->atime = 0; - ent_inode->mtime = 0; - ent_inode->ctime = 0; - ent_inode->dtime = 0; - - ent_inode->size_lower = strlen(dst); - - if (ent_inode->size_lower <= 60) { - char *dst_str = (char *) ent_inode->direct_blocks; - memset(dst_str, 0, 60); - - strncpy(dst_str, dst, ent_inode->size_lower); - } else { - char block_buffer[sb->block_size]; - uint32_t block_no; - - if ((res = ext2_alloc_block(ext2, &block_no)) < 0) { - return res; - } - - memset(block_buffer, 0, sb->block_size); - strncpy(block_buffer, dst, sb->block_size); - - if ((res = ext2_write_block(ext2, block_no, block_buffer)) < 0) { - return res; - } - - memset(ent_inode->direct_blocks, 0, sizeof(ent_inode->direct_blocks)); - ent_inode->l1_indirect_block = 0; - ent_inode->l2_indirect_block = 0; - ent_inode->l3_indirect_block = 0; - - ent_inode->direct_blocks[0] = block_no; - } - - ent_inode->uid = ctx->uid; - ent_inode->gid = ctx->gid; - ent_inode->type_perm = 0777 | EXT2_TYPE_LNK; - - // Write the inode - if ((res = ext2_write_inode(ext2, ent_inode, new_ino)) < 0) { - return res; - } - - return 0; -} +// +//static int ext2_vnode_symlink(vnode_t *at, struct vfs_ioctx *ctx, const char *name, const char *dst) { +// _assert(at && at->fs && at->fs_data); +// struct ext2_inode *inode = at->fs_data; +// fs_t *ext2 = at->fs; +// struct ext2_extsb *sb = (struct ext2_extsb *) ext2->fs_private; +// +// uint32_t new_ino; +// int res; +// +// // Allocate new inode number +// if ((res = ext2_alloc_inode(ext2, &new_ino)) != 0) { +// kerror("Failed to allocate inode\n"); +// return res; +// } +// +// kdebug("Allocated inode %d\n", new_ino); +// +// // Create an inode struct in memory +// struct ext2_inode *ent_inode = (struct ext2_inode *) kmalloc(sb->inode_struct_size); +// +// // Now create an entry in parents dirent list +// if ((res = ext2_dir_add_inode(ext2, at, name, new_ino)) < 0) { +// return res; +// } +// +// // Fill the inode +// ent_inode->flags = 0; +// ent_inode->dir_acl = 0; +// ent_inode->frag_block_addr = 0; +// ent_inode->gen_number = 0; +// ent_inode->hard_link_count = 1; +// ent_inode->acl = 0; +// ent_inode->os_value_1 = 0; +// memset(ent_inode->os_value_2, 0, sizeof(ent_inode->os_value_2)); +// // TODO: time support in kernel +// ent_inode->atime = 0; +// ent_inode->mtime = 0; +// ent_inode->ctime = 0; +// ent_inode->dtime = 0; +// +// ent_inode->size_lower = strlen(dst); +// +// if (ent_inode->size_lower <= 60) { +// char *dst_str = (char *) ent_inode->direct_blocks; +// memset(dst_str, 0, 60); +// +// strncpy(dst_str, dst, ent_inode->size_lower); +// } else { +// char block_buffer[sb->block_size]; +// uint32_t block_no; +// +// if ((res = ext2_alloc_block(ext2, &block_no)) < 0) { +// return res; +// } +// +// memset(block_buffer, 0, sb->block_size); +// strncpy(block_buffer, dst, sb->block_size); +// +// if ((res = ext2_write_block(ext2, block_no, block_buffer)) < 0) { +// return res; +// } +// +// memset(ent_inode->direct_blocks, 0, sizeof(ent_inode->direct_blocks)); +// ent_inode->l1_indirect_block = 0; +// ent_inode->l2_indirect_block = 0; +// ent_inode->l3_indirect_block = 0; +// +// ent_inode->direct_blocks[0] = block_no; +// } +// +// ent_inode->uid = ctx->uid; +// ent_inode->gid = ctx->gid; +// ent_inode->type_perm = 0777 | EXT2_TYPE_LNK; +// +// // Write the inode +// if ((res = ext2_write_inode(ext2, ent_inode, new_ino)) < 0) { +// return res; +// } +// +// return 0; +//} diff --git a/sys/vfs/node.c b/sys/vfs/node.c index 88b6b04..5fe5137 100644 --- a/sys/vfs/node.c +++ b/sys/vfs/node.c @@ -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); - } -} diff --git a/sys/vfs/tar.c b/sys/vfs/tar.c index f5ccdb0..176cf1b 100644 --- a/sys/vfs/tar.c +++ b/sys/vfs/tar.c @@ -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; diff --git a/sys/vfs/vfs.c b/sys/vfs/vfs.c index dbf520b..091b727 100644 --- a/sys/vfs/vfs.c +++ b/sys/vfs/vfs.c @@ -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; }