aboutsummaryrefslogtreecommitdiff
path: root/src/kernel/user/vfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel/user/vfs.c')
-rw-r--r--src/kernel/user/vfs.c158
1 files changed, 144 insertions, 14 deletions
diff --git a/src/kernel/user/vfs.c b/src/kernel/user/vfs.c
index 32fcb03..81325dd 100644
--- a/src/kernel/user/vfs.c
+++ b/src/kernel/user/vfs.c
@@ -16,7 +16,7 @@ fs_driver_t *drivers = 0;
void register_fs_driver(const char* name, fs_driver_ops_t *ops) {
fs_driver_t *d = (fs_driver_t*)malloc(sizeof(fs_driver_t));
- ASSERT(d != 0); // should we fail in a more graceful manner ?
+ ASSERT(d != 0); // should we fail in a more graceful manner ? TODO
d->name = name;
d->ops = ops;
@@ -25,7 +25,7 @@ void register_fs_driver(const char* name, fs_driver_ops_t *ops) {
}
// ================================== //
-// CREATING AND DELETINF FILE SYSTEMS //
+// CREATING AND DELETING FILE SYSTEMS //
// ================================== //
fs_t *make_fs(const char* drv_name, fs_handle_t *source, char* opts) {
@@ -36,13 +36,18 @@ fs_t *make_fs(const char* drv_name, fs_handle_t *source, char* opts) {
if (drv_name == 0 && source != 0 && i->ops->detect && i->ops->detect(source)) d = i;
if (d != 0) break;
}
+ if (d == 0) return 0; // driver not found
// Open file system
fs_t *fs = (fs_t*)malloc(sizeof(fs_t));
if (fs == 0) return 0;
+ fs->refs = 1;
+ fs->root.refs = 1; // root node is never disposed of (done by fs->shutdown)
+ fs->root.fs = fs;
+ fs->root.parent = 0;
+
if (d->ops->make(source, opts, fs)) {
- fs->refs = 1;
return fs;
} else {
free(fs);
@@ -61,27 +66,141 @@ void ref_fs(fs_t *fs) {
void unref_fs(fs_t *fs) {
fs->refs--;
if (fs->refs == 0) {
+ // don't unref root node, don't call dispose on it
+ // (done by fs->shutdown)
fs->ops->shutdown(fs->data);
free(fs);
}
}
+// ====================================================== //
+// WALKING IN THE FILE SYSTEM CREATING AND DELETING NODES //
+
+void ref_fs_node(fs_node_t *n) {
+ n->refs++;
+}
+
+void unref_fs_node(fs_node_t *n) {
+ n->refs--;
+ if (n->refs == 0) {
+ ASSERT(n != &n->fs->root);
+ ASSERT(n->parent != 0);
+
+ n->ops->dispose(n->data);
+ unref_fs_node(n->parent);
+ unref_fs(n->fs);
+ free(n);
+ }
+}
+
+fs_node_t* fs_walk_one(fs_node_t* from, const char* file) {
+ 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;
+ }
+}
+
+fs_node_t* fs_walk_path(fs_node_t* from, const char* path) {
+ fs_node_t *n = from;
+ ref_fs_node(n);
+
+ while (n && (*path)) {
+ const char* d = strchr(path, '/');
+ if (d == path) {
+ path++;
+ } else if (d == 0) {
+ fs_node_t *n2 = fs_walk_one(n, path);
+ unref_fs_node(n);
+
+ return n2;
+ } else {
+ size_t nlen = d - path;
+ if (nlen >= DIR_MAX - 1) {
+ unref_fs_node(n);
+ return 0; // sorry, path item too long
+ }
+ char name_buf[DIR_MAX];
+ strncpy(name_buf, path, nlen);
+ name_buf[nlen] = 0;
+
+ fs_node_t *n2 = fs_walk_one(n, name_buf);
+ unref_fs_node(n);
+ n = n2;
+
+ path = d + 1;
+ }
+ }
+ return n;
+}
+
+fs_node_t* fs_walk_path_except_last(fs_node_t* from, const char* path, char* last_file_buf) {
+ // TODO : parse path, walk
+ // This function does NOT walk into the last component of the path (it may not even exist),
+ // instead it isolates it in the buffer last_file
+ // This buffer is expected to be of size DIR_MAX (defined in fs.h)
+ return 0;
+}
+
+// ========================== //
// DOING THINGS IN FLESYSTEMS //
+bool fs_create(fs_t *fs, const char* file, int type) {
+ char name[DIR_MAX];
+ fs_node_t *n = fs_walk_path_except_last(&fs->root, file, name);
+ if (n == 0) return false;
+
+ bool ret = n->ops->create && n->ops->create(n->data, name, type);
+ unref_fs_node(n);
+ return ret;
+}
+
bool fs_delete(fs_t *fs, const char* file) {
- return fs->ops->delete && fs->ops->delete(fs->data, file);
+ fs_node_t* n = fs_walk_path(&fs->root, file);
+ if (n == 0) return false;
+
+ bool ret = n->ops->delete && n->ops->delete(n->data);
+ unref_fs_node(n);
+ return ret;
}
-bool fs_rename(fs_t *fs, const char* from, const char* to) {
- return fs->ops->rename && fs->ops->rename(fs->data, from, to);
+bool fs_move(fs_t *fs, const char* from, const char* to) {
+ fs_node_t *n = fs_walk_path(&fs->root, from);
+ if (n == 0) return false;
+
+ char new_name[DIR_MAX];
+ fs_node_t *new_parent = fs_walk_path_except_last(&fs->root, to, new_name);
+ if (new_parent == 0) return false;
+
+ return n->ops->move && n->ops->move(n->data, new_parent, new_name);
}
-bool fs_stat(fs_t *fs, const char* name, stat_t *st) {
- return fs->ops->stat && fs->ops->stat(fs->data, name, st);
+bool fs_stat(fs_t *fs, const char* file, stat_t *st) {
+ fs_node_t* n = fs_walk_path(&fs->root, file);
+ if (n == 0) return false;
+
+ bool ret = n->ops->stat && n->ops->stat(n->data, st);
+ unref_fs_node(n);
+ return ret;
}
int fs_ioctl(fs_t *fs, const char* file, int command, void* data) {
- return fs->ops->ioctl && fs->ops->ioctl(fs->data, file, command, data);
+ fs_node_t* n = fs_walk_path(&fs->root, file);
+ if (n == 0) return false;
+
+ int ret = (n->ops->ioctl ? n->ops->ioctl(n->data, command, data) : -1);
+ unref_fs_node(n);
+ return ret;
}
// =================== //
@@ -89,15 +208,25 @@ int fs_ioctl(fs_t *fs, const char* file, int command, void* data) {
// =================== //
fs_handle_t* fs_open(fs_t *fs, const char* file, int mode) {
+ fs_node_t *n = fs_walk_path(&fs->root, file);
+ if (n == 0) return false;
+
fs_handle_t *h = (fs_handle_t*)malloc(sizeof(fs_handle_t));
- if (h == 0) return 0;
+ if (h == 0) {
+ unref_fs_node(n);
+ return 0;
+ }
+
+ h->refs = 1;
+ h->fs = fs;
+ h->n = n;
- if (fs->ops->open(fs->data, file, mode, h)) {
- h->refs = 1;
- h->fs = fs;
+ if (n->ops->open(n->data, mode, h)) {
+ // our reference to node n is transferred to the file handle
ref_fs(fs);
return h;
} else {
+ unref_fs_node(n);
free(h);
return 0;
}
@@ -110,8 +239,9 @@ void ref_file(fs_handle_t *file) {
void unref_file(fs_handle_t *file) {
file->refs--;
if (file->refs == 0) {
- unref_fs(file->fs);
file->ops->close(file->data);
+ unref_fs_node(file->n);
+ unref_fs(file->fs);
free(file);
}
}