MOS Source Code
Loading...
Searching...
No Matches
vfs.c
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/assert.h"
4#include "mos/device/timer.h"
10#include "mos/mm/mm.h"
11#include "mos/mm/mmstat.h"
12#include "mos/mm/physical/pmm.h"
14
15#include <dirent.h>
16#include <errno.h>
19#include <mos/filesystem/vfs.h>
21#include <mos/io/io.h>
25#include <mos/mos_global.h>
27#include <mos/syslog/printk.h>
28#include <mos/tasks/process.h>
29#include <mos/types.h>
30#include <mos_stdlib.h>
31#include <mos_string.h>
32
35
37
39
43
44static long do_pagecache_flush(file_t *file, off_t pgoff, size_t npages)
45{
46 pr_dinfo2(vfs, "vfs: flushing page cache for file %pio", (void *) &file->io);
47
49 long ret = 0;
50 if (pgoff == 0 && npages == (size_t) -1)
51 ret = pagecache_flush_or_drop_all(&file->dentry->inode->cache, false);
52 else
53 ret = pagecache_flush_or_drop(&file->dentry->inode->cache, pgoff, npages, false);
54
56 return ret;
57}
58
59static long do_sync_inode(file_t *file)
60{
61 const superblock_ops_t *ops = file->dentry->inode->superblock->ops;
62 if (ops && ops->sync_inode)
63 return ops->sync_inode(file->dentry->inode);
64
65 return 0;
66}
67
68// BEGIN: filesystem's io_t operations
69static void vfs_io_ops_close(io_t *io)
70{
71 file_t *file = container_of(io, file_t, io);
72 if (io->type == IO_FILE && io->flags & IO_WRITABLE) // only flush if the file is writable
73 {
74 do_pagecache_flush(file, 0, (off_t) -1);
75 do_sync_inode(file);
76 }
77
78 dentry_unref(file->dentry);
79
80 if (io->type == IO_FILE)
81 {
82 const file_ops_t *file_ops = file_get_ops(file);
83 if (file_ops)
84 {
85 if (file_ops->release)
86 file_ops->release(file);
87 }
88 }
89
90 kfree(file);
91}
92
93static void vfs_io_ops_close_dir(io_t *io)
94{
95 file_t *file = container_of(io, file_t, io);
96
97 if (file->private_data)
98 {
99 vfs_listdir_state_t *state = file->private_data;
101 {
102 list_remove(entry);
103 kfree(entry->name);
104 kfree(entry);
105 }
106
107 kfree(state);
108 file->private_data = NULL;
109 }
110
111 vfs_io_ops_close(io); // close the file
112}
113
114static size_t vfs_io_ops_read(io_t *io, void *buf, size_t count)
115{
116 file_t *file = container_of(io, file_t, io);
117 const file_ops_t *const file_ops = file_get_ops(file);
118 if (!file_ops || !file_ops->read)
119 return 0;
120
122 size_t ret = file_ops->read(file, buf, count, file->offset);
123 if (IS_ERR_VALUE(ret))
124 ; // do nothing
125 else if (ret != (size_t) -1)
126 file->offset += ret;
128
129 return ret;
130}
131
132static size_t vfs_io_ops_write(io_t *io, const void *buf, size_t count)
133{
134 file_t *file = container_of(io, file_t, io);
135 const file_ops_t *const file_ops = file_get_ops(file);
136 if (!file_ops || !file_ops->write)
137 return 0;
138
140 size_t ret = file_ops->write(file, buf, count, file->offset);
141 if (!IS_ERR_VALUE(ret))
142 file->offset += ret;
144 return ret;
145}
146
148{
149 file_t *file = container_of(io, file_t, io);
150
151 const file_ops_t *const ops = file_get_ops(file);
152 if (ops->seek)
153 return ops->seek(file, offset, whence); // use the filesystem's lseek if it exists
154
156
157 switch (whence)
158 {
159 case IO_SEEK_SET:
160 {
161 file->offset = MAX(offset, 0);
162 break;
163 }
164 case IO_SEEK_CURRENT:
165 {
166 off_t new_offset = file->offset + offset;
167 new_offset = MAX(new_offset, 0);
168 file->offset = new_offset;
169 break;
170 }
171 case IO_SEEK_END:
172 {
173 off_t new_offset = file->dentry->inode->size + offset;
174 new_offset = MAX(new_offset, 0);
175 file->offset = new_offset;
176 break;
177 }
178 case IO_SEEK_DATA: mos_warn("vfs: IO_SEEK_DATA is not supported"); break;
179 case IO_SEEK_HOLE: mos_warn("vfs: IO_SEEK_HOLE is not supported"); break;
180 };
181
183 return file->offset;
184}
185
187{
188 MOS_ASSERT(vmap->io);
189 file_t *file = container_of(vmap->io, file_t, io);
190 const size_t fault_pgoffset = (vmap->io_offset + ALIGN_DOWN_TO_PAGE(fault_addr) - vmap->vaddr) / MOS_PAGE_SIZE;
191
192 mutex_acquire(&file->dentry->inode->cache.lock); // lock the inode cache
193 phyframe_t *const pagecache_page = pagecache_get_page_for_read(&file->dentry->inode->cache, fault_pgoffset);
195
196 if (IS_ERR(pagecache_page))
198
199 // ! mm subsystem has verified that this vmap can be written to, but in the page table it's marked as read-only
200 // * currently, only CoW pages have this property, we treat this as a CoW page
201 if (info->is_present && info->is_write)
202 {
203 if (pagecache_page == info->faulting_page)
204 vmap_stat_dec(vmap, pagecache); // the faulting page is a pagecache page
205 else
206 vmap_stat_dec(vmap, cow); // the faulting page is a COW page
207 vmap_stat_inc(vmap, regular);
208 return mm_resolve_cow_fault(vmap, fault_addr, info); // resolve by copying data page into prevate page
209 }
210
211 info->backing_page = pagecache_page;
212 if (vmap->type == VMAP_TYPE_PRIVATE)
213 {
214 if (info->is_write)
215 {
216 vmap_stat_inc(vmap, regular);
217 // present pages are handled above
218 MOS_ASSERT(!info->is_present);
219 return VMFAULT_COPY_BACKING_PAGE; // copy and (also) map the backing page
220 }
221 else
222 {
223 vmap_stat_inc(vmap, pagecache);
224 vmap_stat_inc(vmap, cow);
226 }
227 }
228 else
229 {
230 vmap_stat_inc(vmap, pagecache);
231 vmap_stat_inc(vmap, regular);
233 }
234}
235
236static bool vfs_io_ops_mmap(io_t *io, vmap_t *vmap, off_t offset)
237{
238 file_t *file = container_of(io, file_t, io);
239 const file_ops_t *const file_ops = file_get_ops(file);
240
241 MOS_ASSERT(!vmap->on_fault); // there should be no fault handler set
243
244 if (file_ops->mmap)
245 return file_ops->mmap(file, vmap, offset);
246
247 return true;
248}
249
250static bool vfs_io_ops_munmap(io_t *io, vmap_t *vmap, bool *unmapped)
251{
252 file_t *file = container_of(io, file_t, io);
253 const file_ops_t *const file_ops = file_get_ops(file);
254
255 if (file_ops->munmap)
256 return file_ops->munmap(file, vmap, unmapped);
257
258 return true;
259}
260
261static void vfs_io_ops_getname(const io_t *io, char *buf, size_t size)
262{
263 const file_t *file = container_of(io, file_t, io);
264 dentry_path(file->dentry, root_dentry, buf, size);
265}
266
267static const io_op_t file_io_ops = {
268 .read = vfs_io_ops_read,
269 .write = vfs_io_ops_write,
270 .close = vfs_io_ops_close,
271 .seek = vfs_io_ops_seek,
272 .mmap = vfs_io_ops_mmap,
273 .munmap = vfs_io_ops_munmap,
274 .get_name = vfs_io_ops_getname,
275};
276
277static const io_op_t dir_io_ops = {
278 .read = vfs_list_dir,
279 .close = vfs_io_ops_close_dir,
280 .get_name = vfs_io_ops_getname,
281};
282
283// END: filesystem's io_t operations
284
285static void vfs_flusher_entry(void *arg)
286{
287 MOS_UNUSED(arg);
288 while (true)
289 {
290 timer_msleep(10 * 1000);
291 // pagecache_flush_all();
292 }
293}
294
295static void vfs_flusher_init(void)
296{
297 // kthread_create(vfs_flusher_entry, NULL, "vfs_flusher");
298}
300
301static void vfs_copy_stat(file_stat_t *statbuf, inode_t *inode)
302{
303 statbuf->ino = inode->ino;
304 statbuf->type = inode->type;
305 statbuf->perm = inode->perm;
306 statbuf->size = inode->size;
307 statbuf->uid = inode->uid;
308 statbuf->gid = inode->gid;
309 statbuf->sticky = inode->sticky;
310 statbuf->suid = inode->suid;
311 statbuf->sgid = inode->sgid;
312 statbuf->nlinks = inode->nlinks;
313 statbuf->accessed = inode->accessed;
314 statbuf->modified = inode->modified;
315 statbuf->created = inode->created;
316}
317
319{
320 filesystem_t *fs_found = NULL;
323 {
324 if (strcmp(fs->name, name) == 0)
325 {
326 fs_found = fs;
327 break;
328 }
329 }
331 return fs_found;
332}
333
334static bool vfs_verify_permissions(dentry_t *file_dentry, bool open, bool read, bool create, bool execute, bool write)
335{
336 MOS_ASSERT(file_dentry && file_dentry->inode);
337 const file_perm_t file_perm = file_dentry->inode->perm;
338
339 // TODO: we are treating all users as root for now, only checks for execute permission
340 MOS_UNUSED(open);
341 MOS_UNUSED(read);
342 MOS_UNUSED(create);
343 MOS_UNUSED(write);
344
345 if (execute && !(file_perm & PERM_EXEC))
346 return false; // execute permission denied
347
348 return true;
349}
350
351static file_t *vfs_do_open(dentry_t *base, const char *path, open_flags flags)
352{
353 if (base == NULL)
354 return NULL;
355
356 const bool may_create = flags & OPEN_CREATE;
357 const bool read = flags & OPEN_READ;
358 const bool write = flags & OPEN_WRITE;
359 const bool exec = flags & OPEN_EXECUTE;
360 const bool no_follow = flags & OPEN_NO_FOLLOW;
361 const bool expect_dir = flags & OPEN_DIR;
362 const bool truncate = flags & OPEN_TRUNCATE;
363
365 (no_follow ? RESOLVE_SYMLINK_NOFOLLOW : 0) | //
367 (expect_dir ? RESOLVE_EXPECT_DIR : 0);
368 dentry_t *entry = dentry_resolve(base, root_dentry, path, resolve_flags);
369 if (IS_ERR(entry))
370 {
371 pr_dinfo2(vfs, "failed to resolve '%s': create=%d, r=%d, x=%d, nofollow=%d, dir=%d, truncate=%d", path, may_create, read, exec, no_follow, expect_dir, truncate);
372 return ERR(entry);
373 }
374
375 bool created = false;
376
377 if (may_create && entry->inode == NULL)
378 {
379 dentry_t *parent = dentry_parent(entry);
380 if (!parent->inode->ops->newfile)
381 {
382 dentry_unref(entry);
383 return ERR_PTR(-EROFS);
384 }
385
386 if (!parent->inode->ops->newfile(parent->inode, entry, FILE_TYPE_REGULAR, 0666))
387 {
388 dentry_unref(entry);
389 return ERR_PTR(-EIO); // failed to create file
390 }
391
392 created = true;
393 }
394
395 if (!vfs_verify_permissions(entry, true, read, may_create, exec, write))
396 {
397 dentry_unref(entry);
398 return ERR_PTR(-EACCES);
399 }
400
401 file_t *file = vfs_do_open_dentry(entry, created, read, write, exec, truncate);
402 if (IS_ERR(file))
403 {
404 kfree(file);
405 dentry_unref(entry);
406 return ERR(file);
407 }
408
409 return file;
410}
411
412// public functions
413file_t *vfs_do_open_dentry(dentry_t *entry, bool created, bool read, bool write, bool exec, bool truncate)
414{
415 MOS_ASSERT(entry->inode);
416 MOS_UNUSED(truncate);
417
418 file_t *file = kmalloc(file_cache);
419 file->dentry = entry;
420
421 io_flags_t io_flags = IO_SEEKABLE;
422
423 if (read)
424 io_flags |= IO_READABLE;
425
426 if (write)
427 io_flags |= IO_WRITABLE;
428
429 if (exec)
430 io_flags |= IO_EXECUTABLE;
431
432 // only regular files are mmapable
433 if (entry->inode->type == FILE_TYPE_REGULAR)
434 io_flags |= IO_MMAPABLE;
435
436 if (file->dentry->inode->type == FILE_TYPE_DIRECTORY)
437 io_init(&file->io, IO_DIR, (io_flags | IO_READABLE) & ~IO_SEEKABLE, &dir_io_ops);
438 else
439 io_init(&file->io, IO_FILE, io_flags, &file_io_ops);
440
441 const file_ops_t *ops = file_get_ops(file);
442 if (ops && ops->open)
443 {
444 bool opened = ops->open(file->dentry->inode, file, created);
445 if (!opened)
446 return ERR_PTR(-ENOTSUP);
447 }
448
449 return file;
450}
451
453{
454 if (vfs_find_filesystem(fs->name))
455 mos_panic("filesystem '%s' already registered", fs->name);
456
458
462
463 pr_dinfo2(vfs, "filesystem '%s' registered", fs->name);
464}
465
466long vfs_mount(const char *device, const char *path, const char *fs, const char *options)
467{
468 filesystem_t *real_fs = vfs_find_filesystem(fs);
469 if (unlikely(real_fs == NULL))
470 {
471 mos_warn("filesystem '%s' not found", fs);
472 return -EINVAL;
473 }
474
475 MOS_ASSERT_X(real_fs->mount, "filesystem '%s' does not support mounting", real_fs->name);
476
477 if (unlikely(strcmp(path, "/") == 0))
478 {
479 // special case: mount root filesystem
480 if (root_dentry)
481 {
482 pr_warn("root filesystem is already mounted");
483 return -EBUSY;
484 }
485 pr_dinfo2(vfs, "mounting root filesystem '%s'...", fs);
486 root_dentry = real_fs->mount(real_fs, device, options);
487 if (root_dentry == NULL)
488 {
489 mos_warn("failed to mount root filesystem");
490 return -EIO;
491 }
492 pr_dinfo2(vfs, "root filesystem mounted, dentry=%p", (void *) root_dentry);
493
495 bool mounted = dentry_mount(root_dentry, root_dentry, real_fs);
496 MOS_ASSERT(mounted);
497
498 return 0;
499 }
500
501 dentry_t *base = path_is_absolute(path) ? root_dentry : dentry_from_fd(AT_FDCWD);
503 if (IS_ERR(mountpoint))
504 return PTR_ERR(mountpoint);
505
506 if (mountpoint->is_mountpoint)
507 {
508 // we don't support overlaying filesystems yet
509 mos_warn("mount point is already mounted");
510 dentry_unref(mountpoint);
511 return -ENOTSUP;
512 }
513
514 // when mounting:
515 // mounted_root will have a reference of 1
516 // the mount_point will have its reference incremented by 1
517 dentry_t *mounted_root = real_fs->mount(real_fs, device, options);
518 if (IS_ERR(mounted_root))
519 {
520 mos_warn("failed to mount filesystem");
521 return PTR_ERR(mounted_root);
522 }
523
524 const bool mounted = dentry_mount(mountpoint, mounted_root, real_fs);
525 if (unlikely(!mounted))
526 {
527 mos_warn("failed to mount filesystem");
528 return -EIO;
529 }
530
531 MOS_ASSERT_X(mountpoint->refcount == mounted_root->refcount, "mountpoint refcount=%zu, mounted_root refcount=%zu", mountpoint->refcount, mounted_root->refcount);
532 pr_dinfo2(vfs, "mounted filesystem '%s' on '%s'", fs, path);
533 return 0;
534}
535
536long vfs_unmount(const char *path)
537{
539 if (IS_ERR(mounted_root))
540 return PTR_ERR(mounted_root);
541
542 // the mounted root itself holds a ref, and the caller of this function
543 if (mounted_root->refcount != 2)
544 {
545 dentry_check_refstat(mounted_root);
546 mos_warn("refcount is not as expected");
547 return -EBUSY;
548 }
549
550 dentry_unref(mounted_root); // release the reference held by this function
551
552 // unmounting root filesystem
553 dentry_t *mountpoint = dentry_unmount(mounted_root);
554 if (!mountpoint)
555 {
556 mos_warn("failed to unmount filesystem");
557 return -EIO;
558 }
559
560 MOS_ASSERT(mounted_root->refcount == mountpoint->refcount && mountpoint->refcount == 1);
561 if (mounted_root->superblock->fs->unmount)
562 mounted_root->superblock->fs->unmount(mounted_root->superblock->fs, mounted_root);
563 else
565 MOS_ASSERT_X(mounted_root->refcount == 0, "fs->umount should release the last reference to the mounted root");
566
567 if (mounted_root == root_dentry)
568 {
569 pr_info2("unmounted root filesystem");
571 return 0;
572 }
573
574 dentry_unref(mountpoint);
575 return 0;
576}
577
578file_t *vfs_openat(int fd, const char *path, open_flags flags)
579{
580 pr_dinfo2(vfs, "vfs_openat(fd=%d, path='%s', flags=%x)", fd, path, flags);
581 dentry_t *basedir = path_is_absolute(path) ? root_dentry : dentry_from_fd(fd);
582 if (IS_ERR(basedir))
583 return ERR(basedir);
584 file_t *file = vfs_do_open(basedir, path, flags);
585 return file;
586}
587
588long vfs_fstatat(fd_t fd, const char *path, file_stat_t *restrict statbuf, fstatat_flags flags)
589{
590 if (flags & FSTATAT_FILE)
591 {
592 pr_dinfo2(vfs, "vfs_fstatat(fd=%d, path='%p', stat=%p, flags=%x)", fd, (void *) path, (void *) statbuf, flags);
594 if (!(io_valid(io) && (io->type == IO_FILE || io->type == IO_DIR)))
595 return -EBADF; // io is closed, or is not a file or directory
596
597 file_t *file = container_of(io, file_t, io);
598 MOS_ASSERT(file);
599 if (statbuf)
600 vfs_copy_stat(statbuf, file->dentry->inode);
601
602 return 0;
603 }
604
605 pr_dinfo2(vfs, "vfs_fstatat(fd=%d, path='%s', stat=%p, flags=%x)", fd, path, (void *) statbuf, flags);
606 dentry_t *basedir = path_is_absolute(path) ? root_dentry : dentry_from_fd(fd);
607 if (IS_ERR(basedir))
608 return PTR_ERR(basedir);
610 if (flags & FSTATAT_NOFOLLOW)
611 resolve_flags |= RESOLVE_SYMLINK_NOFOLLOW;
612
613 dentry_t *dentry = dentry_resolve(basedir, root_dentry, path, resolve_flags);
614 if (IS_ERR(dentry))
615 return PTR_ERR(dentry);
616
617 if (statbuf)
618 vfs_copy_stat(statbuf, dentry->inode);
619 dentry_unref(dentry);
620 return 0;
621}
622
623size_t vfs_readlinkat(fd_t dirfd, const char *path, char *buf, size_t size)
624{
625 dentry_t *base = path_is_absolute(path) ? root_dentry : dentry_from_fd(dirfd);
627 if (IS_ERR(dentry))
628 return PTR_ERR(dentry);
629
630 if (dentry->inode->type != FILE_TYPE_SYMLINK)
631 {
632 dentry_unref(dentry);
633 return -EINVAL;
634 }
635
636 const size_t len = dentry->inode->ops->readlink(dentry, buf, size);
637
638 dentry_unref(dentry);
639
640 if (len >= size) // buffer too small
641 return -ENAMETOOLONG;
642
643 return len;
644}
645
646long vfs_symlink(const char *path, const char *target)
647{
648 pr_dinfo2(vfs, "vfs_symlink(path='%s', target='%s')", path, target);
649 dentry_t *base = path_is_absolute(path) ? root_dentry : dentry_from_fd(AT_FDCWD);
651 if (IS_ERR(dentry))
652 return PTR_ERR(dentry);
653
654 dentry_t *parent_dir = dentry_parent(dentry);
655 const bool created = parent_dir->inode->ops->symlink(parent_dir->inode, dentry, target);
656
657 if (!created)
658 mos_warn("failed to create symlink '%s'", path);
659
660 dentry_unref(dentry);
661 return created ? 0 : -EIO;
662}
663
664long vfs_mkdir(const char *path)
665{
666 pr_dinfo2(vfs, "vfs_mkdir('%s')", path);
667 dentry_t *base = path_is_absolute(path) ? root_dentry : dentry_from_fd(AT_FDCWD);
669 if (IS_ERR(dentry))
670 return PTR_ERR(dentry);
671
672 dentry_t *parent_dir = dentry_parent(dentry);
673 if (parent_dir->inode == NULL || parent_dir->inode->ops == NULL || parent_dir->inode->ops->mkdir == NULL)
674 {
675 dentry_unref(dentry);
676 return false;
677 }
678
679 // TODO: use umask or something else
680 const bool created = parent_dir->inode->ops->mkdir(parent_dir->inode, dentry, parent_dir->inode->perm);
681
682 if (!created)
683 mos_warn("failed to create directory '%s'", path);
684
685 dentry_unref(dentry);
686 return created ? 0 : -EIO;
687}
688
689long vfs_rmdir(const char *path)
690{
691 pr_dinfo2(vfs, "vfs_rmdir('%s')", path);
692 dentry_t *base = path_is_absolute(path) ? root_dentry : dentry_from_fd(AT_FDCWD);
694 if (IS_ERR(dentry))
695 return PTR_ERR(dentry);
696
697 dentry_t *parent_dir = dentry_parent(dentry);
698 if (parent_dir->inode == NULL || parent_dir->inode->ops == NULL || parent_dir->inode->ops->rmdir == NULL)
699 {
700 dentry_unref(dentry);
701 return -ENOTSUP;
702 }
703
704 const bool removed = parent_dir->inode->ops->rmdir(parent_dir->inode, dentry);
705
706 if (!removed)
707 mos_warn("failed to remove directory '%s'", path);
708
709 dentry_unref(dentry);
710 return removed ? 0 : -EIO;
711}
712
713size_t vfs_list_dir(io_t *io, void *user_buf, size_t user_size)
714{
715 pr_dinfo2(vfs, "vfs_list_dir(io=%p, buf=%p, size=%zu)", (void *) io, (void *) user_buf, user_size);
716 file_t *file = container_of(io, file_t, io);
718 {
719 mos_warn("not a directory");
720 return 0;
721 }
722
723 if (file->private_data == NULL)
724 {
725 vfs_listdir_state_t *const state = file->private_data = kmalloc(sizeof(vfs_listdir_state_t));
726 linked_list_init(&state->entries);
727 state->n_count = state->read_offset = 0;
728 vfs_populate_listdir_buf(file->dentry, state);
729 }
730
731 vfs_listdir_state_t *const state = file->private_data;
732
733 if (state->read_offset >= state->n_count)
734 return 0; // no more entries
735
736 size_t bytes_copied = 0;
737 size_t i = 0;
739 {
740 if (i++ < state->read_offset)
741 continue; // skip the entries we have already read
742
743 if (state->read_offset >= state->n_count)
744 break;
745
746 const size_t entry_size = sizeof(ino_t) + sizeof(off_t) + sizeof(short) + sizeof(char) + entry->name_len + 1; // +1 for the null terminator
747 if (bytes_copied + entry_size > user_size)
748 break;
749
750 struct dirent *dirent = (struct dirent *) (((char *) user_buf) + bytes_copied);
751 dirent->d_ino = entry->ino;
752 dirent->d_type = entry->type;
753 dirent->d_reclen = entry_size;
754 dirent->d_off = entry_size - 1;
755 memcpy(dirent->d_name, entry->name, entry->name_len);
756 dirent->d_name[entry->name_len] = '\0';
757 bytes_copied += entry_size;
758 state->read_offset++;
759 }
760
761 return bytes_copied;
762}
763
764long vfs_chdirat(fd_t dirfd, const char *path)
765{
766 pr_dinfo2(vfs, "vfs_chdir('%s')", path);
767 dentry_t *base = path_is_absolute(path) ? root_dentry : dentry_from_fd(dirfd);
769 if (IS_ERR(dentry))
770 return PTR_ERR(dentry);
771
772 dentry_t *old_cwd = dentry_from_fd(AT_FDCWD);
773 if (old_cwd)
774 dentry_unref(old_cwd);
775
776 current_process->working_directory = dentry;
777 return 0;
778}
779
780ssize_t vfs_getcwd(char *buf, size_t size)
781{
782 dentry_t *cwd = dentry_from_fd(AT_FDCWD);
783 if (IS_ERR(cwd))
784 return PTR_ERR(cwd);
785
786 return dentry_path(cwd, root_dentry, buf, size);
787}
788
789long vfs_fchmodat(fd_t fd, const char *path, int perm, int flags)
790{
791 pr_dinfo2(vfs, "vfs_fchmodat(fd=%d, path='%s', perm=%o, flags=%x)", fd, path, perm, flags);
794 if (IS_ERR(dentry))
795 return PTR_ERR(dentry);
796
797 // TODO: check if the underlying filesystem supports chmod, and is not read-only
798 dentry->inode->perm = perm;
799 dentry_unref(dentry);
800 return 0;
801}
802
803long vfs_unlinkat(fd_t dirfd, const char *path)
804{
805 pr_dinfo2(vfs, "vfs_unlinkat(dirfd=%d, path='%s')", dirfd, path);
806 dentry_t *base = path_is_absolute(path) ? root_dentry : dentry_from_fd(dirfd);
808 if (IS_ERR(dentry))
809 return PTR_ERR(dentry);
810
811 dentry_t *parent_dir = dentry_parent(dentry);
812 if (parent_dir->inode == NULL || parent_dir->inode->ops == NULL || parent_dir->inode->ops->unlink == NULL)
813 {
814 dentry_unref(dentry);
815 return -ENOTSUP;
816 }
817
818 if (!inode_unlink(parent_dir->inode, dentry))
819 {
820 dentry_unref(dentry);
821 return -EIO;
822 }
823
824 dentry_unref(dentry); // it won't release dentry because dentry->inode is still valid
825 dentry_detach(dentry);
826 dentry_try_release(dentry);
827 return 0;
828}
829
830long vfs_fsync(io_t *io, bool sync_metadata, off_t start, off_t end)
831{
832 pr_dinfo2(vfs, "vfs_fsync(io=%p, sync_metadata=%d, start=%ld, end=%ld)", (void *) io, sync_metadata, start, end);
833 file_t *file = container_of(io, file_t, io);
834
835 const off_t nbytes = end - start;
836 const off_t npages = ALIGN_UP_TO_PAGE(nbytes) / MOS_PAGE_SIZE;
837 const off_t pgoffset = start / MOS_PAGE_SIZE;
838
839 long ret = do_pagecache_flush(file, pgoffset, npages);
840 if (ret < 0)
841 return ret;
842
843 if (sync_metadata)
844 {
845 ret = do_sync_inode(file);
846 if (ret < 0)
847 return ret;
848 }
849
850 return ret;
851}
852
853// ! sysfs support
854
856{
858 {
859 sysfs_printf(f, "%s\n", fs->name);
860 }
861
862 return true;
863}
864
866{
867 char pathbuf[MOS_PATH_MAX_LENGTH];
869 {
870 dentry_path(mp->mountpoint, root_dentry, pathbuf, sizeof(pathbuf));
871 sysfs_printf(f, "%-20s %-10s\n", pathbuf, mp->fs->name);
872 }
873
874 return true;
875}
876
877static void vfs_sysfs_dentry_stats_stat_receiver(int depth, const dentry_t *dentry, bool mountroot, void *data)
878{
879 sysfs_file_t *file = data;
880 sysfs_printf(file, "%*s%s: refcount=%zu%s\n", //
881 depth * 4, //
882 "", //
883 dentry_name(dentry), //
884 dentry->refcount, //
885 mountroot ? " (mount root)" : (dentry->is_mountpoint ? " (mountpoint)" : "") //
886 );
887}
888
894
900
#define MOS_ASSERT_X(cond, msg,...)
Definition assert.h:15
#define MOS_ASSERT(cond)
Definition assert.h:14
#define mos_warn(fmt,...)
Definition assert.h:23
#define MOS_PATH_MAX_LENGTH
Definition autoconf.h:27
#define MOS_PAGE_SIZE
Definition autoconf.h:6
void dentry_dump_refstat(const dentry_t *dentry, dump_refstat_receiver_t *receiver, void *receiver_data)
fstatat_flags
Definition fs_types.h:40
@ FSTATAT_FILE
Definition fs_types.h:43
@ FSTATAT_NOFOLLOW
Definition fs_types.h:42
u16 file_perm_t
Definition fs_types.h:52
open_flags
Definition fs_types.h:26
@ OPEN_READ
Definition fs_types.h:28
@ OPEN_NO_FOLLOW
Definition fs_types.h:31
@ OPEN_TRUNCATE
Definition fs_types.h:33
@ OPEN_WRITE
Definition fs_types.h:29
@ OPEN_DIR
Definition fs_types.h:34
@ OPEN_CREATE
Definition fs_types.h:32
@ OPEN_EXECUTE
Definition fs_types.h:30
#define PERM_EXEC
Definition fs_types.h:59
@ FILE_TYPE_REGULAR
Definition fs_types.h:15
@ FILE_TYPE_SYMLINK
Definition fs_types.h:17
@ FILE_TYPE_DIRECTORY
Definition fs_types.h:16
dentry_t * dentry_unmount(dentry_t *root)
Unmount a filesystem at the mountpoint.
Definition mount.c:107
dentry_t * dentry_from_fd(fd_t fd)
Get the dentry from a file descriptor.
Definition dentry.c:330
bool dentry_mount(dentry_t *mountpoint, dentry_t *root, filesystem_t *fs)
Mount a filesystem at a mountpoint.
Definition mount.c:76
should_inline bool path_is_absolute(const char *path)
Check if a path is absolute.
Definition dentry.h:60
void dentry_detach(dentry_t *d)
Detach the inode from a dentry.
Definition dentry.c:318
void dentry_try_release(dentry_t *dentry)
ssize_t dentry_path(dentry_t *dentry, dentry_t *root, char *buf, size_t size)
Get the path of a dentry.
void vfs_populate_listdir_buf(dentry_t *dir, vfs_listdir_state_t *state)
List the contents of a directory.
Definition dentry.c:446
should_inline dentry_t * dentry_parent(const dentry_t *dentry)
Definition dentry.h:65
dentry_t * dentry_resolve(dentry_t *starting_dir, dentry_t *root_dir, const char *path, lastseg_resolve_flags_t flags)
Lookup a path in the filesystem.
Definition dentry.c:397
lastseg_resolve_flags_t
Definition dentry.h:38
void dentry_check_refstat(const dentry_t *dentry)
Check the reference count of a dentry.
void dentry_unref(dentry_t *dentry)
Decrement the reference count of a dentry.
__nodiscard bool dentry_unref_one_norelease(dentry_t *dentry)
Decrease the refcount of ONE SINGLE dentry, including (if it's a mountpoint) the mountpoint dentry.
@ RESOLVE_EXPECT_EXIST
Definition dentry.h:49
@ RESOLVE_EXPECT_ANY_EXIST
Definition dentry.h:51
@ RESOLVE_SYMLINK_NOFOLLOW
Definition dentry.h:46
@ RESOLVE_EXPECT_ANY_TYPE
Definition dentry.h:42
@ RESOLVE_EXPECT_DIR
Definition dentry.h:41
@ RESOLVE_EXPECT_NONEXIST
Definition dentry.h:50
@ RESOLVE_EXPECT_FILE
Definition dentry.h:40
MOSAPI s32 strcmp(const char *str1, const char *str2)
Definition mos_string.c:24
#define MAX(a, b)
Definition mos_stdlib.h:31
MOSAPI void linked_list_init(list_node_t *head_node)
Initialise a circular double linked list.
Definition list.c:15
#define LIST_HEAD_INIT(container)
Definition list.h:38
MOSAPI void list_node_append(list_node_t *head, list_node_t *item)
Definition list.c:68
#define list_foreach(t, v, h)
Iterate over a list.
Definition list.h:83
#define list_node(element)
Get the ‘list_node’ of a list element. This is exactly the reverse of ‘list_entry’ above.
Definition list.h:68
list_node_t list_head
A linked list head.
Definition list.h:23
MOSAPI bool list_is_empty(const list_node_t *head)
Definition list.c:21
#define list_remove(element)
Definition list.h:74
vmfault_result_t
Definition mm.h:45
vmfault_result_t mm_resolve_cow_fault(vmap_t *vmap, ptr_t fault_addr, pagefault_t *info)
Helper function to resolve a copy-on-write fault.
Definition mm.c:273
@ VMFAULT_COPY_BACKING_PAGE
the caller should copy the backing page into the faulting address
Definition mm.h:49
@ VMFAULT_MAP_BACKING_PAGE
the caller should map the backing page into the faulting address
Definition mm.h:48
@ VMFAULT_CANNOT_HANDLE
the handler cannot handle this fault
Definition mm.h:50
@ VMFAULT_MAP_BACKING_PAGE_RO
the caller should map the backing page into the faulting address, and mark it non-writable
Definition mm.h:47
@ VMAP_TYPE_PRIVATE
Definition mm.h:31
dentry_t * root_dentry
Definition vfs.c:36
long vfs_chdirat(fd_t dirfd, const char *path)
Change the current working directory.
Definition vfs.c:764
size_t vfs_list_dir(io_t *io, void *user_buf, size_t user_size)
Get the content of a directory.
Definition vfs.c:713
long vfs_symlink(const char *path, const char *target)
Create a symbolic link.
Definition vfs.c:646
long vfs_unmount(const char *path)
Unmount a filesystem at a given path.
Definition vfs.c:536
long vfs_rmdir(const char *path)
Definition vfs.c:689
should_inline const file_ops_t * file_get_ops(file_t *file)
Definition vfs.h:18
file_t * vfs_openat(int fd, const char *path, open_flags flags)
Open a file at a given path.
Definition vfs.c:578
ssize_t vfs_getcwd(char *buf, size_t size)
Get the current working directory.
Definition vfs.c:780
long vfs_unlinkat(fd_t dirfd, const char *path)
Remove the name of a file, and possibly the file itself.
Definition vfs.c:803
long vfs_fstatat(fd_t fd, const char *path, file_stat_t *restrict statbuf, fstatat_flags flags)
Stat a file.
Definition vfs.c:588
long vfs_mount(const char *device, const char *path, const char *fs, const char *options)
Mount a filesystem at a given existing path.
Definition vfs.c:466
void vfs_register_filesystem(filesystem_t *fs)
Definition vfs.c:452
long vfs_fsync(io_t *io, bool sync_metadata, off_t start, off_t end)
Synchronize a file with the filesystem.
Definition vfs.c:830
long vfs_mkdir(const char *path)
Create a directory.
Definition vfs.c:664
long vfs_fchmodat(fd_t fd, const char *path, int perm, int flags)
Change the permissions of a file.
Definition vfs.c:789
file_t * vfs_do_open_dentry(dentry_t *entry, bool created, bool read, bool write, bool exec, bool truncate)
Open an directory dentry.
Definition vfs.c:413
size_t vfs_readlinkat(fd_t dirfd, const char *path, char *buf, size_t size)
Read a symbolic link.
Definition vfs.c:623
bool inode_unlink(inode_t *dir, dentry_t *dentry)
Unlink a dentry from its parent inode.
Definition inode.c:86
void io_init(io_t *io, io_type_t type, io_flags_t flags, const io_op_t *ops)
Definition io.c:44
io_flags_t
Definition io.h:25
@ IO_MMAPABLE
Definition io.h:31
@ IO_EXECUTABLE
Definition io.h:29
@ IO_READABLE
Definition io.h:27
@ IO_SEEKABLE
Definition io.h:30
@ IO_WRITABLE
Definition io.h:28
__nodiscard bool io_valid(const io_t *io)
Definition io.c:127
@ IO_FILE
Definition io.h:17
@ IO_DIR
Definition io.h:18
io_seek_whence_t
Definition io_types.h:6
@ IO_SEEK_DATA
Definition io_types.h:10
@ IO_SEEK_SET
Definition io_types.h:9
@ IO_SEEK_CURRENT
Definition io_types.h:7
@ IO_SEEK_HOLE
Definition io_types.h:11
@ IO_SEEK_END
Definition io_types.h:8
#define vmap_stat_inc(vmap, type)
Definition mmstat.h:70
#define vmap_stat_dec(vmap, type)
Definition mmstat.h:71
#define ALIGN_UP_TO_PAGE(addr)
Definition mos_global.h:75
#define ALIGN_DOWN_TO_PAGE(addr)
Definition mos_global.h:76
#define MOS_UNUSED(x)
Definition mos_global.h:64
#define IS_ERR_VALUE(x)
Definition mos_global.h:126
#define unlikely(x)
Definition mos_global.h:40
#define container_of(ptr, type, member)
Definition mos_global.h:50
list_head vfs_mountpoint_list
Definition mount.c:20
long pagecache_flush_or_drop(inode_cache_t *icache, off_t pgoff, size_t npages, bool drop_page)
Flush or drop a range of pages from the page cache.
Definition page_cache.c:56
long pagecache_flush_or_drop_all(inode_cache_t *icache, bool drop_page)
Flush or drop all pages in the page cache.
Definition page_cache.c:78
phyframe_t * pagecache_get_page_for_read(inode_cache_t *cache, off_t pgoff)
Get a page from the page cache.
Definition page_cache.c:85
#define mos_panic(fmt,...)
Definition panic.h:55
static void * memcpy(void *s1, const void *s2, size_t n)
Definition pb_syshdr.h:90
#define NULL
Definition pb_syshdr.h:46
#define current_process
Definition platform.h:31
#define pr_info2(fmt,...)
Definition printk.h:36
#define pr_warn(fmt,...)
Definition printk.h:38
#define pr_dinfo2(feat, fmt,...)
Definition printk.h:27
io_t * process_get_fd(process_t *process, fd_t fd)
Definition process.c:192
#define mutex_acquire(mutex)
Definition rpc_client.c:41
#define mutex_release(mutex)
Definition rpc_client.c:42
#define MOS_INIT(_comp, _fn)
Definition setup.h:40
size_t size
Definition slab.c:30
const char * name
Definition slab.c:31
#define SLAB_AUTOINIT(name, var, type)
#define spinlock_acquire(lock)
Definition spinlock.h:61
#define SPINLOCK_INIT
Definition spinlock.h:28
#define spinlock_release(lock)
Definition spinlock.h:62
superblock_t * superblock
Definition vfs_types.h:119
bool is_mountpoint
Definition vfs_types.h:120
const char * name
Definition vfs_types.h:118
atomic_t refcount
Definition vfs_types.h:116
inode_t * inode
Definition vfs_types.h:117
bool(* open)(inode_t *inode, file_t *file, bool created)
called when a file is opened, or created
Definition vfs_types.h:90
off_t(* seek)(file_t *file, off_t offset, io_seek_whence_t whence)
seek to a new position in the file
Definition vfs_types.h:94
bool(* munmap)(file_t *file, vmap_t *vmap, bool *unmapped)
unmap the file from memory
Definition vfs_types.h:96
void(* release)(file_t *file)
called when the last reference to the file is dropped
Definition vfs_types.h:93
ssize_t(* read)(const file_t *file, void *buf, size_t size, off_t offset)
read from the file
Definition vfs_types.h:91
ssize_t(* write)(const file_t *file, const void *buf, size_t size, off_t offset)
write to the file
Definition vfs_types.h:92
bool(* mmap)(file_t *file, vmap_t *vmap, off_t offset)
map the file into memory
Definition vfs_types.h:95
uid_t uid
Definition fs_types.h:69
u64 modified
Definition fs_types.h:77
u64 accessed
Definition fs_types.h:75
u64 created
Definition fs_types.h:76
file_perm_t perm
Definition fs_types.h:67
bool sgid
Definition fs_types.h:73
size_t size
Definition fs_types.h:68
file_type_t type
Definition fs_types.h:66
bool suid
Definition fs_types.h:72
bool sticky
Definition fs_types.h:71
gid_t gid
Definition fs_types.h:70
ssize_t nlinks
Definition fs_types.h:74
spinlock_t offset_lock
Definition vfs_types.h:201
io_t io
Definition vfs_types.h:199
size_t offset
Definition vfs_types.h:202
void * private_data
Definition vfs_types.h:203
dentry_t * dentry
Definition vfs_types.h:200
void(* unmount)(filesystem_t *fs, dentry_t *mountpoint)
Definition vfs_types.h:185
dentry_t *(* mount)(filesystem_t *fs, const char *dev_name, const char *mount_options)
Definition vfs_types.h:184
const char * name
Definition vfs_types.h:183
mutex_t lock
Definition vfs_types.h:149
bool(* mkdir)(inode_t *dir, dentry_t *dentry, file_perm_t perm)
create a new directory
Definition vfs_types.h:71
size_t(* readlink)(dentry_t *dentry, char *buffer, size_t buflen)
read the contents of a symbolic link
Definition vfs_types.h:77
bool(* unlink)(inode_t *dir, dentry_t *dentry)
remove a file name, this is called after nlinks is decremented
Definition vfs_types.h:85
bool(* newfile)(inode_t *dir, dentry_t *dentry, file_type_t type, file_perm_t perm)
create a new file
Definition vfs_types.h:75
bool(* rmdir)(inode_t *dir, dentry_t *dentry)
remove a directory
Definition vfs_types.h:81
bool(* symlink)(inode_t *dir, dentry_t *dentry, const char *symname)
create a symbolic link
Definition vfs_types.h:83
superblock_t * superblock
Definition vfs_types.h:171
uid_t uid
Definition vfs_types.h:161
u64 modified
Definition vfs_types.h:169
u64 accessed
Definition vfs_types.h:167
u64 created
Definition vfs_types.h:168
file_perm_t perm
Definition vfs_types.h:159
const inode_ops_t * ops
Definition vfs_types.h:172
bool sgid
Definition vfs_types.h:165
size_t size
Definition vfs_types.h:160
file_type_t type
Definition vfs_types.h:158
bool suid
Definition vfs_types.h:164
inode_cache_t cache
Definition vfs_types.h:175
bool sticky
Definition vfs_types.h:163
gid_t gid
Definition vfs_types.h:162
u64 ino
Definition vfs_types.h:157
ssize_t nlinks
Definition vfs_types.h:166
Definition io.h:35
Definition io.h:46
io_type_t type
Definition io.h:50
io_flags_t flags
Definition io.h:49
bool is_present
Definition mm.h:37
phyframe_t * faulting_page
the frame that contains the copy-on-write data (if any)
Definition mm.h:40
const phyframe_t * backing_page
the frame that contains the data for this page, the on_fault handler should set this
Definition mm.h:41
bool is_write
Definition mm.h:37
Definition slab.h:45
long(* sync_inode)(inode_t *inode)
flush the inode to disk
Definition vfs_types.h:102
const superblock_ops_t * ops
Definition vfs_types.h:109
filesystem_t * fs
Definition vfs_types.h:108
Definition vfs_types.h:45
size_t read_offset
user has read up to this offset, start from this offset when reading more entries
Definition vfs_types.h:57
size_t n_count
number of entries in the list
Definition vfs_types.h:56
list_head entries
Definition vfs_types.h:55
Definition mm.h:58
ptr_t vaddr
Definition mm.h:62
vmfault_handler_t on_fault
Definition mm.h:73
io_t * io
Definition mm.h:67
off_t io_offset
Definition mm.h:68
vmap_type_t type
Definition mm.h:71
ssize_t sysfs_printf(sysfs_file_t *file, const char *fmt,...)
Definition sysfs.c:73
#define SYSFS_RO_ITEM(_name, _show_fn)
Definition sysfs.h:48
#define SYSFS_AUTOREGISTER(sysfs_name, sysfs_items)
long timer_msleep(u64 ms)
Definition timer.c:39
ssize_t off_t
Definition types.h:84
s32 fd_t
Definition types.h:81
unsigned long ptr_t
Definition types.h:25
signed long ssize_t
Definition types.h:83
static bool vfs_io_ops_mmap(io_t *io, vmap_t *vmap, off_t offset)
Definition vfs.c:236
static size_t vfs_io_ops_read(io_t *io, void *buf, size_t count)
Definition vfs.c:114
static vmfault_result_t vfs_fault_handler(vmap_t *vmap, ptr_t fault_addr, pagefault_t *info)
Definition vfs.c:186
slab_t * superblock_cache
Definition vfs.c:38
static void vfs_io_ops_close(io_t *io)
Definition vfs.c:69
static bool vfs_sysfs_dentry_stats(sysfs_file_t *f)
Definition vfs.c:889
static filesystem_t * vfs_find_filesystem(const char *name)
Definition vfs.c:318
static void vfs_sysfs_dentry_stats_stat_receiver(int depth, const dentry_t *dentry, bool mountroot, void *data)
Definition vfs.c:877
static void vfs_flusher_init(void)
Definition vfs.c:295
static list_head vfs_fs_list
Definition vfs.c:33
static void vfs_io_ops_close_dir(io_t *io)
Definition vfs.c:93
slab_t * file_cache
Definition vfs.c:38
slab_t * mount_cache
Definition vfs.c:38
static void vfs_flusher_entry(void *arg)
Definition vfs.c:285
static const io_op_t dir_io_ops
Definition vfs.c:277
static bool vfs_verify_permissions(dentry_t *file_dentry, bool open, bool read, bool create, bool execute, bool write)
Definition vfs.c:334
static sysfs_item_t vfs_sysfs_items[]
Definition vfs.c:895
static const io_op_t file_io_ops
Definition vfs.c:267
static off_t vfs_io_ops_seek(io_t *io, off_t offset, io_seek_whence_t whence)
Definition vfs.c:147
static long do_sync_inode(file_t *file)
Definition vfs.c:59
static bool vfs_io_ops_munmap(io_t *io, vmap_t *vmap, bool *unmapped)
Definition vfs.c:250
static spinlock_t vfs_fs_list_lock
Definition vfs.c:34
static size_t vfs_io_ops_write(io_t *io, const void *buf, size_t count)
Definition vfs.c:132
static bool vfs_sysfs_filesystems(sysfs_file_t *f)
Definition vfs.c:855
static file_t * vfs_do_open(dentry_t *base, const char *path, open_flags flags)
Definition vfs.c:351
static void vfs_io_ops_getname(const io_t *io, char *buf, size_t size)
Definition vfs.c:261
static long do_pagecache_flush(file_t *file, off_t pgoff, size_t npages)
Definition vfs.c:44
static bool vfs_sysfs_mountpoints(sysfs_file_t *f)
Definition vfs.c:865
static void vfs_copy_stat(file_stat_t *statbuf, inode_t *inode)
Definition vfs.c:301
#define dentry_name(dentry)
Definition vfs_types.h:125