39 dInfo2<vfs> <<
"vfs: flushing page cache for file " << (
void *) file;
43 if (pgoff == 0 && npages == (
size_t) -1)
123 if (!file_ops || !file_ops->
read)
127 size_t ret = file_ops->
read(
this, buf, count, this->
offset);
130 else if (ret != (
size_t) -1)
139 if (!file_ops || !file_ops->
write)
143 size_t ret = file_ops->
write(
this, buf, count, this->
offset);
162 this->offset = std::max(
offset, 0l);
168 new_offset = std::max(new_offset, 0l);
169 this->offset = new_offset;
175 new_offset = std::max(new_offset, 0l);
176 this->offset = new_offset;
197 if (pagecache_page.isErr())
255 return file_ops->
munmap(
this, vmap, unmapped);
294 statbuf->
ino = inode->
ino;
298 statbuf->
uid = inode->
uid;
299 statbuf->
gid = inode->
gid;
314 if (fs->name ==
name)
348 const bool expect_dir = flags &
OPEN_DIR;
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();
367 bool created =
false;
369 if (may_create && entry->inode ==
NULL)
372 if (!parent->inode->ops->newfile)
378 if (!parent->inode->ops->newfile(parent->inode, entry.get(),
FILE_TYPE_REGULAR, 0666))
397 return file.getErr();
437 if (ops && ops->
open)
469 mos_warn(
"filesystem '%s' not found", fs);
480 mWarn <<
"root filesystem is already mounted";
483 dInfo2<vfs> <<
"mounting root filesystem '" << fs <<
"'...";
484 const auto mountResult = real_fs->
mount(real_fs, device, options);
485 if (mountResult.isErr())
487 mWarn <<
"failed to mount root filesystem";
506 return base.getErr();
510 return mpRoot.getErr();
512 if (mpRoot->is_mountpoint)
515 mWarn <<
"mount point is already mounted";
523 auto mounted_root = real_fs->
mount(real_fs, device, options);
524 if (mounted_root.isErr())
526 mWarn <<
"failed to mount filesystem";
527 return mounted_root.getErr();
530 const bool mounted =
dentry_mount(mpRoot.get(), mounted_root.get(), real_fs);
533 mWarn <<
"failed to mount filesystem";
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 <<
"'";
546 if (mounted_root.isErr())
547 return mounted_root.getErr();
550 if (mounted_root->refcount != 2)
553 mWarn <<
"refcount is not as expected";
563 mWarn <<
"failed to unmount filesystem";
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());
572 MOS_ASSERT_X(mounted_root->refcount == 0,
"fs->umount should release the last reference to the mounted root");
587 dInfo2<vfs> <<
"vfs_openat(fd=" << fd <<
", path='" << path <<
"', flags=" << flags <<
")";
590 return basedir.getErr();
592 auto file =
vfs_do_open(basedir.get(), path, flags);
600 dInfo2<vfs> <<
"vfs_fstatat(fd=" << fd <<
", path=" << (
void *) path <<
", stat=" << (
void *) statbuf <<
", flags=" << flags <<
")";
613 dInfo2<vfs> <<
"vfs_fstatat(fd=" << fd <<
", path='" << path <<
"', stat=" << (
void *) statbuf <<
", flags=" << flags <<
")";
616 return basedir.getErr();
624 return dentry.getErr();
636 return base.getErr();
640 return dentry.getErr();
648 const size_t len = dentry->inode->ops->readlink(dentry.get(), buf,
size);
653 return -ENAMETOOLONG;
660 dInfo2<vfs> <<
"vfs_symlink(path='" << path <<
"', target='" << target <<
"')";
663 return base.getErr();
667 return dentry.getErr();
673 mos_warn(
"failed to create symlink '%s'", path);
676 return created ? 0 : -EIO;
684 return base.getErr();
688 return dentry.getErr();
702 mos_warn(
"failed to create directory '%s'", path);
705 return created ? 0 : -EIO;
713 return base.getErr();
717 return dentry.getErr();
729 mos_warn(
"failed to remove directory '%s'", path);
732 return removed ? 0 : -EIO;
737 dInfo2<vfs> <<
"vfs_list_dir(io=" << (
void *) io <<
", buf=" << (
void *) user_buf <<
", size=" << user_size <<
")";
759 size_t bytes_copied = 0;
769 const size_t entry_size =
sizeof(ino_t) +
sizeof(
off_t) +
sizeof(short) +
sizeof(
char) + entry->name.size() + 1;
770 if (bytes_copied + entry_size > user_size)
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;
789 dInfo2<vfs> <<
"vfs_chdirat('" << dirfd <<
", " << path <<
"')";
792 return base.getErr();
796 return dentry.getErr();
808 dInfo2<vfs> <<
"vfs_getcwd(buf=" << (
void *) buf <<
", size=" <<
size <<
")";
817 const size_t n = path->copy(buf,
size);
819 if (n != path->size())
831 dInfo2<vfs> <<
"vfs_fchmodat(fd=" << fd <<
", path='" << path <<
"', perm=" << perm <<
", flags=" << flags <<
")";
834 return base.getErr();
838 return dentry.getErr();
841 dentry->inode->perm = perm;
848 dInfo2<vfs> <<
"vfs_unlinkat(dirfd=" << dirfd <<
", path='" << path <<
"')";
851 return base.getErr();
855 return dentry.getErr();
878 dInfo2<vfs> <<
"vfs_fsync(io=" << (
void *) io <<
", sync_metadata=" << sync_metadata <<
", start=" << start <<
", end=" << end <<
")";
881 const off_t nbytes = end - start;
917 sysfs_printf(
f,
"%-20s %-10s\n", str->c_str(), mp->fs->name.c_str());
919 sysfs_printf(
f,
"%-20s %-10s\n",
"<error>", mp->fs->name.c_str());
933 mountroot ?
" (mount root)" : (dentry->
is_mountpoint ?
" (mountpoint)" :
"")
#define MOS_ASSERT_X(cond, msg,...)
#define mos_warn(fmt,...)
const Char * c_str() const
basic_string_view< Char > value_or(basic_string_view< Char > other) const
void dentry_dump_refstat(const dentry_t *dentry, dump_refstat_receiver_t *receiver, void *receiver_data)
dentry_t * dentry_unmount(dentry_t *root)
Unmount a filesystem at the mountpoint.
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.
bool dentry_mount(dentry_t *mountpoint, dentry_t *root, filesystem_t *fs)
Mount a filesystem at a mountpoint.
void dentry_detach(dentry_t *d)
Detach the inode from a dentry.
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.
void dentry_try_release(dentry_t *dentry)
should_inline dentry_t * dentry_parent(const dentry_t &dentry)
void vfs_populate_listdir_buf(dentry_t *dir, vfs_listdir_state_t *state)
List the contents of a directory.
should_inline bool path_is_absolute(mos::string_view path)
Check if a path is absolute.
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_ANY_EXIST
@ RESOLVE_SYMLINK_NOFOLLOW
@ RESOLVE_EXPECT_ANY_TYPE
@ RESOLVE_EXPECT_NONEXIST
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.
MOSAPI void list_node_append(list_node_t *head, list_node_t *item)
#define list_foreach(t, v, h)
Iterate over a list.
#define list_node(element)
Get the ‘list_node’ of a list element. This is exactly the reverse of ‘list_entry’ above.
list_node_t list_head
A linked list head.
MOSAPI bool list_is_empty(const list_node_t *head)
#define list_remove(element)
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.
@ VMFAULT_COPY_BACKING_PAGE
the caller should copy the backing page into the faulting address
@ VMFAULT_MAP_BACKING_PAGE
the caller should map the backing page into the faulting address
@ VMFAULT_CANNOT_HANDLE
the handler cannot handle this fault
@ VMFAULT_MAP_BACKING_PAGE_RO
the caller should map the backing page into the faulting address, and mark it non-writable
long vfs_chdirat(fd_t dirfd, const char *path)
Change the current working directory.
long vfs_symlink(const char *path, const char *target)
Create a symbolic link.
long vfs_unmount(const char *path)
Unmount a filesystem at a given path.
PtrResult< void > vfs_mount(const char *device, const char *path, const char *fs, const char *options)
Mount a filesystem at a given existing path.
long vfs_fstatat(fd_t fd, const char *path, file_stat_t *__restrict statbuf, FStatAtFlags flags)
Stat a file.
PtrResult< FsBaseFile > vfs_do_open_dentry(dentry_t *dentry, bool created, bool read, bool write, bool exec, bool truncate)
Open an directory dentry.
ssize_t vfs_getcwd(char *buf, size_t size)
Get the current working directory.
PtrResult< FsBaseFile > vfs_openat(int fd, mos::string_view path, OpenFlags flags)
Open a file at a given path.
long vfs_unlinkat(fd_t dirfd, const char *path)
Remove the name of a file, and possibly the file itself.
PtrResult< void > vfs_mkdir(const char *path)
Create a directory.
void vfs_register_filesystem(filesystem_t *fs)
long vfs_fsync(IO *io, bool sync_metadata, off_t start, off_t end)
Synchronize a file with the filesystem.
PtrResult< void > vfs_rmdir(const char *path)
long vfs_fchmodat(fd_t fd, const char *path, int perm, int flags)
Change the permissions of a file.
size_t vfs_readlinkat(fd_t dirfd, const char *path, char *buf, size_t size)
Read a symbolic link.
size_t vfs_list_dir(IO *io, void *user_buf, size_t user_size)
Get the content of a directory.
bool inode_unlink(inode_t *dir, dentry_t *dentry)
Unlink a dentry from its parent inode.
#define vmap_stat_inc(vmap, type)
#define vmap_stat_dec(vmap, type)
#define ALIGN_UP_TO_PAGE(addr)
#define ALIGN_DOWN_TO_PAGE(addr)
list_head vfs_mountpoint_list
basic_string_view< char > string_view
T * create(Args &&...args)
mos::basic_string< char > string
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,...)
static void * memcpy(void *s1, const void *s2, size_t n)
IO * process_get_fd(Process *process, fd_t fd)
#define mutex_acquire(mutex)
#define mutex_release(mutex)
#define MOS_INIT(_comp, _fn)
#define spinlock_acquire(lock)
#define spinlock_release(lock)
const file_ops_t * get_ops() const
mos::string name() const override
void on_closed() override
size_t on_read(void *buf, size_t size) override
off_t on_seek(off_t offset, io_seek_whence_t whence) override
size_t on_write(const void *buf, size_t size) override
bool on_mmap(vmap_t *vmap, off_t offset) override
bool on_munmap(vmap_t *vmap, bool *unmapped) override
void on_closed() override
size_t on_read(void *buf, size_t size) override
static bool IsValid(const IO *io)
off_t(* seek)(FsBaseFile *file, off_t offset, io_seek_whence_t whence)
seek to a new position in the file
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
ssize_t(* read)(const FsBaseFile *file, void *buf, size_t size, off_t offset)
read from the file
void(* release)(FsBaseFile *file)
called when the last reference to the file is dropped
bool(* mmap)(FsBaseFile *file, vmap_t *vmap, off_t offset)
map the file into memory
bool(* open)(inode_t *inode, FsBaseFile *file, bool created)
called when a file is opened, or created
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
bool(* unlink)(inode_t *dir, dentry_t *dentry)
remove a file name, this is called after nlinks is decremented
bool(* rmdir)(inode_t *dir, dentry_t *dentry)
remove a directory
bool(* symlink)(inode_t *dir, dentry_t *dentry, const char *symname)
create a symbolic link
superblock_t * superblock
phyframe_t * faulting_page
the frame that contains the copy-on-write data (if any)
const phyframe_t * backing_page
the frame that contains the data for this page, the on_fault handler should set this
long(* sync_inode)(inode_t *inode)
flush the inode to disk
const superblock_ops_t * ops
size_t read_offset
user has read up to this offset, start from this offset when reading more entries
size_t n_count
number of entries in the list
vmfault_handler_t on_fault
ssize_t sysfs_printf(sysfs_file_t *file, const char *fmt,...)
#define SYSFS_RO_ITEM(_name, _show_fn)
#define SYSFS_AUTOREGISTER(sysfs_name, sysfs_items)
long timer_msleep(u64 ms)
static bool vfs_verify_permissions(dentry_t &file_dentry, bool open, bool read, bool create, bool execute, bool write)
static vmfault_result_t vfs_fault_handler(vmap_t *vmap, ptr_t fault_addr, pagefault_t *info)
static bool vfs_sysfs_dentry_stats(sysfs_file_t *f)
static void vfs_sysfs_dentry_stats_stat_receiver(int depth, const dentry_t *dentry, bool mountroot, void *data)
static void vfs_flusher_init(void)
static list_head vfs_fs_list
static __used void vfs_flusher_entry(void *arg)
static long do_sync_inode(FsBaseFile *file)
static sysfs_item_t vfs_sysfs_items[]
static filesystem_t * vfs_find_filesystem(mos::string_view name)
static PtrResult< FsBaseFile > vfs_do_open(dentry_t *base, mos::string_view path, OpenFlags flags)
static spinlock_t vfs_fs_list_lock
static long do_pagecache_flush(FsBaseFile *file, off_t pgoff, size_t npages)
static bool vfs_sysfs_filesystems(sysfs_file_t *f)
static bool vfs_sysfs_mountpoints(sysfs_file_t *f)
static void vfs_copy_stat(file_stat_t *statbuf, inode_t *inode)
mos::string dentry_name(const dentry_t *dentry)