From cfd0267927f05d9b0828bade41eef84cdf548278 Mon Sep 17 00:00:00 2001 From: Mark Date: Mon, 13 Jan 2020 15:28:14 +0200 Subject: [PATCH] Add opendir/readdir --- include/sys/fs/node.h | 2 + include/sys/fs/ofile.h | 3 + include/sys/fs/vfs.h | 2 + sys/amd64/sys_file.c | 19 ++++- sys/init.c | 14 ++-- sys/vfs/ext2/ext2vnop.c | 182 ++++++++++++++++++++-------------------- sys/vfs/vfs_ops.c | 170 ++++++++++++++++++++++++++++++++++--- 7 files changed, 282 insertions(+), 110 deletions(-) diff --git a/include/sys/fs/node.h b/include/sys/fs/node.h index d3c9872..321f4c0 100644 --- a/include/sys/fs/node.h +++ b/include/sys/fs/node.h @@ -32,6 +32,8 @@ enum vnode_type { struct vnode_operations { int (*find) (struct vnode *at, const char *name, struct vnode **node); + ssize_t (*readdir) (struct ofile *fd, struct dirent *ent); + int (*opendir) (struct ofile *fd); int (*open) (struct ofile *fd, int opt); void (*close) (struct ofile *fd); int (*creat) (struct vnode *at, const char *filename, uid_t uid, gid_t gid, mode_t mode); diff --git a/include/sys/fs/ofile.h b/include/sys/fs/ofile.h index 586b525..cd0b634 100644 --- a/include/sys/fs/ofile.h +++ b/include/sys/fs/ofile.h @@ -5,6 +5,9 @@ #define OF_WRITABLE (1 << 0) #define OF_READABLE (1 << 1) #define OF_DIRECTORY (1 << 2) +#define OF_MEMDIR (1 << 3) +#define OF_MEMDIR_DOT (1 << 4) +#define OF_MEMDIR_DOTDOT (1 << 5) struct ofile { int flags; diff --git a/include/sys/fs/vfs.h b/include/sys/fs/vfs.h index 495d3bb..146361e 100644 --- a/include/sys/fs/vfs.h +++ b/include/sys/fs/vfs.h @@ -31,7 +31,9 @@ int vfs_mount(struct vfs_ioctx *ctx, const char *at, void *blk, const char *fs, int vfs_open_vnode(struct vfs_ioctx *ctx, struct ofile *fd, struct vnode *node, int opt); int vfs_open(struct vfs_ioctx *ctx, struct ofile *fd, const char *path, int flags, int mode); void vfs_close(struct vfs_ioctx *ctx, struct ofile *fd); +int vfs_readdir(struct vfs_ioctx *ctx, struct ofile *fd, struct dirent *ent); +int vfs_access(struct vfs_ioctx *ctx, const char *path, int accmode); int vfs_stat(struct vfs_ioctx *ctx, const char *path, struct stat *st); int vfs_access_check(struct vfs_ioctx *ctx, int desm, mode_t mode, uid_t uid, gid_t gid); int vfs_access_node(struct vfs_ioctx *ctx, struct vnode *vn, int mode); diff --git a/sys/amd64/sys_file.c b/sys/amd64/sys_file.c index e3fd43e..ad9e7fa 100644 --- a/sys/amd64/sys_file.c +++ b/sys/amd64/sys_file.c @@ -41,7 +41,18 @@ ssize_t sys_write(int fd, const void *buf, size_t lim) { } ssize_t sys_readdir(int fd, struct dirent *ent) { - return -EINVAL; + struct thread *thr = get_cpu()->thread; + _assert(thr); + + if (fd < 0 || fd >= THREAD_MAX_FDS) { + return -EBADF; + } + + if (thr->fds[fd] == NULL) { + return -EBADF; + } + + return vfs_readdir(&thr->ioctx, thr->fds[fd], ent); } int sys_creat(const char *pathname, int mode) { @@ -125,7 +136,11 @@ int sys_stat(const char *filename, struct stat *st) { } int sys_access(const char *path, int mode) { - return -EINVAL; + struct thread *thr = get_cpu()->thread; + _assert(thr); + _assert(path); + + return vfs_access(&thr->ioctx, path, mode); } int sys_openpty(int *master, int *slave) { diff --git a/sys/init.c b/sys/init.c index 397c540..89a680f 100644 --- a/sys/init.c +++ b/sys/init.c @@ -123,13 +123,13 @@ void init_func(void *arg) { panic("Failed to mount root device: %s\n", kstrerror(res)); } - // Mount something else - if ((res = dev_find(DEV_CLASS_BLOCK, "sda1", &root_dev)) != 0) { - panic("sda1: %s\n", kstrerror(res)); - } - if ((res = vfs_mount(kernel_ioctx, "/mnt", root_dev->dev, "ext2", NULL)) != 0) { - panic("Failed to mount root device: %s\n", kstrerror(res)); - } + //// Mount something else + //if ((res = dev_find(DEV_CLASS_BLOCK, "sda1", &root_dev)) != 0) { + // panic("sda1: %s\n", kstrerror(res)); + //} + //if ((res = vfs_mount(kernel_ioctx, "/mnt", root_dev->dev, "ext2", NULL)) != 0) { + // panic("Failed to mount root device: %s\n", kstrerror(res)); + //} // Start user init binary user_init_start(); diff --git a/sys/vfs/ext2/ext2vnop.c b/sys/vfs/ext2/ext2vnop.c index 973b35e..1fae067 100644 --- a/sys/vfs/ext2/ext2vnop.c +++ b/sys/vfs/ext2/ext2vnop.c @@ -23,11 +23,11 @@ static int ext2_vnode_find(struct vnode *vn, const char *name, struct vnode **re static int ext2_vnode_creat(struct vnode *at, const char *name, uid_t uid, gid_t gid, mode_t mode); //static int ext2_vnode_mkdir(vnode_t *at, const char *name, mode_t mode); static int ext2_vnode_open(struct ofile *fd, int opt); -//static int ext2_vnode_opendir(vnode_t *vn, int opt); +static int ext2_vnode_opendir(struct ofile *fd); 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 vnode *vn, size_t length); -//static int ext2_vnode_readdir(struct ofile *fd); +static ssize_t ext2_vnode_readdir(struct ofile *fd, struct dirent *ent); //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); @@ -52,9 +52,9 @@ struct vnode_operations ext2_vnode_ops = { // .unlink = ext2_vnode_unlink, // .access = ext2_vnode_access, // -// .opendir = ext2_vnode_opendir, -// .readdir = ext2_vnode_readdir, -// + .opendir = ext2_vnode_opendir, + .readdir = ext2_vnode_readdir, + .open = ext2_vnode_open, .read = ext2_vnode_read, .write = ext2_vnode_write, @@ -117,18 +117,24 @@ static int ext2_vnode_find(struct vnode *vn, const char *name, struct vnode **re return -ENOENT; } -// -//static int ext2_vnode_opendir(vnode_t *vn, int opt) { -// _assert(vn->type == VN_DIR); -// return 0; -//} -// + +static int ext2_vnode_opendir(struct ofile *fd) { + _assert(fd); + _assert(fd->vnode); + _assert(fd->vnode->type == VN_DIR); + + fd->pos = 0; + + return 0; +} + static int ext2_vnode_open(struct ofile *fd, int opt) { _assert(fd); _assert(fd->vnode); _assert(fd->vnode->type == VN_REG); // TODO: O_APPEND should place fd->pos at the end of the file + fd->pos = 0; return 0; } @@ -464,84 +470,82 @@ static int ext2_vnode_truncate(struct vnode *vn, size_t length) { } } -//// 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; -//} -// +// TODO: replace this with getdents +static ssize_t ext2_vnode_readdir(struct ofile *fd, struct dirent *vfsdir) { + struct vnode *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, vfsdir); + } + + 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 ext2dir->len; +} + //static void ext2_vnode_destroy(vnode_t *vn) { // // Release inode struct // kfree(vn->fs_data); diff --git a/sys/vfs/vfs_ops.c b/sys/vfs/vfs_ops.c index b2e6b82..885c93f 100644 --- a/sys/vfs/vfs_ops.c +++ b/sys/vfs/vfs_ops.c @@ -54,6 +54,115 @@ static const char *vfs_path_parent(const char *input, char *dst) { } } +static int vfs_opendir(struct vfs_ioctx *ctx, struct ofile *fd) { + _assert(ctx); + _assert(fd); + + struct vnode *node = fd->vnode; + + _assert(node); + + if (node->type != VN_DIR) { + return -ENOTDIR; + } + + if (node->flags & VN_MEMORY) { + // Filesystem doesn't have to implement opendir/readdir for these - they're all + // in memory and can be easily iterated + fd->flags |= OF_MEMDIR | OF_MEMDIR_DOT; + fd->pos = (size_t) node->first_child; + + return 0; + } else { + if (!node->op || !node->op->opendir) { + return -EINVAL; + } + + return node->op->opendir(fd); + } +} + +int vfs_readdir(struct vfs_ioctx *ctx, struct ofile *fd, struct dirent *ent) { + _assert(ctx); + _assert(fd); + + if (!(fd->flags & OF_DIRECTORY)) { + return -ENOTDIR; + } + + if (fd->flags & OF_MEMDIR) { + // Kind of hacky but works + if (fd->flags & OF_MEMDIR_DOT) { + struct vnode *node = fd->vnode; + _assert(node); + + fd->flags &= ~OF_MEMDIR_DOT; + fd->flags |= OF_MEMDIR_DOTDOT; + + ent->d_ino = node->ino; + ent->d_type = DT_DIR; + ent->d_off = 0; + ent->d_name[0] = '.'; + ent->d_name[1] = 0; + ent->d_reclen = sizeof(struct dirent) + 1; + + return ent->d_reclen; + } else if (fd->flags & OF_MEMDIR_DOTDOT) { + struct vnode *node = fd->vnode; + _assert(node); + if (node->parent) { + node = node->parent; + } + + fd->flags &= ~OF_MEMDIR_DOTDOT; + + ent->d_ino = node->ino; + ent->d_type = DT_DIR; + ent->d_off = 0; + ent->d_name[0] = '.'; + ent->d_name[1] = '.'; + ent->d_name[2] = 0; + ent->d_reclen = sizeof(struct dirent) + 2; + + return ent->d_reclen; + } + + struct vnode *item = (struct vnode *) fd->pos; + if (!item) { + return -1; + } + + // Fill dirent + ent->d_ino = item->ino; + ent->d_off = 0; + + switch (item->type) { + case VN_REG: + ent->d_type = DT_REG; + break; + case VN_DIR: + case VN_MNT: + ent->d_type = DT_DIR; + break; + default: + ent->d_type = DT_UNKNOWN; + break; + } + + strcpy(ent->d_name, item->name); + ent->d_reclen = sizeof(struct dirent) + strlen(item->name); + + fd->pos = (size_t) item->next_child; + + return ent->d_reclen; + } else { + struct vnode *node = fd->vnode; + _assert(node); + _assert(node->op && node->op->readdir); + return node->op->readdir(fd, ent); + } +} + int vfs_open_vnode(struct vfs_ioctx *ctx, struct ofile *fd, struct vnode *node, int opt) { int res; int accmode; @@ -63,7 +172,21 @@ int vfs_open_vnode(struct vfs_ioctx *ctx, struct ofile *fd, struct vnode *node, _assert(node); if (opt & O_DIRECTORY) { - panic("NYI: O_DIRECTORY\n"); + if ((opt & O_ACCMODE) != O_RDONLY) { + return -EACCES; + } + + // fd->refcount = 0; + fd->pos = 0; + fd->vnode = node; + fd->flags = OF_DIRECTORY | OF_READABLE; + + if ((res = vfs_opendir(ctx, fd)) != 0) { + return res; + } + + // ++fd->refcount; + return 0; } // Check node access @@ -73,6 +196,17 @@ int vfs_open_vnode(struct vfs_ioctx *ctx, struct ofile *fd, struct vnode *node, return res; } + // Truncate the file if requested + if (opt & O_TRUNC) { + if (!node->op || !node->op->truncate) { + return -EINVAL; + } + + if ((res = node->op->truncate(node, 0)) != 0) { + return res; + } + } + //fd->refcount = 0; fd->pos = 0; fd->vnode = node; @@ -90,17 +224,6 @@ int vfs_open_vnode(struct vfs_ioctx *ctx, struct ofile *fd, struct vnode *node, break; } - // Truncate the file if requested - if (opt & O_TRUNC) { - if (!node->op || !node->op->truncate) { - return -EINVAL; - } - - if ((res = node->op->truncate(node, 0)) != 0) { - return res; - } - } - // 1. If file operations struct specifies some non-trivial open() // function for the node - use it if (node->op && node->op->open) { @@ -152,6 +275,10 @@ int vfs_open(struct vfs_ioctx *ctx, struct ofile *fd, const char *path, int opt, if ((res = vfs_find(ctx, ctx->cwd_vnode, path, &node)) != 0) { if (opt & O_CREAT) { + if (opt & O_DIRECTORY) { + return -EINVAL; + } + // Try to create a new file if ((res = vfs_creat(ctx, path, mode)) != 0) { return res; @@ -179,6 +306,25 @@ void vfs_close(struct vfs_ioctx *ctx, struct ofile *fd) { } } +int vfs_access(struct vfs_ioctx *ctx, const char *path, int accmode) { + struct vnode *node; + int res; + + _assert(ctx); + _assert(path); + + if ((res = vfs_find(ctx, ctx->cwd_vnode, path, &node)) != 0) { + return res; + } + + if (accmode == F_OK) { + // Just test that file exists + return 0; + } + + return vfs_access_node(ctx, node, accmode); +} + int vfs_stat(struct vfs_ioctx *ctx, const char *path, struct stat *st) { struct vnode *node; int res;