1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/filesystem/vfs_utils.hpp"
4
5#include "mos/filesystem/dentry.hpp"
6#include "mos/filesystem/page_cache.hpp"
7#include "mos/filesystem/vfs_types.hpp"
8#include "mos/lib/sync/spinlock.hpp"
9#include "mos/mm/physical/pmm.hpp"
10
11#include <algorithm>
12#include <memory>
13#include <mos/lib/structures/hashmap_common.hpp>
14#include <mos/types.hpp>
15#include <mos_stdlib.hpp>
16#include <mos_string.hpp>
17
18static dentry_t *dentry_create(superblock_t *sb, dentry_t *parent, mos::string_view name)
19{
20 std::unique_ptr<int> a;
21 const auto dentry = mos::create<dentry_t>();
22 tree_node_init(tree_node(dentry));
23
24 dentry->superblock = sb;
25 dentry->name = name;
26
27 if (parent)
28 {
29 MOS_ASSERT(spinlock_is_locked(&parent->lock));
30 tree_add_child(tree_node(parent), tree_node(dentry));
31 dentry->superblock = parent->superblock;
32 }
33
34 return dentry;
35}
36
37dentry_t *dentry_get_from_parent(superblock_t *sb, dentry_t *parent, mos::string_view name)
38{
39 if (!parent)
40 return dentry_create(sb, NULL, name);
41
42 dentry_t *dentry = NULL;
43
44 spinlock_acquire(&parent->lock);
45 tree_foreach_child(dentry_t, child, parent)
46 {
47 if (child->name == name)
48 {
49 dentry = child;
50 break;
51 }
52 }
53
54 // if not found, create a new one
55 if (!dentry)
56 dentry = dentry_create(sb, parent, name);
57
58 spinlock_release(&parent->lock);
59 return dentry;
60}
61
62bool simple_page_write_begin(inode_cache_t *icache, off_t offset, size_t size, phyframe_t **page, void **private_)
63{
64 MOS_UNUSED(size);
65 const auto newPage = pagecache_get_page_for_write(cache: icache, pgoff: offset / MOS_PAGE_SIZE);
66 if (newPage.isErr())
67 return false;
68
69 *page = newPage.get();
70 *private_ = NULL;
71 return true;
72}
73
74void simple_page_write_end(inode_cache_t *icache, off_t offset, size_t size, phyframe_t *page, void *private_)
75{
76 MOS_UNUSED(page);
77 MOS_UNUSED(private_);
78
79 // also update the inode's size
80 if (offset + size > icache->owner->size)
81 icache->owner->size = offset + size;
82}
83
84long simple_flush_page_discard_data(inode_cache_t *icache, off_t pgoff, phyframe_t *page)
85{
86 MOS_UNUSED(icache);
87 MOS_UNUSED(pgoff);
88 MOS_UNUSED(page);
89 return 0;
90}
91
92// read from the page cache, the size and offset are already validated to be in the file's bounds
93ssize_t vfs_generic_read(const file_t *file, void *buf, size_t size, off_t offset)
94{
95 // cap the read size to the file's size
96 size = std::min(a: size, b: file->dentry->inode->size - offset);
97 inode_cache_t *icache = &file->dentry->inode->cache;
98 const ssize_t read = vfs_read_pagecache(icache, buf, size, offset);
99 return read;
100}
101
102// write to the page cache, the size and offset are already validated to be in the file's bounds
103ssize_t vfs_generic_write(const file_t *file, const void *buf, size_t size, off_t offset)
104{
105 inode_cache_t *icache = &file->dentry->inode->cache;
106 const ssize_t written = vfs_write_pagecache(icache, buf, total_size: size, offset);
107 return written;
108}
109
110bool vfs_simple_write_begin(inode_cache_t *icache, off_t offset, size_t size)
111{
112 MOS_UNUSED(icache);
113 MOS_UNUSED(offset);
114 MOS_UNUSED(size);
115 return true;
116}
117
118void vfs_generic_iterate_dir(const dentry_t *dir, vfs_listdir_state_t *state, dentry_iterator_op add_record)
119{
120 dentry_t *d_parent = dentry_parent(dentry: *dir);
121 if (d_parent == NULL)
122 d_parent = root_dentry;
123
124 MOS_ASSERT(d_parent->inode != NULL);
125 MOS_ASSERT(dir->inode);
126
127 add_record(state, dir->inode->ino, ".", FILE_TYPE_DIRECTORY);
128 add_record(state, d_parent->inode->ino, "..", FILE_TYPE_DIRECTORY);
129
130 tree_foreach_child(dentry_t, child, dir)
131 {
132 if (child->inode)
133 add_record(state, child->inode->ino, child->name, child->inode->type);
134 }
135}
136