Files
my-kern/kernel/vfs/vfs.c
Rick Rongen 03f0ec6f88 feat: Vfs and Ext2 support. Code style/attribute improvements
Added VFS and Ext2 support.
Optimized attributes of methods to improve code highlighting.
Printf attribute, malloc attribute, etc.
2021-10-06 21:45:15 +02:00

187 lines
5.8 KiB
C

//
// Created by rick on 12-08-21.
//
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <myke/tasks/locking.h>
#include <myke/vfs/vfs.h>
#include <myke/vfs/vfs-driver.h>
#include <myke/util/init.h>
#include <myke/libk/libk.h>
vfs_mount_t *first_mount = NULL;
mutex_t *mount_lock = NULL;
extern vfs_driver_t __start_vfs_driver[];
extern vfs_driver_t __stop_vfs_driver[];
#define NUM_DRIVERS ((size_t)(__stop_vfs_driver - __start_vfs_driver))
#define DRIVER(i) ((__start_vfs_driver) + (i))
vfs_driver_t *vfs_get_driver_by_name(const char *name) {
for (size_t i = 0; i < NUM_DRIVERS; ++i) {
if (strcmp(DRIVER(i)->name, name) == 0) {
return DRIVER(i);
}
}
return NULL;
}
void vfs_register_mount(vfs_mount_t *mount) {
mutex_acquire(mount_lock);
if (first_mount == NULL) {
first_mount = mount;
} else {
vfs_mount_t *last_mount = first_mount;
while (last_mount->next != NULL) last_mount = last_mount->next;
last_mount->next = mount;
}
mutex_release(mount_lock);
}
vfs_mount_t *vfs_get_mount_for_path(const char *path) {
if (first_mount == NULL) {
return NULL;
}
mutex_acquire(mount_lock);
size_t max_length = strlen(path);
vfs_mount_t *longest_match = NULL;
size_t match_length = 0;
vfs_mount_t *last_mount = NULL;
do {
if (last_mount == NULL) {
last_mount = first_mount;
} else if ((last_mount = last_mount->next) == NULL) {
break;
}
size_t mount_length = strlen(last_mount->prefix);
if (mount_length > max_length || mount_length < match_length) {
continue;
}
if (strncmp(last_mount->prefix, path, strlen(last_mount->prefix)) == 0) {
longest_match = last_mount;
match_length = mount_length;
}
} while (true);
mutex_release(mount_lock);
return longest_match;
}
int vfs_mount(const char *path, const char *device, const char *driver) {
vfs_driver_t *driver_impl = vfs_get_driver_by_name(driver);
if (driver_impl == NULL) {
return VFS_MOUNT_ERROR;
}
vfs_mount_t *mount = malloc(sizeof(vfs_mount_t));
mount->driver = driver_impl;
mount->device = device;
mount->prefix = path;
int result = driver_impl->vfs_mount(mount);
if (result != VFS_MOUNT_OK) {
free(mount);
return result;
}
vfs_register_mount(mount);
return VFS_MOUNT_OK;
}
vfs_fd_t *vfs_open_intermediate(vfs_mount_t *mount, const char *path, int flags, int mode) {
vfs_fd_t *intermediate_dir = malloc(sizeof(vfs_fd_t));
vfs_fd_t *out = malloc(sizeof(vfs_fd_t));
out->mount = mount;
intermediate_dir->mount = mount;
char *full_path = strdup(path);
char *current_path = full_path;
while (strlen(current_path) > 0) {
if (current_path[0] == '/') {
// root
if (((vfs_driver_t *) mount->driver)->open(mount, NULL, NULL, flags, out) != VFS_OPEN_OK) {
free(out);
free(intermediate_dir);
free(full_path);
return NULL;
}
current_path = &current_path[1];
} else {
char *next_dir = strchr(current_path, '/');
if (next_dir != NULL) {
next_dir[0] = 0;
}
if (((vfs_driver_t *) mount->driver)->open(mount, intermediate_dir, current_path, flags, out) !=
VFS_OPEN_OK) {
((vfs_driver_t *) mount->driver)->close(mount, intermediate_dir);
free(out);
free(intermediate_dir);
free(full_path);
return NULL;
}
if (next_dir == NULL) {
current_path = &current_path[strlen(current_path)];
} else {
current_path = &next_dir[1];
}
if (((vfs_driver_t *) mount->driver)->close(mount, intermediate_dir) != VFS_CLOSE_OK) {
free(out);
free(intermediate_dir);
free(full_path);
return NULL;
}
}
memcpy(intermediate_dir, out, sizeof(vfs_fd_t));
}
free(full_path);
free(intermediate_dir);
return out;
}
// https://man7.org/linux/man-pages/man2/open.2.html
vfs_fd_t *vfs_open(const char *path, int flags, int mode) {
vfs_mount_t *mount = vfs_get_mount_for_path(path);
if (mount == NULL) {
return NULL;
}
return vfs_open_intermediate(mount, &path[strlen(mount->prefix) - 1], flags, mode);
}
void vfs_close(vfs_fd_t *fd) {
((vfs_driver_t *) (fd->mount->driver))->close(fd->mount, fd);
free(fd);
}
// https://man7.org/linux/man-pages/man2/stat.2.html
int vfs_fstat(vfs_fd_t *dirfd, stat_t *target, int flags) {
return ((vfs_driver_t *) (dirfd->mount->driver))->fstat(dirfd->mount, dirfd, target, flags);
}
// https://man7.org/linux/man-pages/man2/read.2.html
int vfs_read(vfs_fd_t *fd, void *target, size_t size) {
return ((vfs_driver_t *) (fd->mount->driver))->fread(fd->mount, fd, target, size);
}
// inspiration https://man7.org/linux/man-pages/man2/getdents.2.html
int vfs_getdents(vfs_fd_t *fd, void *target, int count) {
return ((vfs_driver_t *) fd->mount->driver)->dgetent(fd->mount, fd, target, count);
}
void vfs_mk_dirent_record(vfs_dirent_t *ent, uint32_t inode, uint32_t cur_offset, uint8_t type, char *name,
size_t name_length) {
ent->inode = inode;
strncpy(ent->name, name, name_length);
ent->name[name_length] = 0;
ent->type = type;
ent->reclen = VFS_DIRENT_SIZE(name_length);
ent->offset_next = cur_offset + ent->reclen;
}
void vfs_init() {
mount_lock = mutex_create();
}
INIT_FUNCTION(100) = {
.name = "vfs-task",
.stage = INIT_STAGE_PRE_TASKING,
.init = vfs_init,
};