5085571494f258dba891a136913b9cd38ca750ee
[monolithium.git] / kernel / src / drivers / fs / ram.c
1 /*
2  * ram.c
3  *
4  * Copyright (C) 2017 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "ram.h"
21 #include <heap.h>
22 #include <syscalls.h>
23
24 static dword_t ramfs_mount(const char *device, dword_t flags);
25 static dword_t ramfs_unmount(mounted_volume_t *volume);
26 static dword_t ramfs_load_file(file_t **file);
27 static dword_t ramfs_unload_file(file_t *file);
28 static dword_t ramfs_open_file(file_instance_t **instance);
29 static dword_t ramfs_close_file(file_instance_t *instance);
30 static dword_t ramfs_delete_file(mounted_volume_t *volume, const char *path, bool_t purge);
31 static dword_t ramfs_read_file(file_instance_t *file, void *buffer, qword_t offset, size_t length, size_t *bytes_read);
32 static dword_t ramfs_write_file(file_instance_t *file, const void *buffer, qword_t offset, size_t length, size_t *bytes_written);
33 static dword_t ramfs_list_dir(file_instance_t *file, char *filename, bool_t continue_scan);
34 static dword_t ramfs_set_file(file_t *file, dword_t info_type, const void *buffer, size_t size);
35
36 static fs_driver_t ramfs_driver =
37 {
38     .name = "RAM",
39     .mount = ramfs_mount,
40     .unmount = ramfs_unmount,
41     .load_file = ramfs_load_file,
42     .unload_file = ramfs_unload_file,
43     .open_file = ramfs_open_file,
44     .close_file = ramfs_close_file,
45     .delete_file = ramfs_delete_file,
46     .read_file = ramfs_read_file,
47     .write_file = ramfs_write_file,
48     .list_dir = ramfs_list_dir,
49     .set_file = ramfs_set_file,
50 };
51
52 static ramfs_node_t *ramfs_get_entry(ramfs_volume_t *volume, const char *path)
53 {
54     char path_copy[MAX_PATH];
55     char *token, *endptr;
56     strcpy(path_copy, path);
57
58     ramfs_node_t *current = &volume->root;
59
60     for (token = strtok(path_copy, PATH_DELIMITER_STRING, &endptr);
61          token != NULL;
62          token = strtok(NULL, PATH_DELIMITER_STRING, &endptr))
63     {
64         if (*token == '\0' || !(current->attributes & FILE_ATTR_DIRECTORY)) return NULL;
65
66         list_entry_t *ptr;
67         list_entry_t *files = &current->files;
68
69         for (ptr = files->next; ptr != files; ptr = ptr->next)
70         {
71             current = CONTAINER_OF(ptr, ramfs_node_t, list);
72             if (strcmp(current->name, token) == 0) break;
73         }
74
75         if (ptr == files) return NULL;
76     }
77
78     return current;
79 }
80
81 static void ramfs_purge_file(ramfs_volume_t *volume, ramfs_node_t *node)
82 {
83     volume->max_size += strlen(node->name) + 1;
84     free(node->name);
85     node->name = NULL;
86
87     if (!(node->attributes & FILE_ATTR_DIRECTORY))
88     {
89         volume->max_size += node->size;
90         free(node->contents);
91     }
92
93     volume->max_size += sizeof(ramfs_node_t);
94     free(node);
95 }
96
97 static dword_t ramfs_mount(const char *device, dword_t flags)
98 {
99     char *endptr;
100     char *device_name = strdup(device);
101
102     char *requested_size = strchr(device_name, ':');
103     if (requested_size == NULL)
104     {
105         free(device_name);
106         return ERR_INVALID;
107     }
108
109     *requested_size++ = '\0';
110
111     qword_t size = strtoull(requested_size, &endptr, 10);
112     if (!size)
113     {
114         free(device_name);
115         return ERR_INVALID;
116     }
117
118     switch (*endptr)
119     {
120     case 'K':
121         size *= 1024ULL;
122         break;
123
124     case 'M':
125         size *= 1048576ULL;
126         break;
127
128     case 'G':
129         size *= 1073741824ULL;
130         break;
131     }
132
133     device_t *virtual_device = (device_t*)malloc(sizeof(device_t));
134     if (virtual_device == NULL) return ERR_NOMEMORY;
135
136     virtual_device->flags = 0;
137     virtual_device->driver = NULL;
138     strcpy(virtual_device->name, device_name);
139     virtual_device->capacity = size;
140     virtual_device->resource = 0;
141
142     dword_t ret = register_block_device(virtual_device);
143     if (ret != ERR_SUCCESS)
144     {
145         free(virtual_device);
146         free(device_name);
147         return ret;
148     }
149
150     ramfs_volume_t *volume = (ramfs_volume_t*)malloc(sizeof(ramfs_volume_t));
151     if (volume == NULL)
152     {
153         unregister_block_device(virtual_device);
154         free(virtual_device);
155         free(device_name);
156         return ERR_NOMEMORY;
157     }
158
159     volume->header.driver = &ramfs_driver;
160     volume->header.device = NULL;
161     volume->max_size = size;
162     list_init(&volume->root.list);
163     volume->root.name = "";
164     volume->root.attributes = FILE_ATTR_DIRECTORY;
165     list_init(&volume->root.files);
166
167     ret = register_mounted_volume(&volume->header);
168
169     if (ret != ERR_SUCCESS)
170     {
171         free(volume);
172         unregister_block_device(virtual_device);
173         free(virtual_device);
174     }
175
176     free(device_name);
177     return ret;
178 }
179
180 static void ramfs_release_node_recursive(ramfs_node_t *node)
181 {
182     if (node->attributes & FILE_ATTR_DIRECTORY)
183     {
184         while (node->files.next != &node->files)
185         {
186             ramfs_node_t *child_node = CONTAINER_OF(node->files.next, ramfs_node_t, list);
187             ramfs_release_node_recursive(child_node);
188             list_remove(&child_node->list);
189             heap_free(&evictable_heap, child_node);
190         }
191     }
192     else
193     {
194         heap_free(&evictable_heap, node->contents);
195         node->contents = NULL;
196         node->size = 0ULL;
197     }
198
199     heap_free(&evictable_heap, node->name);
200     node->name = NULL;
201 }
202
203 static dword_t ramfs_unmount(mounted_volume_t *_volume)
204 {
205     ramfs_volume_t *volume = CONTAINER_OF(_volume, ramfs_volume_t, header);
206     ramfs_release_node_recursive(&volume->root);
207
208     unregister_mounted_volume(&volume->header);
209     free(volume);
210     return ERR_SUCCESS;
211 }
212
213 static dword_t ramfs_load_file(file_t **_file)
214 {
215     ramfs_file_t *file = realloc(*_file, sizeof(ramfs_file_t));
216     if (file == NULL) return ERR_NOMEMORY;
217     *_file = &file->header;
218
219     ramfs_volume_t *volume = CONTAINER_OF(file->header.volume, ramfs_volume_t, header);
220     ramfs_node_t *node = ramfs_get_entry(volume, file->header.path);
221
222     if (node == NULL)
223     {
224         if (!(file->header.global_mode & FILE_MODE_CREATE)) return ERR_NOTFOUND;
225         char parent_dir[MAX_PATH];
226
227         strcpy(parent_dir, file->header.path);
228         char *last_delimiter = strrchr(parent_dir, PATH_DELIMITER_CHAR);
229         if (last_delimiter) *last_delimiter = '\0';
230         else *parent_dir = '\0';
231
232         ramfs_node_t *dir = ramfs_get_entry(volume, parent_dir);
233         if (dir == NULL || !(dir->attributes & FILE_ATTR_DIRECTORY)) return ERR_ISNOTDIR;
234
235         node = heap_alloc(&evictable_heap, sizeof(ramfs_node_t));
236         if (node == NULL) return ERR_DISKFULL;
237
238         node->name = heap_alloc(&evictable_heap, strlen(last_delimiter + 1) + 1);
239         strcpy(node->name, last_delimiter + 1);
240
241         node->attributes = file->header.attributes;
242
243         if (node->attributes & FILE_ATTR_DIRECTORY)
244         {
245             list_init(&node->files);
246         }
247         else
248         {
249             node->contents = NULL;
250             node->size = 0;
251         }
252
253         list_append(&dir->files, &node->list);
254     }
255
256     file->header.attributes = node->attributes;
257     file->node = node;
258
259     return ERR_SUCCESS;
260 }
261
262 static dword_t ramfs_unload_file(file_t *_file)
263 {
264     ramfs_file_t *file = CONTAINER_OF(_file, ramfs_file_t, header);
265     ramfs_volume_t *volume = CONTAINER_OF(file->header.volume, ramfs_volume_t, header);
266
267     if (file->header.attributes & FILE_ATTR_DELETED)
268     {
269         ramfs_purge_file(volume, file->node);
270     }
271
272     return ERR_SUCCESS;
273 }
274
275 static dword_t ramfs_open_file(file_instance_t **_instance)
276 {
277     ramfs_file_instance_t *instance = realloc(*_instance, sizeof(ramfs_file_instance_t));
278     if (instance == NULL) return ERR_NOMEMORY;
279     *_instance = &instance->header;
280     return ERR_SUCCESS;
281 }
282
283 static dword_t ramfs_close_file(file_instance_t *instance)
284 {
285     return ERR_SUCCESS;
286 }
287
288 static dword_t ramfs_delete_file(mounted_volume_t *_volume, const char *path, bool_t purge)
289 {
290     ramfs_volume_t *volume = CONTAINER_OF(volume, ramfs_volume_t, header);
291     ramfs_node_t *node = ramfs_get_entry(volume, path);
292     if (node == NULL) return ERR_NOTFOUND;
293
294     list_remove(&node->list);
295     if (purge) ramfs_purge_file(volume, node);
296
297     return ERR_SUCCESS;
298 }
299
300 static dword_t ramfs_read_file(file_instance_t *_instance, void *buffer, qword_t offset, size_t length, size_t *bytes_read)
301 {
302     ramfs_file_t *file = CONTAINER_OF(_instance->global, ramfs_file_t, header);
303     if (length == 0) return ERR_SUCCESS;
304     if ((offset + length) > file->node->size) return ERR_BEYOND;
305
306     uintptr_t actual_length = MIN(length, file->node->size - offset);
307     memcpy(buffer, &file->node->contents[(uintptr_t)offset], actual_length);
308     *bytes_read = actual_length;
309
310     return ERR_SUCCESS;
311 }
312
313 static dword_t ramfs_write_file(file_instance_t *_instance, const void *buffer, qword_t offset, size_t length, size_t *bytes_written)
314 {
315     ramfs_file_t *file = CONTAINER_OF(_instance->global, ramfs_file_t, header);
316     ramfs_volume_t *volume = CONTAINER_OF(file->header.volume, ramfs_volume_t, header);
317     if (length == 0) return ERR_SUCCESS;
318
319     if ((offset + length) > file->node->size)
320     {
321         if ((offset + length - file->node->size) > volume->max_size) return ERR_DISKFULL;
322
323         byte_t *new_contents = heap_realloc(&evictable_heap, file->node->contents, offset + length);
324         if (!new_contents) return ERR_DISKFULL;
325
326         file->node->contents = new_contents;
327         memset(&file->node->contents[file->node->size], 0, offset + length - file->node->size);
328         file->node->size = offset + length;
329         volume->max_size -= offset + length - file->node->size;
330     }
331
332     memcpy(&file->node->contents[(uintptr_t)offset], buffer, length);
333     *bytes_written = length;
334     return ERR_SUCCESS;
335 }
336
337 static dword_t ramfs_list_dir(file_instance_t *_instance, char *filename, bool_t continue_scan)
338 {
339     ramfs_file_t *directory = CONTAINER_OF(_instance->global, ramfs_file_t, header);
340     ramfs_file_instance_t *instance = CONTAINER_OF(_instance, ramfs_file_instance_t, header);
341     if (!continue_scan) instance->dir_scan_node = directory->node->files.next;
342     if (instance->dir_scan_node == &directory->node->files) return ERR_NOMORE;
343
344     ramfs_node_t *node = CONTAINER_OF(instance->dir_scan_node, ramfs_node_t, list);
345     strcpy(filename, node->name);
346
347     instance->dir_scan_node = instance->dir_scan_node->next;
348     return ERR_SUCCESS;
349 }
350
351 static dword_t ramfs_set_file(file_t *_file, dword_t info_type, const void *buffer, size_t size)
352 {
353     ramfs_volume_t *volume = CONTAINER_OF(_file->volume, ramfs_volume_t, header);
354     ramfs_file_t *file = CONTAINER_OF(_file, ramfs_file_t, header);
355
356     switch (info_type)
357     {
358     case FILE_INFO_ATTRIBUTES:
359     case FILE_INFO_TIME:
360         // TODO
361         return ERR_NOSYSCALL;
362
363     case FILE_INFO_SIZE:
364         if (size >= sizeof(qword_t))
365         {
366             qword_t new_size = *((qword_t*)buffer);
367             if ((new_size - file->node->size) > volume->max_size) return ERR_DISKFULL;
368
369             byte_t *new_contents = heap_realloc(&evictable_heap, file->node->contents, new_size);
370             if (!new_contents) return ERR_DISKFULL;
371
372             if (new_size > file->node->size)
373             {
374                 memset(&file->node->contents[file->node->size], 0, new_size - file->node->size);
375             }
376
377             file->node->contents = new_contents;
378             volume->max_size -= new_size - file->node->size;
379             file->node->size = new_size;
380         }
381         else
382         {
383             return ERR_SMALLBUF;
384         }
385
386     default:
387         return ERR_INVALID;
388     }
389 }
390
391 dword_t ramfs_driver_load(void)
392 {
393     register_filesystem_driver(&ramfs_driver);
394     return ERR_SUCCESS;
395 }