feat: lseek(2), read(2), write(2) implementation
[lunaix-os.git] / lunaix-os / kernel / fs / twifs.c
1 /**
2  * @file twifs.c
3  * @author Lunaixsky (zelong56@gmail.com)
4  * @brief TwiFS - A pseudo file system for kernel state exposure.
5  * @version 0.1
6  * @date 2022-07-21
7  *
8  * @copyright Copyright (c) 2022
9  *
10  */
11 #include <klibc/string.h>
12 #include <lunaix/clock.h>
13 #include <lunaix/fs.h>
14 #include <lunaix/fs/twifs.h>
15 #include <lunaix/mm/cake.h>
16 #include <lunaix/mm/valloc.h>
17
18 static struct twifs_node* fs_root;
19
20 static struct cake_pile* twi_pile;
21
22 int
23 __twifs_dirlookup(struct v_inode* inode, struct v_dnode* dnode);
24
25 int
26 __twifs_openfile(struct v_inode* inode, struct v_file* file);
27
28 struct twifs_node*
29 __twifs_get_node(struct twifs_node* parent, struct hstr* name);
30
31 struct v_inode*
32 __twifs_create_inode(struct twifs_node* twi_node);
33
34 int
35 __twifs_iterate_dir(struct v_file* file, struct dir_context* dctx);
36
37 int
38 __twifs_mount(struct v_superblock* vsb, struct v_dnode* mount_point);
39
40 void
41 twifs_init()
42 {
43     twi_pile = cake_new_pile("twifs_node", sizeof(struct twifs_node), 1, 0);
44
45     struct filesystem* twifs = vzalloc(sizeof(struct filesystem));
46     twifs->fs_name = HSTR("twifs", 5);
47     twifs->mount = __twifs_mount;
48
49     fsm_register(twifs);
50
51     fs_root = twifs_dir_node(NULL, NULL, 0);
52
53     // 预备一些常用的类别
54     twifs_toplevel_node("kernel", 6);
55     twifs_toplevel_node("dev", 3);
56     twifs_toplevel_node("bus", 3);
57 }
58
59 struct twifs_node*
60 __twifs_new_node(struct twifs_node* parent, const char* name, int name_len)
61 {
62     struct twifs_node* node = cake_grab(twi_pile);
63     memset(node, 0, sizeof(*node));
64
65     node->name = HSTR(name, name_len);
66     hstr_rehash(&node->name, HSTR_FULL_HASH);
67     llist_init_head(&node->children);
68
69     if (parent) {
70         llist_append(&parent->children, &node->siblings);
71     }
72
73     return node;
74 }
75
76 struct twifs_node*
77 twifs_file_node(struct twifs_node* parent, const char* name, int name_len)
78 {
79     struct twifs_node* twi_node = __twifs_new_node(parent, name, name_len);
80     twi_node->itype = VFS_INODE_TYPE_FILE;
81
82     struct v_inode* twi_inode = __twifs_create_inode(twi_node);
83     twi_node->inode = twi_inode;
84
85     return twi_inode;
86 }
87
88 struct twifs_node*
89 twifs_dir_node(struct twifs_node* parent, const char* name, int name_len)
90 {
91     struct hstr hname = HSTR(name, name_len);
92     hstr_rehash(&hname, HSTR_FULL_HASH);
93     struct twifs_node* node = __twifs_get_node(parent, &hname);
94     if (node) {
95         return node;
96     }
97
98     struct twifs_node* twi_node = __twifs_new_node(parent, name, name_len);
99     twi_node->itype = VFS_INODE_TYPE_DIR;
100     twi_node->fops.readdir = __twifs_iterate_dir;
101
102     struct v_inode* twi_inode = __twifs_create_inode(twi_node);
103     struct twifs_node* dot = __twifs_new_node(twi_node, ".", 1);
104     struct twifs_node* ddot = __twifs_new_node(twi_node, "..", 2);
105
106     dot->itype = VFS_INODE_TYPE_DIR;
107     ddot->itype = VFS_INODE_TYPE_DIR;
108
109     twi_node->inode = twi_inode;
110     dot->inode = twi_inode;
111     ddot->inode = parent ? parent->inode : twi_inode;
112
113     return twi_node;
114 }
115
116 struct twifs_node*
117 twifs_toplevel_node(const char* name, int name_len)
118 {
119     return twifs_dir_node(fs_root, name, name_len);
120 }
121
122 int
123 __twifs_mount(struct v_superblock* vsb, struct v_dnode* mount_point)
124 {
125     mount_point->inode = fs_root->inode;
126     // FIXME: try to mitigate this special case or pull it up to higher level of
127     // abstraction
128     if (mount_point->parent && mount_point->parent->inode) {
129         struct hstr ddot_name = HSTR("..", 2);
130         struct twifs_node* root_ddot = __twifs_get_node(fs_root, &ddot_name);
131         root_ddot->inode = mount_point->parent->inode;
132     }
133     return 0;
134 }
135
136 struct v_inode*
137 __twifs_create_inode(struct twifs_node* twi_node)
138 {
139     struct v_inode* inode = vfs_i_alloc();
140     *inode = (struct v_inode){ .ctime = 0,
141                                .itype = twi_node->itype,
142                                .lb_addr = 0,
143                                .lb_usage = 0,
144                                .data = twi_node,
145                                .mtime = 0,
146                                .ref_count = 0 };
147     inode->ops.dir_lookup = __twifs_dirlookup;
148     inode->ops.open = __twifs_openfile;
149
150     return inode;
151 }
152
153 struct twifs_node*
154 __twifs_get_node(struct twifs_node* parent, struct hstr* name)
155 {
156     if (!parent)
157         return NULL;
158
159     struct twifs_node *pos, *n;
160     llist_for_each(pos, n, &parent->children, siblings)
161     {
162         if (HSTR_EQ(&pos->name, name)) {
163             return pos;
164         }
165     }
166     return NULL;
167 }
168
169 int
170 __twifs_dirlookup(struct v_inode* inode, struct v_dnode* dnode)
171 {
172     struct twifs_node* twi_node = (struct twifs_node*)inode->data;
173
174     struct twifs_node* child_node = __twifs_get_node(twi_node, &dnode->name);
175     if (child_node) {
176         dnode->inode = child_node->inode;
177         return 0;
178     }
179     return ENOENT;
180 }
181
182 int
183 __twifs_iterate_dir(struct v_file* file, struct dir_context* dctx)
184 {
185     struct twifs_node* twi_node = (struct twifs_node*)(file->inode->data);
186     int counter = 0;
187     struct twifs_node *pos, *n;
188
189     llist_for_each(pos, n, &twi_node->children, siblings)
190     {
191         if (counter++ >= dctx->index) {
192             dctx->index = counter;
193             dctx->read_complete_callback(
194               dctx, pos->name.value, pos->name.len, pos->itype);
195             return 0;
196         }
197     }
198
199     return 1;
200 }
201
202 int
203 __twifs_openfile(struct v_inode* inode, struct v_file* file)
204 {
205     struct twifs_node* twi_node = (struct twifs_node*)inode->data;
206     if (twi_node) {
207         file->inode = twi_node->inode;
208         file->ops = twi_node->fops;
209         return 0;
210     }
211     return ENOTSUP;
212 }