feat: refine symbolic link support.
[lunaix-os.git] / lunaix-os / kernel / fs / ramfs / ramfs.c
1 /**
2  * @file ramfs.c
3  * @author Lunaixsky
4  * @brief RamFS - a file system sit in RAM
5  * @version 0.1
6  * @date 2022-08-18
7  *
8  * @copyright Copyright (c) 2022
9  *
10  */
11 #include <klibc/string.h>
12 #include <lunaix/fs.h>
13 #include <lunaix/fs/ramfs.h>
14 #include <lunaix/mm/valloc.h>
15 #include <lunaix/spike.h>
16
17 /*
18     A RAM FS will play a role of being a root.
19
20     This is an temporary meaure as we don't have any concrete fs
21     yet. In the near future, we will have an ISO-9660 as our first
22     fs and mount our boot medium as root. And similar thing could
23     be done when we have installed our OS into hard disk, in that
24     case, our rootfs will be something like ext2.
25
26     RamFS vs. TwiFS: Indeed, they are both fs that lives in RAM so
27     there is no foundmental differences. However, TwiFS is designed
28     to be a 'virtual FIlesystem for KERnel space' (FIKER), so other
29     kernel sub-modules can just create node and attach their own
30     implementation of read/write, without brothering to create a
31     full featured concrete filesystem. This particularly useful for
32     kernel state exposure. In Lunaix, TwiFS is summation of procfs,
33     sysfs and kernfs in Linux world.
34
35     However, there is a smell of bad-design in the concept of TwiFS.
36     Since it tries to integrate too much responsibility. The TwiFS may
37     be replaced by more specific fs in the future.
38
39     On the other hand, RamFS is designed to be act like a dummy fs
40     that just ensure 'something there at least' and thus provide basic
41     'mountibility' for other fs.
42 */
43
44 volatile static inode_t ino = 0;
45
46 extern const struct v_inode_ops ramfs_inode_ops;
47 extern const struct v_file_ops ramfs_file_ops;
48
49 static int
50 __ramfs_mknod(struct v_dnode* dnode, struct v_inode** nod_out, u32_t flags)
51 {
52     struct v_inode* inode = vfs_i_alloc(dnode->super_block);
53
54     if (!inode) {
55         return ENOMEM;
56     }
57
58     struct ram_inode* rinode = valloc(sizeof(struct ram_inode));
59
60     if (!rinode) {
61         vfs_i_free(inode);
62         return ENOMEM;
63     }
64
65     rinode->flags = flags;
66     inode->data = rinode;
67
68     if ((flags & RAMF_DIR)) {
69         inode->itype = VFS_IFDIR;
70     } else {
71         inode->itype = VFS_IFFILE;
72     }
73
74     if ((flags & RAMF_SYMLINK)) {
75         inode->itype |= VFS_IFSYMLINK;
76     }
77
78     if (nod_out) {
79         *nod_out = inode;
80     }
81
82     vfs_i_addhash(inode);
83     vfs_assign_inode(dnode, inode);
84
85     return 0;
86 }
87
88 int
89 ramfs_readdir(struct v_file* file, struct dir_context* dctx)
90 {
91     int i = 0;
92     struct v_dnode *pos, *n;
93     llist_for_each(pos, n, &file->dnode->children, siblings)
94     {
95         if (i++ >= dctx->index) {
96             dctx->read_complete_callback(dctx,
97                                          pos->name.value,
98                                          pos->name.len,
99                                          vfs_get_dtype(pos->inode->itype));
100             return 1;
101         }
102     }
103     return 0;
104 }
105
106 int
107 ramfs_mkdir(struct v_inode* this, struct v_dnode* dnode)
108 {
109     return __ramfs_mknod(dnode, NULL, RAMF_DIR);
110 }
111
112 int
113 ramfs_create(struct v_inode* this, struct v_dnode* dnode)
114 {
115     return __ramfs_mknod(dnode, NULL, RAMF_FILE);
116 }
117
118 void
119 ramfs_inode_init(struct v_superblock* vsb, struct v_inode* inode)
120 {
121     inode->id = ino++;
122     inode->ops = &ramfs_inode_ops;
123     inode->default_fops = &ramfs_file_ops;
124 }
125
126 int
127 ramfs_mount(struct v_superblock* vsb, struct v_dnode* mount_point)
128 {
129     vsb->ops.init_inode = ramfs_inode_init;
130
131     return __ramfs_mknod(mount_point, NULL, RAMF_DIR);
132 }
133
134 int
135 ramfs_unmount(struct v_superblock* vsb)
136 {
137     return 0;
138 }
139
140 void
141 ramfs_init()
142 {
143     struct filesystem* ramfs = fsm_new_fs("ramfs", -1);
144     ramfs->mount = ramfs_mount;
145     ramfs->unmount = ramfs_unmount;
146
147     fsm_register(ramfs);
148 }
149
150 int
151 ramfs_mksymlink(struct v_inode* this, const char* target)
152 {
153     struct ram_inode* rinode = RAM_INODE(this->data);
154
155     assert(!(rinode->flags & RAMF_SYMLINK));
156
157     size_t len = strlen(target);
158     char* symlink = valloc(len);
159
160     if (!symlink) {
161         return ENOMEM;
162     }
163
164     memcpy(symlink, target, len);
165
166     this->itype |= VFS_IFSYMLINK;
167     rinode->flags |= RAMF_SYMLINK;
168     rinode->symlink = symlink;
169
170     return 0;
171 }
172
173 int
174 ramfs_read_symlink(struct v_inode* this, const char** path_out)
175 {
176     struct ram_inode* rinode = RAM_INODE(this->data);
177
178     if (!(rinode->flags & RAMF_SYMLINK)) {
179         return EINVAL;
180     }
181
182     *path_out = rinode->symlink;
183
184     return 0;
185 }
186
187 int
188 ramfs_unlink(struct v_inode* this)
189 {
190     struct ram_inode* rinode = RAM_INODE(this->data);
191
192     if ((rinode->flags & RAMF_SYMLINK)) {
193         rinode->flags &= ~RAMF_SYMLINK;
194         this->itype &= ~VFS_IFSYMLINK;
195         vfree(rinode->symlink);
196         return;
197     }
198
199     // TODO
200
201     return 0;
202 }
203
204 const struct v_inode_ops ramfs_inode_ops = { .mkdir = ramfs_mkdir,
205                                              .rmdir = default_inode_rmdir,
206                                              .dir_lookup =
207                                                default_inode_dirlookup,
208                                              .create = ramfs_create,
209                                              .open = default_inode_open,
210                                              .unlink = ramfs_unlink,
211                                              .set_symlink = ramfs_mksymlink,
212                                              .read_symlink = ramfs_read_symlink,
213                                              .rename = default_inode_rename };
214
215 const struct v_file_ops ramfs_file_ops = { .readdir = ramfs_readdir,
216                                            .close = default_file_close,
217                                            .read = default_file_read,
218                                            .read_page = default_file_read,
219                                            .write = default_file_write,
220                                            .write_page = default_file_write,
221                                            .seek = default_file_seek };