aboutsummaryrefslogtreecommitdiff
path: root/src/kernel/include/vfs.h
blob: 37b28b9c412238aad98487b512cffd52b2f6ad72 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#pragma once

#include <stdbool.h>
#include <malloc.h>

#include <hashtbl.h>
#include <mutex.h>

#include <fs.h> 		// common header

// How to use :
// - When using a filesystem : never call the operations in fs_*_ops_t directly, use
// 		the functions defined bellow in section "public functions";
//		Users of the VFS should not manipulate directly fs_node_t items, only fs_t and fs_handle_t
// - When programming a filesystem : don't worry about allocating the fs_handle_t and fs_t,
// 		it is done automatically
// - The three types defined below (filesystem, fs node, file handle) are reference-counters to
//		some data managed by the underlying filesystem. The following types are aliases to void*,
//		but are used to disambiguate the different types of void* : fs_handle_ptr, fs_node_ptr, fs_ptr

// About thread safety :
// - The VFS implements a locking mechanism on handles so that only one operation is executed
//		at the same time on a given handle
// - Same for FS nodes
// - The VFS does not lock nodes that have a handle open to them : it is the FS code's responsibility
//		to refuse some commands if neccessary on a node that is open.
// - The VFS does not implement any locking mechanism on filesystems themselves (e.g. the add_source
//		implementation must have its own locking system)

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

typedef struct {
	size_t (*read)(fs_handle_ptr f, size_t offset, size_t len, char* buf);
	size_t (*write)(fs_handle_ptr f, size_t offset, size_t len, const char* buf);
	bool (*readdir)(fs_handle_ptr f, dirent_t *d);
	void (*close)(fs_handle_ptr f);
} fs_handle_ops_t;

typedef struct fs_handle {
	// These field are filled by the VFS's generic open() code
	struct fs *fs;
	struct fs_node *node;

	int refs;
	mutex_t lock;

	// These fields are filled by the FS's specific open() code
	fs_handle_ops_t *ops;
	fs_handle_ptr data;
	int mode;
} fs_handle_t;

// -------------------------------------------
// Structure defining a filesystem node
// In the future this is where FS-level cache may be implemented : calls to dispose() are not
// executed immediately when refcount falls to zero but only when we need to free memory
// Remarks :
//  - fs_node_t not to be used in public interface
//  - nodes keep a reference to their parent
//  - 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
//  - delete() is expected to unlink the node from its parent (make it inaccessible) and delete
//		the corresponding data. It is guaranteed that delete() is never called on a node that
//		is currently in use. (different from posix semantics !)
//	- move() may be called on a node that is currently in use
//  - 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
//	- delete() is not expected to delete recursively : it should fail on a non-empty directory

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, const char* file);
	bool (*move)(fs_node_ptr dir, const char* old_name, struct fs_node *new_parent, const char *new_name);
	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);
} fs_node_ops_t;

typedef struct fs_node {
	// These fields are filled by the VFS's generic walk() code
	int refs;
	mutex_t lock;
	
	char* name;				// name in parent
	struct fs *fs;
	hashtbl_t *children;	// not all children, only those in memory
	struct fs_node *parent;

	// These fields are filled by the FS's specific walk() code
	fs_node_ops_t *ops;
	fs_node_ptr data;
} fs_node_t;

// -------------------------------------------
// Structure defining a filesystem

typedef struct {
	bool (*add_source)(fs_ptr fs, fs_handle_t* source, const char* opts);
	void (*shutdown)(fs_ptr fs);
} fs_ops_t;

typedef struct fs {
	// Filled by VFS's make_fs()
	int refs;
	// Filled by FS's specific make()
	fs_ops_t *ops;
	fs_ptr data;
	// Filled by both according to what is specified for fs_node_t
	fs_node_t root;
} fs_t;

// -------------------------------------------
// Structure defining a filesystem driver

typedef struct {
	bool (*make)(fs_handle_t *source, const char* opts, fs_t *d);
	bool (*detect)(fs_handle_t *source);
} 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);

fs_t* make_fs(const char* driver, fs_handle_t *source, const char* opts);
bool fs_add_source(fs_t *fs, fs_handle_t *source, const char* opts);
void ref_fs(fs_t *fs);
void unref_fs(fs_t *fs);

bool fs_create(fs_t *fs, const char* file, int type);
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);

fs_handle_t* fs_open(fs_t *fs, const char* file, int mode);
void ref_file(fs_handle_t *file);
void unref_file(fs_handle_t *file);
int file_get_mode(fs_handle_t *f);
size_t file_read(fs_handle_t *f, size_t offset, size_t len, char* buf);
size_t file_write(fs_handle_t *f, size_t offset, size_t len, const char* buf);
bool file_stat(fs_handle_t *f, stat_t *st);
bool file_readdir(fs_handle_t *f, dirent_t *d);

/* vim: set ts=4 sw=4 tw=0 noet :*/