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