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