Add opendir/readdir

This commit is contained in:
Mark
2020-01-13 15:28:14 +02:00
parent 63bd9a2b35
commit cfd0267927
7 changed files with 282 additions and 110 deletions
+2
View File
@@ -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);
+3
View File
@@ -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;
+2
View File
@@ -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);
+17 -2
View File
@@ -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) {
+7 -7
View File
@@ -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();
+93 -89
View File
@@ -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);
+158 -12
View File
@@ -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;