Add opendir/readdir
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user