feat: open(2), close(2), mkdir(2) and readdir(2) syscall
[lunaix-os.git] / lunaix-os / kernel / fs / vfs.c
1 /**
2  * @file vfs.c
3  * @author Lunaixsky (zelong56@gmail.com)
4  * @brief Lunaix virtual file system - an abstraction layer for all file system.
5  * @version 0.1
6  * @date 2022-07-24
7  *
8  * @copyright Copyright (c) 2022
9  *
10  */
11
12 #include <klibc/string.h>
13 #include <lunaix/dirent.h>
14 #include <lunaix/foptions.h>
15 #include <lunaix/fs.h>
16 #include <lunaix/mm/cake.h>
17 #include <lunaix/mm/page.h>
18 #include <lunaix/mm/valloc.h>
19 #include <lunaix/process.h>
20 #include <lunaix/spike.h>
21 #include <lunaix/syscall.h>
22
23 #define PATH_DELIM '/'
24 #define DNODE_HASHTABLE_BITS 10
25 #define DNODE_HASHTABLE_SIZE (1 << DNODE_HASHTABLE_BITS)
26 #define DNODE_HASH_MASK (DNODE_HASHTABLE_SIZE - 1)
27 #define DNODE_HASHBITS (32 - DNODE_HASHTABLE_BITS)
28
29 static struct cake_pile* dnode_pile;
30 static struct cake_pile* inode_pile;
31 static struct cake_pile* file_pile;
32 static struct cake_pile* superblock_pile;
33 static struct cake_pile* fd_pile;
34
35 static struct v_superblock* root_sb;
36 static struct hbucket* dnode_cache;
37
38 static int fs_id = 0;
39
40 struct v_dnode*
41 vfs_d_alloc();
42
43 void
44 vfs_d_free(struct v_dnode* dnode);
45
46 struct v_superblock*
47 vfs_sb_alloc();
48
49 void
50 vfs_sb_free(struct v_superblock* sb);
51
52 void
53 vfs_init()
54 {
55     // 为他们专门创建一个蛋糕堆,而不使用valloc,这样我们可以最小化内碎片的产生
56     dnode_pile = cake_new_pile("dnode_cache", sizeof(struct v_dnode), 1, 0);
57     inode_pile = cake_new_pile("inode_cache", sizeof(struct v_inode), 1, 0);
58     file_pile = cake_new_pile("file_cache", sizeof(struct v_file), 1, 0);
59     fd_pile = cake_new_pile("fd_cache", sizeof(struct v_fd), 1, 0);
60     superblock_pile =
61       cake_new_pile("sb_cache", sizeof(struct v_superblock), 1, 0);
62
63     dnode_cache = vzalloc(DNODE_HASHTABLE_SIZE * sizeof(struct hbucket));
64
65     // 创建一个根superblock,用来蕴含我们的根目录。
66     root_sb = vfs_sb_alloc();
67     root_sb->root = vfs_d_alloc();
68 }
69
70 inline struct hbucket*
71 __dcache_get_bucket(struct v_dnode* parent, unsigned int hash)
72 {
73     // 与parent的指针值做加法,来减小碰撞的可能性。
74     hash += (uint32_t)parent;
75     // 确保低位更加随机
76     hash = hash ^ (hash >> DNODE_HASHBITS);
77     return &dnode_cache[hash & DNODE_HASH_MASK];
78 }
79
80 struct v_dnode*
81 vfs_dcache_lookup(struct v_dnode* parent, struct hstr* str)
82 {
83     struct hbucket* slot = __dcache_get_bucket(parent, str->hash);
84
85     struct v_dnode *pos, *n;
86     hashtable_bucket_foreach(slot, pos, n, hash_list)
87     {
88         if (pos->name.hash == str->hash) {
89             return pos;
90         }
91     }
92     return NULL;
93 }
94
95 void
96 vfs_dcache_add(struct v_dnode* parent, struct v_dnode* dnode)
97 {
98     struct hbucket* bucket = __dcache_get_bucket(parent, dnode->name.hash);
99     hlist_add(&bucket->head, &dnode->hash_list);
100 }
101
102 int
103 vfs_walk(struct v_dnode* start,
104          const char* path,
105          struct v_dnode** dentry,
106          struct hstr* component,
107          int walk_options)
108 {
109     int errno = 0;
110     int i = 0, j = 0;
111
112     if (path[0] == PATH_DELIM) {
113         if ((walk_options & VFS_WALK_FSRELATIVE) && start) {
114             start = start->super_block->root;
115         } else {
116             start = root_sb->root;
117         }
118         i++;
119     }
120
121     struct v_dnode* dnode;
122     struct v_dnode* current_level = start;
123
124     char name_content[VFS_NAME_MAXLEN];
125     struct hstr name = HSTR(name_content, 0);
126
127     char current = path[i++], lookahead;
128     while (current) {
129         lookahead = path[i++];
130         if (current != PATH_DELIM) {
131             if (j >= VFS_NAME_MAXLEN - 1) {
132                 return ENAMETOOLONG;
133             }
134             if (!VFS_VALID_CHAR(current)) {
135                 return VFS_EINVLD;
136             }
137             name_content[j++] = current;
138             if (lookahead) {
139                 goto cont;
140             }
141         }
142
143         // handling cases like /^.*(\/+).*$/
144         if (lookahead == PATH_DELIM) {
145             goto cont;
146         }
147
148         name_content[j] = 0;
149         hstr_rehash(&name, HSTR_FULL_HASH);
150
151         if (!lookahead && (walk_options & VFS_WALK_PARENT)) {
152             if (component) {
153                 component->hash = name.hash;
154                 component->len = j;
155                 strcpy(component->value, name_content);
156             }
157             break;
158         }
159
160         dnode = vfs_dcache_lookup(current_level, &name);
161
162         if (!dnode) {
163             dnode = vfs_d_alloc();
164             dnode->name = HSTR(valloc(VFS_NAME_MAXLEN), j);
165             dnode->name.hash = name.hash;
166
167             strcpy(dnode->name.value, name_content);
168
169             errno =
170               current_level->inode->ops.dir_lookup(current_level->inode, dnode);
171
172             if (errno == ENOENT && (walk_options & VFS_WALK_MKPARENT)) {
173                 if (!current_level->inode->ops.mkdir) {
174                     errno = ENOTSUP;
175                 } else {
176                     errno = current_level->inode->ops.mkdir(
177                       current_level->inode, dnode);
178                 }
179             }
180
181             if (errno) {
182                 goto error;
183             }
184
185             vfs_dcache_add(current_level, dnode);
186
187             dnode->parent = current_level;
188             llist_append(&current_level->children, &dnode->siblings);
189         }
190
191         j = 0;
192         current_level = dnode;
193     cont:
194         current = lookahead;
195     };
196
197     *dentry = current_level;
198     return 0;
199
200 error:
201     vfree(dnode->name.value);
202     vfs_d_free(dnode);
203     return errno;
204 }
205
206 int
207 vfs_mount(const char* target, const char* fs_name, bdev_t device)
208 {
209     int errno;
210     struct v_dnode* mnt;
211
212     if (!(errno = vfs_walk(NULL, target, &mnt, NULL, 0))) {
213         errno = vfs_mount_at(fs_name, device, mnt);
214     }
215
216     return errno;
217 }
218
219 int
220 vfs_unmount(const char* target)
221 {
222     int errno;
223     struct v_dnode* mnt;
224
225     if (!(errno = vfs_walk(NULL, target, &mnt, NULL, 0))) {
226         errno = vfs_unmount_at(mnt);
227     }
228
229     return errno;
230 }
231
232 int
233 vfs_mount_at(const char* fs_name, bdev_t device, struct v_dnode* mnt_point)
234 {
235     struct filesystem* fs = fsm_get(fs_name);
236     if (!fs)
237         return VFS_ENOFS;
238     struct v_superblock* sb = vfs_sb_alloc();
239     sb->dev = device;
240     sb->fs_id = fs_id++;
241
242     int errno = 0;
243     if (!(errno = fs->mount(sb, mnt_point))) {
244         sb->fs = fs;
245         sb->root = mnt_point;
246         mnt_point->super_block = sb;
247         llist_append(&root_sb->sb_list, &sb->sb_list);
248     }
249
250     return errno;
251 }
252
253 int
254 vfs_unmount_at(struct v_dnode* mnt_point)
255 {
256     int errno = 0;
257     struct v_superblock* sb = mnt_point->super_block;
258     if (!sb) {
259         return VFS_EBADMNT;
260     }
261     if (!(errno = sb->fs->unmount(sb))) {
262         struct v_dnode* fs_root = sb->root;
263         llist_delete(&fs_root->siblings);
264         llist_delete(&sb->sb_list);
265         vfs_sb_free(sb);
266     }
267     return errno;
268 }
269
270 int
271 vfs_open(struct v_dnode* dnode, struct v_file** file)
272 {
273     if (!dnode->inode || !dnode->inode->ops.open) {
274         return ENOTSUP;
275     }
276
277     struct v_file* vfile = cake_grab(file_pile);
278     memset(vfile, 0, sizeof(*vfile));
279
280     int errno = dnode->inode->ops.open(dnode->inode, vfile);
281     if (errno) {
282         cake_release(file_pile, vfile);
283     } else {
284         *file = vfile;
285     }
286     return errno;
287 }
288
289 int
290 vfs_close(struct v_file* file)
291 {
292     if (!file->ops.close) {
293         return ENOTSUP;
294     }
295
296     int errno = file->ops.close(file);
297     if (!errno) {
298         cake_release(file_pile, file);
299     }
300     return errno;
301 }
302
303 int
304 vfs_fsync(struct v_file* file)
305 {
306     int errno = ENOTSUP;
307     if (file->ops.sync) {
308         errno = file->ops.sync(file);
309     }
310     if (!errno && file->inode->ops.sync) {
311         return file->inode->ops.sync(file->inode);
312     }
313     return errno;
314 }
315
316 int
317 vfs_alloc_fdslot(int* fd)
318 {
319     for (size_t i = 0; i < VFS_MAX_FD; i++) {
320         if (!__current->fdtable->fds[i]) {
321             *fd = i;
322             return 0;
323         }
324     }
325     return EMFILE;
326 }
327
328 struct v_superblock*
329 vfs_sb_alloc()
330 {
331     struct v_superblock* sb = cake_grab(superblock_pile);
332     memset(sb, 0, sizeof(*sb));
333     llist_init_head(&sb->sb_list);
334     return sb;
335 }
336
337 void
338 vfs_sb_free(struct v_superblock* sb)
339 {
340     cake_release(superblock_pile, sb);
341 }
342
343 struct v_dnode*
344 vfs_d_alloc()
345 {
346     struct v_dnode* dnode = cake_grab(dnode_pile);
347     llist_init_head(&dnode->children);
348 }
349
350 void
351 vfs_d_free(struct v_dnode* dnode)
352 {
353     if (dnode->ops.destruct) {
354         dnode->ops.destruct(dnode);
355     }
356     cake_release(dnode_pile, dnode);
357 }
358
359 struct v_inode*
360 vfs_i_alloc()
361 {
362     struct v_inode* inode = cake_grab(inode_pile);
363     memset(inode, 0, sizeof(*inode));
364
365     return inode;
366 }
367
368 void
369 vfs_i_free(struct v_inode* inode)
370 {
371     cake_release(inode_pile, inode);
372 }
373
374 __DEFINE_LXSYSCALL2(int, open, const char*, path, int, options)
375 {
376     char name_str[VFS_NAME_MAXLEN];
377     struct hstr name = HSTR(name_str, 0);
378     struct v_dnode *dentry, *file;
379     int errno, fd;
380     if ((errno = vfs_walk(NULL, path, &dentry, &name, VFS_WALK_PARENT))) {
381         return -1;
382     }
383
384     struct v_file* opened_file = 0;
385     if (!(file = vfs_dcache_lookup(dentry, &name)) && (options & FO_CREATE)) {
386         errno = dentry->inode->ops.create(dentry->inode, opened_file);
387     } else if (!file) {
388         errno = EEXIST;
389     } else {
390         errno = vfs_open(file, &opened_file);
391     }
392
393     __current->k_status = errno;
394
395     if (!errno && !(errno = vfs_alloc_fdslot(&fd))) {
396         struct v_fd* fd_s = vzalloc(sizeof(*fd_s));
397         fd_s->file = opened_file;
398         fd_s->pos = file->inode->fsize & -((options & FO_APPEND) == 0);
399         __current->fdtable->fds[fd] = fd_s;
400     }
401
402     return SYSCALL_ESTATUS(errno);
403 }
404
405 __DEFINE_LXSYSCALL1(int, close, int, fd)
406 {
407     struct v_fd* fd_s;
408     int errno;
409     if (fd < 0 || fd >= VFS_MAX_FD || !(fd_s = __current->fdtable->fds[fd])) {
410         errno = EBADF;
411     } else if (!(errno = vfs_close(fd_s->file))) {
412         vfree(fd_s);
413     }
414
415     __current->k_status = errno;
416
417     return SYSCALL_ESTATUS(errno);
418 }
419
420 void
421 __vfs_readdir_callback(struct dir_context* dctx,
422                        const char* name,
423                        const int len,
424                        const int dtype)
425 {
426     struct dirent* dent = (struct dirent*)dctx->cb_data;
427     strcpy(dent->d_name, name);
428     dent->d_nlen = len;
429     dent->d_type = dtype;
430 }
431
432 __DEFINE_LXSYSCALL2(int, readdir, int, fd, struct dirent*, dent)
433 {
434     struct v_fd* fd_s;
435     int errno;
436     if (fd < 0 || fd >= VFS_MAX_FD || !(fd_s = __current->fdtable->fds[fd])) {
437         errno = EBADF;
438     } else if (!(fd_s->file->inode->itype & VFS_INODE_TYPE_DIR)) {
439         errno = ENOTDIR;
440     } else {
441         struct dir_context dctx =
442           (struct dir_context){ .cb_data = dent,
443                                 .index = dent->d_offset,
444                                 .read_complete_callback =
445                                   __vfs_readdir_callback };
446         if (!(errno = fd_s->file->ops.readdir(fd_s->file, &dctx))) {
447             dent->d_offset++;
448         }
449     }
450
451     __current->k_status = errno;
452     return SYSCALL_ESTATUS(errno);
453 }
454
455 __DEFINE_LXSYSCALL1(int, mkdir, const char*, path)
456 {
457     struct v_dnode *parent, *dir;
458     struct hstr component = HSTR(valloc(VFS_NAME_MAXLEN), 0);
459     int errno =
460       vfs_walk(root_sb->root, path, &parent, &component, VFS_WALK_PARENT);
461     if (errno) {
462         goto done;
463     }
464
465     if (!parent->inode->ops.mkdir) {
466         errno = ENOTSUP;
467     } else if (!(parent->inode->itype & VFS_INODE_TYPE_DIR)) {
468         errno = ENOTDIR;
469     } else {
470         dir = vfs_d_alloc();
471         dir->name = component;
472         if (!(errno = parent->inode->ops.mkdir(parent->inode, dir))) {
473             llist_append(&parent->children, &dir->siblings);
474         } else {
475             vfs_d_free(dir);
476             vfree(component.value);
477         }
478     }
479
480 done:
481     __current->k_status = errno;
482     return SYSCALL_ESTATUS(errno);
483 }