aboutsummaryrefslogtreecommitdiff
path: root/src/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel')
-rw-r--r--src/kernel/include/vfs.h30
-rw-r--r--src/kernel/user/vfs.c158
2 files changed, 171 insertions, 17 deletions
diff --git a/src/kernel/include/vfs.h b/src/kernel/include/vfs.h
index 4efd0a2..ccb33f4 100644
--- a/src/kernel/include/vfs.h
+++ b/src/kernel/include/vfs.h
@@ -19,6 +19,11 @@ typedef void* fs_handle_ptr;
typedef void* fs_node_ptr;
typedef void* fs_ptr;
+// usefull forward declarations
+struct fs;
+struct fs_node;
+struct fs_handle;
+
// -------------------------------------------
// Structure defining a handle to an open file
@@ -32,6 +37,7 @@ typedef struct {
typedef struct fs_handle {
// These two field are filled by the VFS's generic open() code
struct fs *fs;
+ struct fs_node *n;
int refs;
// These fields are filled by the FS's specific open() code
@@ -47,13 +53,17 @@ typedef struct fs_handle {
// Remarks :
// - fs_node_t not to be used in public interface
// - nodes keep a reference to their parent
+// - delete() is expected to delete the node (make it inaccessible), but not dispose
+// of its data before dispos() is called !!
+// - 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
-struct fs_node;
typedef struct {
bool (*open)(fs_node_ptr n, int mode, fs_handle_t *s); // open current node
bool (*stat)(fs_node_ptr n, stat_t *st);
bool (*walk)(fs_node_ptr n, const char* file, struct fs_node *node_d);
bool (*delete)(fs_node_ptr n);
+ bool (*move)(fs_node_ptr n, struct fs_node *new_parent, const char *new_name); // TODO : not sure about this ?
bool (*create)(fs_node_ptr n, const char* name, int type); // create sub-node in directory
int (*ioctl)(fs_node_ptr n, int command, void* data);
void (*dispose)(fs_node_ptr n);
@@ -96,6 +106,20 @@ typedef struct {
} fs_driver_ops_t;
// -------------------------------------------
+// All functions that return a fs_node_t*, fs_t* or fs_handle_t* return an object
+// that will have to be dereferenced when not used anymore.
+// (on fs_handle_t*, dereferencing is like closing, only that the actual closing happens
+// when refcount falls to zero)
+
+// Internals
+
+void ref_fs_node(fs_node_t *n);
+void unref_fs_node(fs_node_t *n);
+
+fs_node_t* fs_walk_one(fs_node_t* from, const char *file);
+fs_node_t* fs_walk_path(fs_node_t* from, const char *p);
+fs_node_t* fs_walk_path_except_last(fs_node_t* from, const char *p, char* last_file_buf);
+
// Public functions
void register_fs_driver(const char* name, fs_driver_ops_t *ops);
@@ -105,9 +129,9 @@ bool fs_add_source(fs_t *fs, fs_handle_t *source);
void ref_fs(fs_t *fs);
void unref_fs(fs_t *fs);
-bool fs_delete(fs_t *fs, const char* file);
bool fs_create(fs_t *fs, const char* file, int type);
-bool fs_rename(fs_t *fs, const char* from, const char* to);
+bool fs_delete(fs_t *fs, const char* file);
+bool fs_move(fs_t *fs, const char* from, const char* to);
bool fs_stat(fs_t *fs, const char* file, stat_t *st);
int fs_ioctl(fs_t *fs, const char* file, int command, void* data);
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);
}
}