From 6c61ed6baee88ee27031afb35c589b25dd561574 Mon Sep 17 00:00:00 2001 From: Mathieu Serandour Date: Fri, 25 Feb 2022 18:04:11 +0100 Subject: [PATCH] VFS work in progress --- kernel/fs/vfs.c | 1001 ++++++++++++++++++++++++++++------------------- kernel/fs/vfs.h | 14 +- 2 files changed, 620 insertions(+), 395 deletions(-) diff --git a/kernel/fs/vfs.c b/kernel/fs/vfs.c index 00c8ad0..3cd0637 100644 --- a/kernel/fs/vfs.c +++ b/kernel/fs/vfs.c @@ -6,43 +6,43 @@ #include "../lib/dump.h" #include "../lib/sprintf.h" #include "../lib/panic.h" +#include "../lib/math.h" #include "../acpi/power.h" #include "../memory/heap.h" #include "fat32/fat32.h" -typedef struct vdir { - // vdir children +typedef struct vdir +{ + // vdir children // null if no children - // or if this is a fs + // or if this is a fs // root - struct vdir* children; + struct vdir *children; unsigned n_children; - - // null if this is not a + // null if this is not a // fs root - fs_t* fs; + fs_t *fs; - char* path; + char *path; } vdir_t; - static vdir_t vfs_root; // list of every cached dir // force it to take a bounded // amount of memory -static dir_cache_ent_t* cached_dir_list; +static dir_cache_ent_t *cached_dir_list; // cache size must be a power of two static unsigned cache_size = 0; -// dynamic array of every mounted +// dynamic array of every mounted // file system -//static fs_t** mounted_fs_list = NULL; -//static unsigned n_mounted_fs = 0; +// static fs_t** mounted_fs_list = NULL; +// static unsigned n_mounted_fs = 0; static int is_absolute(const char *path) { @@ -52,19 +52,19 @@ static int is_absolute(const char *path) /** * @brief replaces /////// with / * replaces /a/b/.. with /a - * + * * the output path does not contain a final '/' * and no '.' or '..' - * - * @param dst - * @param src + * + * @param dst + * @param src */ -static void simplify_path(char* dst, const char* src) { - - char *buf = malloc(strlen(src)+1); - +static void simplify_path(char *dst, const char *src) +{ + + char *buf = malloc(strlen(src) + 1); - *dst = 0; + *dst = 0; strcpy(buf, src); @@ -76,15 +76,17 @@ static void simplify_path(char* dst, const char* src) { // "/foo/bar/foobar" while (sub != NULL) { - if(!strcmp(sub, ".")) + if (!strcmp(sub, ".")) ; - else if(!strcmp(sub, "..")) { - char* ptr = (char*)strrchr(dst, '/'); + else if (!strcmp(sub, "..")) + { + char *ptr = (char *)strrchr(dst, '/'); - if(ptr != NULL) + if (ptr != NULL) *ptr = '\0'; } - else { + else + { strcat(dst, "/"); strcat(dst, sub); } @@ -94,7 +96,8 @@ static void simplify_path(char* dst, const char* src) { free(buf); - if(dst[0] == '\0') { + if (dst[0] == '\0') + { *dst++ = '/'; *dst = '\0'; } @@ -103,34 +106,33 @@ static void simplify_path(char* dst, const char* src) { /** * hash 0 -> 8191 */ -uint16_t path_hash(const char* path) { +uint16_t path_hash(const char *path) +{ unsigned len = strlen(path); uint16_t res = 0; - const uint8_t* dwp = (const uint8_t*)path; - + const uint8_t *dwp = (const uint8_t *)path; - for(unsigned i = 0; i < len; i += 1) + for (unsigned i = 0; i < len; i += 1) { - uint32_t entropy_bits = dwp[2*i] ^ (dwp[2*i+1] << 4); + uint32_t entropy_bits = dwp[2 * i] ^ (dwp[2 * i + 1] << 4); entropy_bits ^= (entropy_bits << 4); - res = (res ^ (entropy_bits+i)) & 8191; + res = (res ^ (entropy_bits + i)) & 8191; } return res; } - // recursively destruct the entire // vdir tree -static -void free_vtree(vdir_t* root) { - for(unsigned i = 0; i < root->n_children; i++) { - vdir_t* child = &root->children[i]; - +static void free_vtree(vdir_t *root) +{ + for (unsigned i = 0; i < root->n_children; i++) + { + vdir_t *child = &root->children[i]; - // unmount mounted entries - if(child->fs) + // unmount mounted entries + if (child->fs) child->fs->unmount(child->fs); else free_vtree(child); @@ -141,53 +143,51 @@ void free_vtree(vdir_t* root) { root->n_children = 0; } - // return the vdir associated with given fs // or null if it is not found -static -vdir_t* search_fs(vdir_t* root, fs_t* fs) { - if(root->fs == fs) +static vdir_t *search_fs(vdir_t *root, fs_t *fs) +{ + if (root->fs == fs) return root; - for(unsigned i = 0; i < root->n_children; i++) { - vdir_t* child = &root->children[i]; + for (unsigned i = 0; i < root->n_children; i++) + { + vdir_t *child = &root->children[i]; - vdir_t* sub_result = search_fs(child, fs); - if(sub_result) + vdir_t *sub_result = search_fs(child, fs); + if (sub_result) return sub_result; - } return NULL; } - // remove the vdir // return wether or not the vdir has // been removed // return 0 iif something went wrong -static -int remove_vdir(vdir_t* vdir) { +static int remove_vdir(vdir_t *vdir) +{ // cannot remove the root - if(vdir == &vfs_root) + if (vdir == &vfs_root) return 0; - vdir_t* vd = &vfs_root; - - while(1) { - vdir_t* child = NULL; + vdir_t *vd = &vfs_root; + while (1) + { + vdir_t *child = NULL; - for(unsigned i = 0; i < vd->n_children; i++) { - const char* p = vd->children[i].path; + for (unsigned i = 0; i < vd->n_children; i++) + { + const char *p = vd->children[i].path; - - if(&vd->children[i] == vdir) { + if (&vd->children[i] == vdir) + { // actually remove it memmove( vd->children + i, - vd->children + i + 1, - vd->n_children - i - 1 - ); + vd->children + i + 1, + vd->n_children - i - 1); vd->n_children--; @@ -196,77 +196,77 @@ int remove_vdir(vdir_t* vdir) { // path begins with p, // it corresponds! - if(!strncmp(vdir->path, p, strlen(p))) { + if (!strncmp(vdir->path, p, strlen(p))) + { child = &vd->children[i]; break; } } - if(!child) { + if (!child) + { return 0; } - else { + else + { vd = child; } } } - -/* // return 0 iif something // went wrong -static -int unmount(vdir_t* vdir) { - fs_t* fs = vdir->fs; - //get_fs_vdir() - vdir_t* vdir = search_fs(&vfs_root, fs); - - +static int unmount(vdir_t *vdir) +{ + fs_t *fs = vdir->fs; assert(vdir); - assert(vdir->fs == fs); - if(vdir->n_children) { + if (vdir->n_children) + { log_warn("cannot unmount partition %s: it is not a leaf partition", fs->part->name); return 0; } - if(fs->n_open_files != 0) { + if (fs->n_open_files != 0) + { log_warn( - "cannot unmount partition %s: %u open files", + "cannot unmount partition %s: %u open files", fs->n_open_files, fs->part->name); return 0; } - - if(vdir == &vfs_root) + if (vdir == &vfs_root) vfs_root.fs = NULL; else assert(remove_vdir(vdir)); - - - for(unsigned i = 0; i < cache_size; i++) { - if(cached_dir_list[i].fs == fs) { + // remove cache entries related with this thing + for (unsigned i = 0; i < cache_size; i++) + { + if (cached_dir_list[i].fs == fs) + { free(cached_dir_list[i].path); cached_dir_list[i].path = NULL; } } + fs->unmount(fs); return 1; } -*/ -static -void free_cache(void) { - for(unsigned i = 0; i < cache_size; i++) { +static void free_cache(void) +{ + for (unsigned i = 0; i < cache_size; i++) + { // this is null if the entry // is empty - if(cached_dir_list[i].path) { + if (cached_dir_list[i].path) + { free(cached_dir_list[i].path); } } @@ -274,10 +274,9 @@ void free_cache(void) { cache_size = 0; } - void vfs_init(void) { -// cache size must be a power of two + // cache size must be a power of two cache_size = 4096; cached_dir_list = malloc(sizeof(dir_cache_ent_t) * cache_size); @@ -290,127 +289,119 @@ void vfs_init(void) .fs = 0, .path = "/", }; + atshutdown(vfs_cleanup); } - -void vfs_cleanup(void) { +void vfs_cleanup(void) +{ free_cache(); free_vtree(&vfs_root); -/* - for(unsigned i = 0; i < n_mounted_fs; i++) { - fs_t* fs = mounted_fs_list[i]; - - fs->unmount(fs); - } - - free(mounted_fs_list); - n_mounted_fs = 0; -*/ } // return 0 iif entry not present -static int is_cache_entry_present(dir_cache_ent_t* cache_ent) { +static int is_cache_entry_present(dir_cache_ent_t *cache_ent) +{ return cache_ent->path != NULL; } - -static void free_cache_entry(dir_cache_ent_t* cache_ent) { +static void free_cache_entry(dir_cache_ent_t *cache_ent) +{ free(cache_ent->path); // nothing else to unallocate } /** - * @brief do not forget to call + * @brief do not forget to call * fs->free_dirents(entries) * afterwards - * + * * @param fs the fs associated with the dir * @param ino inode number * @param dir_path vfs path in canonical form ('/a/b/c') * @param n (ouput) array size * @return dirent_t* array */ -static -dirent_t* read_dir(fs_t* fs, ino_t ino, const char* dir_path, size_t* n) +static dirent_t *read_dir(fs_t *fs, ino_t ino, const char *dir_path, size_t *n) { assert(cache_size); - - dirent_t* ents = fs->read_dir(fs, ino, n); + dirent_t *ents = fs->read_dir(fs, ino, n); unsigned _n = *n; // add cache entries - for(unsigned i = 0; i < _n; i++) { - dirent_t* dent = &ents[i]; + for (unsigned i = 0; i < _n; i++) + { + dirent_t *dent = &ents[i]; - int len = strlen(dent->name)+strlen(dir_path)+1; + int len = strlen(dent->name) + strlen(dir_path) + 1; assert(len < MAX_PATH); - char* path = malloc(len+1); + char *path = malloc(len + 1); strcpy(path, dir_path); strcat(path, "/"); strcat(path, dent->name); - uint16_t hash = path_hash(path) & (cache_size - 1); - dir_cache_ent_t* cache_ent = &cached_dir_list[hash]; + dir_cache_ent_t *cache_ent = &cached_dir_list[hash]; - if(is_cache_entry_present(cache_ent)) + if (is_cache_entry_present(cache_ent)) free_cache_entry(cache_ent); - - cache_ent->cluster = dent->ino; + + cache_ent->cluster = dent->ino; cache_ent->file_size = dent->reclen; - cache_ent->type = dent->type; - cache_ent->fs = fs; - cache_ent->path = path; + cache_ent->type = dent->type; + cache_ent->fs = fs; + cache_ent->path = path; } - + return ents; } - /** - * @brief return the virtual directory - * representing the fs on which the file/dir - * should be or the virtual dir if the path + * @brief return the virtual directory + * representing the fs on which the file/dir + * should be or the virtual dir if the path * represents a vdir - * + * * @param path file/dir path in cannonical form - * @return NULL if no fs is succeptible to + * @return NULL if no fs is succeptible to * contain the path */ -static -vdir_t* get_fs_vdir(const char *path) { - - vdir_t* vd = &vfs_root; +static vdir_t *get_fs_vdir(const char *path) +{ + vdir_t *vd = &vfs_root; // go deeper and deeper in the vfs tree // until there is no match - while(1) { - vdir_t* child = NULL; - + while (1) + { + vdir_t *child = NULL; - for(unsigned i = 0; i < vd->n_children; i++) { - const char* p = vd->children[i].path; + for (unsigned i = 0; i < vd->n_children; i++) + { + const char *p = vd->children[i].path; // path begins with p, // it corresponds! - if(!strncmp(path, p, strlen(p))) { + if (!strncmp(path, p, strlen(p))) + { child = &vd->children[i]; break; } } - if(!child) { - if(vd->fs == NULL) + if (!child) + { + if (vd->fs == NULL) return NULL; return vd; } - else { + else + { vd = child; } } @@ -419,82 +410,87 @@ vdir_t* get_fs_vdir(const char *path) { // return the number // of naste in path // path in cannonical form -static unsigned nasted_order(const char* path) { +static unsigned nasted_order(const char *path) +{ unsigned n = 0; assert(is_absolute(path)); // path == '/' - if(!path[1]) + if (!path[1]) return 0; path--; - while((path = strchr(++path, '/'))) + while ((path = strchr(++path, '/'))) n++; - + return n; } - /** * @brief insert a virtual directory in * the vdir tree. The input structure * is copied to the tree - * + * * @param new * @return int 0 if it cannot be added */ -static -int insert_vdir(vdir_t* new) { - vdir_t* vdir = &vfs_root; +static vdir_t *insert_vdir(vdir_t *new) +{ + vdir_t *vdir = &vfs_root; - while(1) { - vdir_t* match = NULL; + while (1) + { + vdir_t *match = NULL; - for(unsigned i = 0; i < vdir->n_children; i++) { - vdir_t* child = &vdir->children[i]; - if(!strncmp(child->path, new->path, strlen(child->path))) { + for (unsigned i = 0; i < vdir->n_children; i++) + { + vdir_t *child = &vdir->children[i]; + if (!strncmp(child->path, new->path, strlen(child->path))) + { match = child; break; } } - if(match) + if (match) vdir = match; - else { + else + { // something is already mounted there! - if(!strcmp(vdir->path, new->path)) + if (!strcmp(vdir->path, new->path)) return 0; vdir->n_children++; vdir->children = realloc(vdir->children, vdir->n_children * sizeof(vdir_t)); - memcpy(vdir->children+vdir->n_children-1, new, sizeof(vdir_t)); - return 1; + vdir_t *newdir = vdir->children + vdir->n_children - 1; + memcpy(newdir, new, sizeof(vdir_t)); + + return newdir; } } } - - /** - * @brief return a - * - * @param n the number of vchildren or -1 if the + * @brief return a + * + * @param n the number of vchildren or -1 if the * dir does not seem to exist. Warning: the path * may exist anyway, it is just doesn't appear * in the vfs structure. * @param path path in cannonical form - * @return vdir_t** heap allocated list of virtual + * @return vdir_t** heap allocated list of virtual * children of the path associated dir entry */ -static vdir_t** get_vchildren(const char* path, int* n) { +static vdir_t **get_vchildren(const char *path, int *n) +{ - vdir_t** ret = NULL; + vdir_t **ret = NULL; *n = 0; - vdir_t* vd = &vfs_root; + vdir_t *vd = &vfs_root; unsigned path_nasted_order = nasted_order(path); @@ -503,114 +499,116 @@ static vdir_t** get_vchildren(const char* path, int* n) { // go deeper and deeper in the vfs tree // until there is no ancestor match -// /a/b/d/xr -// /c -// /a -// /a/c/f/z -// /a/d/f/z -// /a/b -// /a/b/d -// /a/b/d -// /a/b/d/xr/gg -// /b - while(1) { - vdir_t* ancestor = NULL; - - for(unsigned i = 0; i < vd->n_children; i++) { - const char* p = vd->children[i].path; + // /a/b/d/xr + // /c + // /a + // /a/c/f/z + // /a/d/f/z + // /a/b + // /a/b/d + // /a/b/d + // /a/b/d/xr/gg + // /b + while (1) + { + vdir_t *ancestor = NULL; + + for (unsigned i = 0; i < vd->n_children; i++) + { + const char *p = vd->children[i].path; // it corresponds! - if(!strncmp(path, p, strlen(path))) { + if (!strncmp(path, p, strlen(path))) + { unsigned no = nasted_order(p); // p begins with path, // therefore the directory // does exist exists = 1; - - if(path_nasted_order + 1 == no) { + if (path_nasted_order + 1 == no) + { // we are in the right directory! unsigned id = (*n)++; - ret = realloc(ret, *n * sizeof(vdir_t*)); + ret = realloc(ret, *n * sizeof(vdir_t *)); ret[id] = &vd->children[i]; } } - if(!strncmp(path, p, strlen(p))) { + if (!strncmp(path, p, strlen(p))) + { ancestor = &vd->children[i]; break; } } - - if(ancestor) + if (ancestor) vd = ancestor; - else { - if(!exists) + else + { + if (!exists) *n = -1; return ret; } } } - /** * find a child with a given name - * - * @param parent - * @param child + * + * @param parent + * @param child * @param name * @return int 0 if no child have this * name */ -static -int find_fs_child( - fs_t* restrict fs, - fast_dirent_t* parent, - fast_dirent_t* child, - const char* name) +static int find_fs_child( + fs_t *restrict fs, + fast_dirent_t *parent, + fast_dirent_t *child, + const char *name) { size_t n; int found = 0; // no cache - dirent_t* ents = fs->read_dir(fs, parent->ino, &n); + dirent_t *ents = fs->read_dir(fs, parent->ino, &n); - for(unsigned i = 0; i < n; i++) { - if(!strcmp(ents[i].name, name)) { - - child->ino = ents[i].ino; + for (unsigned i = 0; i < n; i++) + { + if (!strcmp(ents[i].name, name)) + { + + child->ino = ents[i].ino; child->reclen = ents[i].reclen; - child->type = ents[i].type; - + child->type = ents[i].type; + found = 1; break; } } - fs->free_dirents(ents); return found; } - // path in cannonical form // return NULL if empty / conflict -static -dir_cache_ent_t* get_cache_entry(const char* path) { +static dir_cache_ent_t *get_cache_entry(const char *path) +{ assert(is_absolute(path)); assert(cache_size); - uint16_t hash = path_hash(path) & (cache_size-1); + uint16_t hash = path_hash(path) & (cache_size - 1); // check cache - dir_cache_ent_t* ent = &cached_dir_list[hash]; - if(ent->path == NULL) // not present entry + dir_cache_ent_t *ent = &cached_dir_list[hash]; + if (ent->path == NULL) // not present entry return NULL; - if(!strcmp(ent->path, path)) // right entry (no collision) + if (!strcmp(ent->path, path)) // right entry (no collision) return ent; return NULL; @@ -618,7 +616,7 @@ dir_cache_ent_t* get_cache_entry(const char* path) { /** * @brief open a directory entry - * + * * @param path the directory path in cannonical * form * @param dir (output) directory entry descriptor @@ -626,34 +624,33 @@ dir_cache_ent_t* get_cache_entry(const char* path) { * not exist. the file system associated with the dir * otherwise */ -static -fs_t* open_dir(const char *path, fast_dirent_t* dir) +static fs_t *open_dir(const char *path, fast_dirent_t *dir) { assert(is_absolute(path)); - // check cache - dir_cache_ent_t* ent = get_cache_entry(path); - if(ent) { // present entry - dir->ino = ent->cluster; + dir_cache_ent_t *ent = get_cache_entry(path); + if (ent) + { // present entry + dir->ino = ent->cluster; dir->reclen = ent->file_size; - dir->type = ent->type; + dir->type = ent->type; return ent->fs; } - vdir_t* vdir = get_fs_vdir(path); + vdir_t *vdir = get_fs_vdir(path); - if(!vdir) { + if (!vdir) + { // there is no wat the dir could // exist return NULL; } - fs_t* fs = vdir->fs; - + fs_t *fs = vdir->fs; - char *buf = malloc(strlen(path)+1); + char *buf = malloc(strlen(path) + 1); strcpy(buf, path + strlen(vdir->path)); // only keep the FS part @@ -661,68 +658,66 @@ fs_t* open_dir(const char *path, fast_dirent_t* dir) char *sub = strtok(buf, "/"); - fast_dirent_t cur = { - .ino = fs->root_addr, + .ino = fs->root_addr, .reclen = 0, - .type = DT_DIR, + .type = DT_DIR, }; - while (sub != NULL && *sub) { fast_dirent_t child; - - if(!find_fs_child(fs, &cur, &child, sub)) { + + if (!find_fs_child(fs, &cur, &child, sub)) + { // the child does not exist free(buf); return NULL; } - cur.ino = child.ino; + cur.ino = child.ino; cur.reclen = child.reclen; - cur.type = child.type; + cur.type = child.type; sub = strtok(NULL, "/"); } - free(buf); - dir->ino = cur.ino; + dir->ino = cur.ino; dir->reclen = cur.reclen; - dir->type = cur.type; - + dir->type = cur.type; + return fs; } - -static -void __attribute__((noinline)) log_tree(const char* path, int level) +static void log_tree(const char *path, int level) { - struct DIR* dir = vfs_opendir(path); + struct DIR *dir = vfs_opendir(path); - struct dirent* dirent; + struct dirent *dirent; assert(dir); - while((dirent = vfs_readdir(dir))) { - for(int i = 0; i < level+1; i++) + while ((dirent = vfs_readdir(dir))) + { + for (int i = 0; i < level + 1; i++) puts("-"); printf(" %s (%u)\n", dirent->name, dirent->type); - - if(dirent->type == DT_DIR) { - char* pathb = malloc(1024); - - strcpy(pathb,path); - strcat(pathb,"/"); - strcat(pathb,dirent->name); - log_tree(pathb, level+1); - + if (dirent->type == DT_DIR) + { + char *pathb = malloc(1024); + + strcpy(pathb, path); + strcat(pathb, "/"); + strcat(pathb, dirent->name); + + log_tree(pathb, level + 1); + free(pathb); } } @@ -730,83 +725,84 @@ void __attribute__((noinline)) log_tree(const char* path, int level) vfs_closedir(dir); } - int vfs_mount(disk_part_t *part, const char *path) { - fs_t *fs = fat32_mount(part); - - if (!fs) - { - log_warn("cannot mount %s, unrecognized format", part->name); - return 0; - } - // register the new virtual directory - - vdir_t new = { - .fs = fs, + vdir_t tmp = { + .fs = NULL, .n_children = 0, - .children = NULL, - .path = malloc(strlen(path)+1), + .children = NULL, + .path = malloc(strlen(path) + 1), }; // path argument might not be in // cannonical form - simplify_path(new.path, path); - + simplify_path(tmp.path, path); + vdir_t *new = insert_vdir(&tmp); // if it cannot be inserted - if(!insert_vdir(&new)) { + if (!new) + { log_warn( "cannot mount %s," - "impossible to create virtual directory %s", - part->name, - new.path - ); + "impossible to create virtual directory %s", + part->name, + tmp.path); + + free(tmp.path); + return 0; + } - free(new.path); - fs->unmount(fs); + fs_t *fs = fat32_mount(part); + if (!fs) + { + log_warn("cannot mount %s, unrecognized format", part->name); return 0; } -//return; + new->fs = fs; -/* file_handle_t* f = vfs_open_file("/fs/boot/limine.cfg"); + assert(f); + + assert(!vfs_seek_file(f, 512, SEEK_SET)); + log_warn("FILE SIZE = %u", vfs_tell_file(f)); + //vfs_seek_file(f, 0, SEEK_SET); + char buf[26]; int read = 0; int i = 0; - while((read = vfs_read_file(buf, 1, 25, f))) { + while ((read = vfs_read_file(buf, 1, 25, f))) + { buf[read] = 0; - //puts(buf); - log_debug("%u read %u", i, read); - if(++i == 1) - break; + puts(buf); + i++; + // log_debug("%u read %u", i++, read); } vfs_close_file(f); -*/ - - //log_warn("open"); - //struct DIR* dir = vfs_opendir("/fs/"); - //vfs_closedir(dir); - log_tree("/",0); + log_debug("fg"); return 1; } - -int vfs_unmount(const char* path) { +int vfs_unmount(const char *path) +{ // do stuf! - //get_fs_vdir - //return unmount(path); - return 0; -} + char *pbuf = malloc(strlen(path) + 1); + simplify_path(pbuf, path); + vdir_t *vdir = get_fs_vdir(pbuf); + + if (!vdir) + return 0; + + return unmount(vdir); +} file_handle_t *vfs_open_file(const char *path) { - char* pathbuf = malloc(strlen(path) + 1); + char *pathbuf = malloc(strlen(path) + 1); simplify_path(pathbuf, path); fast_dirent_t dirent; @@ -814,34 +810,31 @@ file_handle_t *vfs_open_file(const char *path) free(pathbuf); - if(!fs) // dirent not found + if (!fs) // dirent not found return NULL; // file does not exist or isn't a file - if(dirent.type != DT_REG) + if (dirent.type != DT_REG) return NULL; - - // big allocation to allocate the // sector buffer too file_handle_t *handler = malloc( - sizeof(file_handle_t) - + fs->file_cursor_size + sizeof(file_handle_t) + fs->file_cursor_size // size of one sector + fs->file_access_granularity); - log_warn("file_access_granularity = %u", fs->file_access_granularity); - handler->file_size = dirent.reclen; handler->fs = fs; handler->file_offset = 0; // empty buffer - handler->sector_offset = fs->file_access_granularity; - handler->sector_buff = (void *)handler + - sizeof(file_handle_t) + fs->file_cursor_size; + handler->sector_offset = 0; + handler->buffer_valid = 0; + + handler->sector_buff = (void *)handler + + sizeof(file_handle_t) + fs->file_cursor_size; file_t file = { .addr = dirent.ino, @@ -853,7 +846,6 @@ file_handle_t *vfs_open_file(const char *path) return handler; } - void vfs_close_file(file_handle_t *handle) { fs_t *fs = handle->fs; @@ -863,30 +855,28 @@ void vfs_close_file(file_handle_t *handle) free(handle); } - -struct DIR* vfs_opendir(const char* path) { +struct DIR *vfs_opendir(const char *path) +{ /** * as they are in different structures, * we must both search for virtual entries * and fs entries. - * + * */ - char* pathbuff = malloc(strlen(path) + 1); + char *pathbuff = malloc(strlen(path) + 1); simplify_path(pathbuff, path); size_t n = 0; - - fast_dirent_t fdir; - fs_t* fs = open_dir(pathbuff, &fdir); - - struct DIR* dir = NULL; + fs_t *fs = open_dir(pathbuff, &fdir); - if(fs) {// dir does exist - dirent_t* list = read_dir(fs, fdir.ino, pathbuff, &n); + struct DIR *dir = NULL; + + if (fs) + { // dir does exist + dirent_t *list = read_dir(fs, fdir.ino, pathbuff, &n); - dir = malloc(sizeof(struct DIR) + sizeof(dirent_t) * n); memcpy(dir->children, list, n * sizeof(dirent_t)); @@ -894,86 +884,82 @@ struct DIR* vfs_opendir(const char* path) { fs->free_dirents(list); } - - //////////////////////////////////////// // now search for virtual entries //////////////////////////////////////// int vn; - vdir_t** vents = get_vchildren(pathbuff, &vn); - - - - if(vn >= 0) {// the dir exists in the vfs + vdir_t **vents = get_vchildren(pathbuff, &vn); - log_warn("VIRTUAL DIRECTORY ENTRY! vn=%u",vn); + if (vn >= 0) + { // the dir exists in the vfs // allocate the extra results - dir = realloc(dir, sizeof(struct DIR) + sizeof(dirent_t) * (n+vn)); + dir = realloc(dir, sizeof(struct DIR) + sizeof(dirent_t) * (n + vn)); - dirent_t* list = dir->children+n; + dirent_t *list = dir->children + n; - - for(int i = 0; i < vn; i++) { - list[i] = (dirent_t) { - .ino = 0, + for (int i = 0; i < vn; i++) + { + list[i] = (dirent_t){ + .ino = 0, .reclen = 0, - .type = DT_DIR, + .type = DT_DIR, }; - const char* subpath = strrchr(vents[i]->path, '/'); + const char *subpath = strrchr(vents[i]->path, '/'); assert(subpath); - strcpy(list[i].name, subpath+1); + strcpy(list[i].name, subpath + 1); } n += vn; - if(vents) + if (vents) free(vents); } - free(pathbuff); - if(dir) { + if (dir) + { dir->len = n; dir->cur = 0; } - + return dir; } - -void vfs_closedir(struct DIR* dir) { +void vfs_closedir(struct DIR *dir) +{ free(dir); } -struct dirent* vfs_readdir(struct DIR* dir) { - if(dir->cur == dir->len) +struct dirent *vfs_readdir(struct DIR *dir) +{ + if (dir->cur == dir->len) return NULL; // reached end of dir else return &dir->children[dir->cur++]; } - size_t vfs_read_file(void *ptr, size_t size, size_t nmemb, file_handle_t *restrict stream) { - assert(stream); - assert(ptr); - void * const buf = stream->sector_buff; + assert(stream); + assert(ptr); - log_warn("buf=%lx, stream=%lx",buf,stream); - const fs_t *fs = stream->fs; + void *const buf = stream->sector_buff; + + fs_t *fs = stream->fs; uint64_t file_size = stream->file_size; - unsigned granularity = stream->fs->file_access_granularity; + unsigned granularity = fs->file_access_granularity; + unsigned cachable = fs->cacheable; unsigned max_read = file_size - stream->file_offset; @@ -981,28 +967,207 @@ size_t vfs_read_file(void *ptr, size_t size, size_t nmemb, // check nmemb bounds - if(max_read < bsize) { + if (max_read < bsize) + { nmemb = max_read / size; bsize = size * nmemb; } + if(bsize == 0) + return 0; + + // we are to read some bytes. + // if the fs is cachable, and the + // granularity cache is invalid, + // we have to fill it beforehand + if(cachable && !stream->buffer_valid) { + fs->read_file_sector( + stream->fs, + stream->cursor, + buf + ); + + stream->buffer_valid = 1; + } + + + while (bsize) + { + unsigned remaining = file_size - stream->file_offset; + + unsigned n_ready = granularity - stream->sector_offset; + + if(n_ready > remaining) + n_ready = remaining; + + if(!cachable) { + // for sure, bsize != 0. + // we have to fetch again + // the sector + // without advancing the cursor + fs->read_file_sector( + stream->fs, + stream->cursor, + buf); + } + + + if (n_ready >= bsize) { + memcpy( + ptr, + buf + stream->sector_offset, + bsize + ); + + ptr += bsize; + stream->sector_offset += bsize; + stream->file_offset += bsize; + bsize = 0; + + // here, + // n_ready = granularity - sector_offset >= bsize + // <=> sector_offset + bsize <= g + // when n_ready = bsize, + // we set stream->sector_offset to granularity. + // + // for consistancy with writes, + // sector_offset has to be strictly inferior to + // granularity, when at least one read has been + // done. + // We therefore advance the cursor + if (!cachable && stream->sector_offset == granularity) { + stream->sector_offset = 0; + fs->advance_file_cursor(fs, stream->cursor); + } + } + else { + if (n_ready) { + memcpy( + ptr, + buf + stream->sector_offset, + n_ready + ); + + bsize -= n_ready; + ptr += n_ready; + stream->file_offset += n_ready; + } + + if (n_ready) // the cache was valid + fs->advance_file_cursor(fs, stream->cursor); + + if (cachable) { + // iif the fs is cachable, + // we can fetch the next file sector + // for the next iteration + + // for coherency with writes, + // the file cursor must always point + // on the current read sector: we therefore + // cannot advance the file cursor afterwards + // like read(cur++) + // + // Instead, we have to increment beforehand. + // we cannot advance the cursor like read(++cur) + // as the first sector would be forgotten + + fs->read_file_sector( + stream->fs, + stream->cursor, + buf + ); + } + stream->sector_offset = 0; + } + } + + return nmemb; +} + + + +size_t vfs_write_file(const void *ptr, size_t size, size_t nmemb, + file_handle_t *stream) +{ + + assert(stream); + assert(ptr); + assert(0); + /* + void * const buf = stream->sector_buff; + + const fs_t *fs = stream->fs; + uint64_t file_size = stream->file_size; + + unsigned granularity = fs->file_access_granularity; + + unsigned cachable = fs->cacheable; + + // no checking: we hope that the drive won't + // ever be overflowed + size_t bsize = size * nmemb; while(bsize) { + + // don't need no buffering + if((stream->sector_offset == 0 + || stream->sector_offset == granularity) + && bsize >= granularity) { + int wr = fs->write_file_sector( + fs, + stream->cursor, + ptr, + granularity + ); + + assert(wr == (int)granularity); + ptr += granularity; + stream->file_offset += granularity; + bsize -= granularity; + } + else { + unsigned offset = stream->sector_offset; + unsigned write_size = MIN(bsize, granularity - offset); + + memcpy(buf + offset, ptr, write_size); + + if(stream->file_offset == stream->file_size + && offset == 0) { + // no need to read before writing + } + else if(!fs->cacheable || offset == granularity) { + // need to + + } + + int wr = fs->write_file_sector( + fs, + stream->cursor, + ptr, + granularity + ); + + assert(wr == write_size); + + ptr += bsize; + stream->file_offset += bsize; + bsize = 0; + } + + stream->file_size = MAX(stream->file_offset, + stream->file_size); unsigned remaining = file_size - stream->file_offset; - - + + unsigned n_ready = granularity - stream->sector_offset; -/* - log_debug("granularity=%u ; stream->file_offset=%u ; n_ready=%u, SECTOR_OFFSET=%u", - granularity, - stream->file_offset, - n_ready, - stream->sector_offset); -*/ + if(n_ready > remaining) n_ready = remaining; + if(!cachable) + n_ready = 0; + if(n_ready >= bsize) { @@ -1020,37 +1185,85 @@ size_t vfs_read_file(void *ptr, size_t size, size_t nmemb, } else { - memcpy( - ptr, - buf + stream->sector_offset, - n_ready - ); - bsize -= n_ready; + if(n_ready) { + memcpy( + ptr, + buf + stream->sector_offset, + n_ready + ); + bsize -= n_ready; + ptr += n_ready; + stream->file_offset += n_ready; + } - ptr += n_ready; - stream->file_offset += n_ready; stream->sector_offset = 0; fs->read_file_sector( stream->fs, stream->cursor, buf); - // memset(buf, 4, 513); -return 0; } } - +*/ return nmemb; } +int vfs_seek_file(file_handle_t *restrict stream, uint64_t offset, int whence) +{ -size_t vfs_write_file(const void *ptr, size_t size, size_t nmemb, - file_handle_t *stream) { - (void) ptr; - (void) size; - (void) nmemb; - (void) stream; - panic("vfs_write_file: unimplemented"); - __builtin_unreachable(); + // real file offset: to be + // calculated with the whence field + uint64_t absolute_offset = offset; + + switch (whence) + { + case SEEK_CUR: + absolute_offset += stream->file_offset; + break; + case SEEK_END: + absolute_offset += stream->file_size; + break; + case SEEK_SET: + break; + default: + // bad whence parameter value + return -1; + } + + size_t block_size = stream->fs->file_access_granularity; + + uint64_t new_sector_id = absolute_offset / block_size; + uint64_t old_sector_id = stream->file_offset / block_size; + + if (new_sector_id != old_sector_id) + { + log_warn("CHANGED SECTOR %u --> %u", old_sector_id, new_sector_id); + // the seeked value is in another block. + // We therefore need to locate the new one + fs_t *fs = stream->fs; + fs->seek(fs, stream->cursor, new_sector_id, SEEK_SET); + + stream->buffer_valid = 0; + + // need to flush the cache content + if(stream->fs->cacheable) + + fs->read_file_sector(fs, stream->cursor, stream->sector_buff); + } + // else, only the sector offset changed. + // no need to access the fs + + stream->sector_offset = absolute_offset % block_size; + stream->file_offset = absolute_offset; + + log_warn("NEW OFFSET %u", stream->sector_offset); + + // everything is fine: return 0 + return 0; } + +long vfs_tell_file(file_handle_t *restrict stream) +{ + return stream->file_offset; +} \ No newline at end of file diff --git a/kernel/fs/vfs.h b/kernel/fs/vfs.h index 8653d2b..be3897f 100644 --- a/kernel/fs/vfs.h +++ b/kernel/fs/vfs.h @@ -35,7 +35,6 @@ void vfs_init(void); // free every memory block void vfs_cleanup(void); - /** * @brief file handler used to read, * write, seek. Uses polymorphic calls @@ -52,6 +51,11 @@ void vfs_cleanup(void); typedef struct file_handler { fs_t* fs; + // if fs->cachable = 0: this field is + // undefined. Else, writes/reads can + // rely on the granularity buffer + unsigned buffer_valid; + // the filesystems have sector granularity // so we put those fields to buffer the accesses // and have byte granularity @@ -168,3 +172,11 @@ size_t vfs_read_file(void* ptr, size_t size, size_t nmemb, size_t vfs_write_file(const void* ptr, size_t size, size_t nmemb, file_handle_t* stream); + + +// SEEK_SET, SEEK_END, SEEK_CUR are defined in fs.h + +// return 0 if successful (see man fseek) +int vfs_seek_file(file_handle_t* stream, uint64_t offset, int whence); +long vfs_tell_file(file_handle_t* stream); +