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