aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/kernel/include/vfs.h14
-rw-r--r--src/kernel/user/vfs.c58
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;