diff options
Diffstat (limited to 'src/kernel')
-rw-r--r-- | src/kernel/include/vfs.h | 14 | ||||
-rw-r--r-- | src/kernel/user/vfs.c | 58 |
2 files changed, 59 insertions, 13 deletions
diff --git a/src/kernel/include/vfs.h b/src/kernel/include/vfs.h index 2a5f3d8..140365c 100644 --- a/src/kernel/include/vfs.h +++ b/src/kernel/include/vfs.h @@ -2,6 +2,7 @@ #include <stdbool.h> #include <malloc.h> +#include <hashtbl.h> #include <fs.h> // common header @@ -53,8 +54,11 @@ typedef struct fs_handle { // Remarks : // - fs_node_t not to be used in public interface // - nodes keep a reference to their parent -// - unink() is expected to unink the node from its parent (make it inaccessible), but not dispose -// of its data before dispos() is called !! +// - a FS node is either in memory (after one call to walk() on its parent), or not in memory +// it can be in memory only once : if it is in memory, walk() cannot be called on the parent again +// - unlink() is expected to unlink the node from its parent (make it inaccessible) and delete +// the corresponding data. It is guaranteed that unlink() is never called on a node that +// is currently in use. (different from posix semantics !) // - the root node of a filesystem is created when the filesystem is created // - dispose() is not called on the root node when a filesystem is shutdown @@ -70,10 +74,14 @@ typedef struct { } fs_node_ops_t; typedef struct fs_node { - // These three fields are filled by the VFS's generic walk() code + // These fields are filled by the VFS's generic walk() code struct fs *fs; int refs; + hashtbl_t *children; // not all children, only those in memory + struct fs_node *parent; + char* name; // name in parent + // These fields are filled by the FS's specific walk() code fs_node_ops_t *ops; fs_node_ptr data; diff --git a/src/kernel/user/vfs.c b/src/kernel/user/vfs.c index 67b335c..995bfbc 100644 --- a/src/kernel/user/vfs.c +++ b/src/kernel/user/vfs.c @@ -46,6 +46,7 @@ fs_t *make_fs(const char* drv_name, fs_handle_t *source, char* opts) { fs->root.refs = 1; // root node is never disposed of (done by fs->shutdown) fs->root.fs = fs; fs->root.parent = 0; + fs->root.children = 0; if (d->ops->make(source, opts, fs)) { return fs; @@ -85,30 +86,62 @@ void unref_fs_node(fs_node_t *n) { if (n->refs == 0) { ASSERT(n != &n->fs->root); ASSERT(n->parent != 0); + ASSERT(n->name != 0); + + hashtbl_remove(n->parent->children, n->name); if (n->ops->dispose) n->ops->dispose(n->data); + unref_fs_node(n->parent); unref_fs(n->fs); + + free(n->name); free(n); } } fs_node_t* fs_walk_one(fs_node_t* from, const char* file) { + if (from->children != 0) { + fs_node_t *n = (fs_node_t*)hashtbl_find(from->children, file); + if (n != 0) { + ref_fs_node(n); + return n; + } + } + + bool walk_ok = false, add_ok = false; + fs_node_t *n = (fs_node_t*)malloc(sizeof(fs_node_t)); if (n == 0) return 0; n->fs = from->fs; n->refs = 1; n->parent = from; - - if (from->ops->walk && from->ops->walk(from->data, file, n)) { - ref_fs_node(n->parent); - ref_fs(n->fs); - return n; - } else { - free(n); - return 0; + n->children = 0; + n->name = strdup(file); + if (n->name == 0) goto error; + + walk_ok = from->ops->walk && from->ops->walk(from->data, file, n); + if (!walk_ok) goto error; + + if (from->children == 0) { + from->children = create_hashtbl(str_key_eq_fun, str_hash_fun, 0, 0); + if (from->children == 0) goto error; } + + add_ok = hashtbl_add(from->children, n->name, n); + if (!add_ok) goto error; + + ref_fs_node(n->parent); + ref_fs(n->fs); + + return n; + +error: + if (walk_ok) n->ops->dispose(n->data); + if (n->name != 0) free(n->name); + free(n); + return 0; } fs_node_t* fs_walk_path(fs_node_t* from, const char* path) { @@ -199,8 +232,8 @@ fs_node_t* fs_walk_path_except_last(fs_node_t* from, const char* path, char* las return n; } -// ========================== // -// DOING THINGS IN FLESYSTEMS // +// =========================== // +// DOING THINGS IN FILESYSTEMS // bool fs_create(fs_t *fs, const char* file, int type) { char name[DIR_MAX]; @@ -218,6 +251,11 @@ bool fs_unlink(fs_t *fs, const char* file) { fs_node_t* n = fs_walk_path_except_last(&fs->root, file, name); if (n == 0) return false; + if (n->children != 0) { + fs_node_t* x = (fs_node_t*)hashtbl_find(n->children, name); + if (x != 0) return false; + } + bool ret = n->ops->unlink && n->ops->unlink(n->data, name); unref_fs_node(n); return ret; |