// // Created by rick on 12-08-21. // #include #include #include #include #include #include #include #include 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 = ¤t_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 = ¤t_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, };