1925b0bcb1d1e7830c368e97cc0162fd8ed46925
[monolithium.git] / kernel / src / filesystem.c
1 /*
2  * filesystem.c
3  *
4  * Copyright (C) 2016 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 <filesystem.h>
21 #include <sync.h>
22 #include <exception.h>
23 #include <memory.h>
24 #include <heap.h>
25 #include <syscalls.h>
26
27 static DECLARE_LIST(fs_driver_list);
28 static DECLARE_LIST(volumes);
29 lock_t fs_driver_list_lock = 0;
30 resource_t volume_list_res = 0;
31
32 static inline int count_delimiters(const char *string)
33 {
34     int count = 0;
35     while ((string = strchr(string, PATH_DELIMITER_CHAR)) != NULL) count++;
36     return count;
37 }
38
39 void file_cleanup(file_t *file)
40 {
41     acquire_resource_exclusive(&file->volume->resource);
42     file->volume->driver->unload_file(file);
43     release_resource(&file->volume->resource);
44
45     file->volume->open_files--;
46 }
47
48 void file_instance_cleanup(file_instance_t *instance)
49 {
50     file_t *file = instance->global;
51     file->volume->driver->close_file(instance);
52
53     if (instance->mode & FILE_MODE_DELETE_ON_CLOSE)
54     {
55         if (file->volume->driver->delete_file(file->volume, file->path, FALSE) == ERR_SUCCESS)
56         {
57             file->attributes |= FILE_ATTR_DELETED;
58         }
59     }
60
61     dereference(&file->header);
62 }
63
64 void register_filesystem_driver(fs_driver_t *driver)
65 {
66     acquire_lock(&fs_driver_list_lock);
67     list_append(&fs_driver_list, &driver->list);
68     release_lock(&fs_driver_list_lock);
69 }
70
71 bool_t unregister_filesystem_driver(fs_driver_t *driver)
72 {
73     acquire_lock(&fs_driver_list_lock);
74     list_remove(&driver->list);
75     release_lock(&fs_driver_list_lock);
76
77     return TRUE;
78 }
79
80 dword_t register_mounted_volume(mounted_volume_t *volume)
81 {
82     acquire_resource_exclusive(&volume_list_res);
83
84     volume->resource = 0;
85     volume->open_files = 0;
86
87     int delimiters = count_delimiters(volume->mountpoint);
88     list_entry_t *ptr;
89
90     for (ptr = volumes.next; ptr != &volumes; ptr = ptr->next)
91     {
92         mounted_volume_t *current = CONTAINER_OF(ptr, mounted_volume_t, list);
93         if (delimiters >= count_delimiters(current->mountpoint)) break;
94     }
95
96     list_put_before(ptr, &volume->list);
97
98     release_resource(&volume_list_res);
99     return ERR_SUCCESS;
100 }
101
102 dword_t unregister_mounted_volume(mounted_volume_t *volume)
103 {
104     if (volume->open_files > 0) return ERR_BUSY;
105     acquire_resource_exclusive(&volume_list_res);
106     acquire_resource_exclusive(&volume->resource);
107
108     list_remove(&volume->list);
109
110     release_resource(&volume->resource);
111     release_resource(&volume_list_res);
112     return ERR_SUCCESS;
113 }
114
115 mounted_volume_t *get_volume_from_path(const char *path)
116 {
117     mounted_volume_t *volume = NULL;
118     list_entry_t *ptr;
119     acquire_resource_shared(&volume_list_res);
120
121     for (ptr = volumes.next; ptr != &volumes; ptr = ptr->next)
122     {
123         mounted_volume_t *vol = CONTAINER_OF(ptr, mounted_volume_t, list);
124
125         if (strstr(path, vol->mountpoint) == path
126             && (path[strlen(vol->mountpoint)] == '\0' || path[strlen(vol->mountpoint)] == PATH_DELIMITER_CHAR))
127         {
128             volume = vol;
129             break;
130         }
131     }
132
133     release_resource(&volume_list_res);
134     return volume;
135 }
136
137 dword_t normalize_path(const char *path, char *normalized_path)
138 {
139     static const char *block_dev_prefix = "BlockDevices/";
140     static const char *char_dev_prefix = "CharDevices/";
141
142     dword_t ret = ERR_SUCCESS;
143     char *path_copy;
144     char *endptr;
145
146     if (*path == '#')
147     {
148         path_copy = malloc(strlen(path + 1) + strlen(block_dev_prefix) + 1);
149         strcpy(path_copy, block_dev_prefix);
150         strcat(path_copy, path + 1);
151     }
152     else if (*path == '@')
153     {
154         path_copy = malloc(strlen(path + 1) + strlen(char_dev_prefix) + 1);
155         strcpy(path_copy, char_dev_prefix);
156         strcat(path_copy, path + 1);
157     }
158     else
159     {
160         path_copy = strdup(path);
161     }
162
163     char *token;
164     *normalized_path = '\0';
165
166     for (token = strtok(path_copy, PATH_DELIMITER_STRING, &endptr);
167          token != NULL;
168          token = strtok(NULL, PATH_DELIMITER_STRING, &endptr))
169     {
170         if (strcmp(token, ".") == 0) continue;
171         if (strcmp(token, "..") == 0)
172         {
173             char *ptr = strrchr(normalized_path, PATH_DELIMITER_CHAR);
174             if (ptr) *ptr = '\0';
175         }
176
177         if ((strlen(normalized_path) + strlen(token) + 2) > MAX_PATH)
178         {
179             ret = ERR_INVALID;
180             break;
181         }
182
183         if (normalized_path[0]) strcat(normalized_path, PATH_DELIMITER_STRING);
184         strcat(normalized_path, token);
185     }
186
187     free(path_copy);
188     return ret;
189 }
190
191 dword_t mount(const char *device, const char *mountpoint, const char *filesystem, dword_t flags)
192 {
193     dword_t ret = ERR_NOTFOUND;
194     list_entry_t *i;
195     char *safe_device;
196     char *safe_mountpoint;
197     char *safe_filesystem;
198
199     if (get_previous_mode() == USER_MODE)
200     {
201         safe_device = copy_user_string(device);
202         safe_mountpoint = copy_user_string(mountpoint);
203         safe_filesystem = copy_user_string(filesystem);
204     }
205     else
206     {
207         safe_device = (char*)device;
208         safe_mountpoint = (char*)mountpoint;
209         safe_filesystem = (char*)filesystem;
210     }
211
212     acquire_lock(&fs_driver_list_lock);
213
214     for (i = fs_driver_list.next; i != &fs_driver_list; i = i->next)
215     {
216         fs_driver_t *fs = CONTAINER_OF(i, fs_driver_t, list);
217
218         if (filesystem == NULL || strcmp(fs->name, safe_filesystem) == 0)
219         {
220             ret = fs->mount(safe_device, safe_mountpoint, flags);
221             if (ret == ERR_SUCCESS) break;
222         }
223     }
224
225     release_lock(&fs_driver_list_lock);
226
227     if (get_previous_mode() == USER_MODE)
228     {
229         free(safe_device);
230         free(safe_mountpoint);
231         free(safe_filesystem);
232     }
233
234     return ret;
235 }
236
237 dword_t unmount(const char *mountpoint)
238 {
239     acquire_resource_exclusive(&volume_list_res);
240     list_entry_t *ptr;
241     mounted_volume_t *vol = NULL;
242
243     for (ptr = volumes.next; ptr != &volumes; ptr = ptr->next)
244     {
245         mounted_volume_t *current = CONTAINER_OF(ptr, mounted_volume_t, list);
246
247         if (strcmp(current->mountpoint, mountpoint) == 0)
248         {
249             vol = current;
250             break;
251         }
252     }
253
254     release_resource(&volume_list_res);
255
256     if (vol->open_files) return ERR_BUSY;
257     return vol->driver->unmount(vol);
258 }
259
260 dword_t open_file_internal(const char *path, file_instance_t **file_instance, dword_t mode, dword_t attributes)
261 {
262     char normalized_path[MAX_PATH];
263     file_t *file = NULL;
264     file_instance_t *instance = NULL;
265
266     dword_t ret = normalize_path(path, normalized_path);
267     if (ret != ERR_SUCCESS) return ret;
268
269     mounted_volume_t *vol = get_volume_from_path(normalized_path);
270     if (vol == NULL) return ERR_NOTFOUND;
271
272
273     if (reference_by_name(normalized_path, OBJECT_FILE, (object_t**)&file))
274     {
275         if (((file->global_mode ^ mode) & (FILE_MODE_SHARE_READ | FILE_MODE_SHARE_WRITE))
276             || (!(file->global_mode & FILE_MODE_SHARE_READ) && (mode & FILE_MODE_READ))
277             || (!(file->global_mode & FILE_MODE_SHARE_WRITE) && (mode & (FILE_MODE_WRITE | FILE_MODE_TRUNCATE))))
278         {
279             dereference(&file->header);
280             return ERR_FORBIDDEN;
281         }
282     }
283     else
284     {
285         file = (file_t*)malloc(sizeof(file_t));
286         if (file == NULL) return ERR_NOMEMORY;
287
288         file->header.name = strdup(normalized_path);
289         file->header.type = OBJECT_FILE;
290         file->volume = vol;
291
292         file->path = &file->header.name[strlen(vol->mountpoint)];
293         if (*file->path == PATH_DELIMITER_CHAR) file->path++;
294
295         file->global_mode = mode;
296         file->attributes = attributes;
297
298         ret = vol->driver->load_file(&file);
299         if (ret != ERR_SUCCESS)
300         {
301             free(file->header.name);
302             free(file);
303             return ret;
304         }
305
306         ret = create_object(&file->header);
307         if (ret != ERR_SUCCESS)
308         {
309             free(file->header.name);
310             free(file);
311             return ret;
312         }
313     }
314
315     instance = (file_instance_t*)malloc(sizeof(file_instance_t));
316     if (instance == NULL)
317     {
318         dereference(&file->header);
319         return ERR_NOMEMORY;
320     }
321
322     instance->header.name = NULL;
323     instance->header.type = OBJECT_FILE_INSTANCE;
324     instance->global = file;
325     instance->mode = mode;
326
327     ret = vol->driver->open_file(&instance);
328     if (ret != ERR_SUCCESS)
329     {
330         free(instance);
331         dereference(&file->header);
332         return ERR_NOMEMORY;
333     }
334
335     ret = create_object(&instance->header);
336     if (ret == ERR_SUCCESS)
337     {
338         *file_instance = instance;
339     }
340     else
341     {
342         free(instance);
343         dereference(&file->header);
344     }
345
346     return ret;
347 }
348
349 dword_t open_file(const char *path, handle_t *handle, dword_t mode, dword_t attributes)
350 {
351     dword_t ret = ERR_SUCCESS;
352     file_instance_t *file;
353     char *safe_path;
354     handle_t safe_handle = 0;
355
356     if (get_previous_mode() == USER_MODE)
357     {
358         if (!check_usermode(handle, sizeof(handle_t))) return ERR_BADPTR;
359         safe_path = copy_user_string(path);
360         if (safe_path == NULL)
361         {
362             ret = ERR_BADPTR;
363             goto cleanup;
364         }
365     }
366     else safe_path = (char*)path;
367
368     ret = open_file_internal(safe_path, &file, mode, attributes);
369     if (ret != ERR_SUCCESS) goto cleanup;
370
371     ret = open_object(&file->header, 0, &safe_handle);
372     if (ret != ERR_SUCCESS)
373     {
374         dereference(&file->header);
375         goto cleanup;
376     }
377
378     EH_TRY
379     {
380         *handle = safe_handle;
381     }
382     EH_CATCH
383     {
384         close_object(safe_handle);
385         ret = ERR_BADPTR;
386     }
387     EH_DONE;
388
389 cleanup:
390     if (get_previous_mode() == USER_MODE) free(safe_path);
391     return ret;
392 }
393
394 dword_t delete_file(const char *path)
395 {
396     dword_t ret = ERR_SUCCESS;
397     char normalized_path[MAX_PATH];
398     char *safe_path;
399
400     if (get_previous_mode() == USER_MODE)
401     {
402         safe_path = copy_user_string(path);
403         if (safe_path == NULL) return ERR_NOMEMORY;
404     }
405     else
406     {
407         safe_path = (char*)path;
408     }
409
410     ret = normalize_path(safe_path, normalized_path);
411     if (ret != ERR_SUCCESS) goto cleanup;
412
413     mounted_volume_t *vol = get_volume_from_path(normalized_path);
414     if (vol == NULL) return ERR_NOTFOUND;
415
416     acquire_resource_exclusive(&vol->resource);
417
418     file_t *file = NULL;
419     reference_by_name(normalized_path, OBJECT_FILE, (object_t**)&file);
420
421     char *relative_path = &normalized_path[strlen(vol->mountpoint)];
422     if (*relative_path == PATH_DELIMITER_CHAR) relative_path++;
423     ret = vol->driver->delete_file(vol, relative_path, file == NULL);
424
425     if (ret == ERR_SUCCESS && file != NULL)
426     {
427         file->attributes |= FILE_ATTR_DELETED;
428         dereference(&file->header);
429     }
430
431     release_resource(&vol->resource);
432
433 cleanup:
434     if (get_previous_mode() == USER_MODE) free(safe_path);
435     return ret;
436 }
437
438 dword_t query_file(handle_t handle, file_info_type_t type, void *buffer, size_t size)
439 {
440     dword_t ret = ERR_SUCCESS;
441     void *safe_buffer;
442     file_instance_t *file = NULL;
443
444     if (get_previous_mode() == USER_MODE)
445     {
446         if (!check_usermode(buffer, size)) return ERR_BADPTR;
447
448         safe_buffer = malloc(size);
449         if (safe_buffer == NULL) return ERR_NOMEMORY;
450         memset(safe_buffer, 0, size);
451     }
452     else
453     {
454         safe_buffer = buffer;
455     }
456
457     if (!reference_by_handle(handle, OBJECT_FILE_INSTANCE, (object_t**)&file)) return ERR_INVALID;
458
459     acquire_resource_shared(&file->global->volume->resource);
460
461     switch (type)
462     {
463     case FILE_INFO_ATTRIBUTES:
464         if (size >= sizeof(dword_t)) *((dword_t*)safe_buffer) = file->global->attributes;
465         else ret = ERR_SMALLBUF;
466         break;
467
468     case FILE_INFO_NAME:
469         if (size >= (strlen(file->global->header.name) + 1)) strncpy(safe_buffer, file->global->header.name, size);
470         else ret = ERR_SMALLBUF;
471         break;
472
473     case FILE_INFO_TIME:
474         ret = ERR_NOSYSCALL; // TODO
475         break;
476
477     case FILE_INFO_SIZE:
478         if (size >= sizeof(qword_t)) *((qword_t*)safe_buffer) = file->global->size;
479         else ret = ERR_SMALLBUF;
480         break;
481
482     case FILE_INFO_OWNER:
483         if (size >= sizeof(dword_t)) *((dword_t*)safe_buffer) = file->global->owner_uid;
484         else ret = ERR_SMALLBUF;
485         break;
486
487     default:
488         ret = ERR_INVALID;
489     }
490
491     release_resource(&file->global->volume->resource);
492
493     if (get_previous_mode() == USER_MODE)
494     {
495         EH_TRY memcpy(buffer, safe_buffer, size);
496         EH_CATCH ret = ERR_BADPTR;
497         EH_DONE;
498     }
499
500     if (file) dereference(&file->header);
501     if (get_previous_mode() == USER_MODE) free(safe_buffer);
502     return ret;
503 }
504
505 dword_t set_file(handle_t handle, file_info_type_t set_type, void *buffer, size_t size)
506 {
507     void *safe_buffer;
508     file_instance_t *file = NULL;
509
510     if (get_previous_mode() == USER_MODE)
511     {
512         if (!check_usermode(buffer, size)) return ERR_BADPTR;
513
514         safe_buffer = malloc(size);
515         if (safe_buffer == NULL) return ERR_NOMEMORY;
516         memcpy(safe_buffer, buffer, size);
517     }
518     else
519     {
520         safe_buffer = buffer;
521     }
522
523     if (!reference_by_handle(handle, OBJECT_FILE_INSTANCE, (object_t**)&file)) return ERR_INVALID;
524
525     acquire_resource_exclusive(&file->global->volume->resource);
526     dword_t ret = file->global->volume->driver->set_file(file->global, set_type, safe_buffer, size);
527     release_resource(&file->global->volume->resource);
528
529     if (file) dereference(&file->header);
530     if (get_previous_mode() == USER_MODE) free(safe_buffer);
531     return ret;
532 }
533
534 dword_t list_directory(handle_t handle, char *filename, bool_t continue_scan)
535 {
536     file_instance_t *directory;
537     char safe_filename[MAX_PATH];
538
539     if (!reference_by_handle(handle, OBJECT_FILE_INSTANCE, (object_t**)&directory)) return ERR_INVALID;
540
541     acquire_resource_shared(&directory->global->volume->resource);
542     dword_t ret = directory->global->volume->driver->list_dir(directory, safe_filename, continue_scan);
543     release_resource(&directory->global->volume->resource);
544
545     if (get_previous_mode() != USER_MODE || check_usermode(filename, strlen(safe_filename) + 1))
546     {
547         EH_TRY strcpy(filename, safe_filename);
548         EH_CATCH ret = ERR_BADPTR;
549         EH_DONE;
550     }
551     else
552     {
553         ret = ERR_BADPTR;
554     }
555
556     return ret;
557 }
558
559 dword_t read_file(handle_t handle, void *buffer, qword_t offset, size_t size, size_t *bytes_read)
560 {
561     dword_t ret;
562     file_instance_t *file;
563     dword_t read_count = 0;
564     void *safe_buffer = NULL;
565
566     if (get_previous_mode() == USER_MODE)
567     {
568         if (!check_usermode(buffer, size)) return ERR_BADPTR;
569         if (!check_usermode(bytes_read, sizeof(dword_t))) return ERR_BADPTR;
570
571         ret = pin_memory(buffer, &safe_buffer, size, FALSE);
572         if (ret != ERR_SUCCESS) return ret;
573     }
574     else
575     {
576         safe_buffer = buffer;
577     }
578
579     if (!reference_by_handle(handle, OBJECT_FILE_INSTANCE, (object_t**)&file)) return ERR_INVALID;
580
581     if (!(file->mode & FILE_MODE_READ))
582     {
583         ret = ERR_FORBIDDEN;
584         goto cleanup;
585     }
586
587     acquire_resource_shared(&file->global->volume->resource);
588     ret = file->global->volume->driver->read_file(file, safe_buffer, offset, size, &read_count);
589     release_resource(&file->global->volume->resource);
590
591 cleanup:
592     EH_TRY *bytes_read = read_count;
593     EH_CATCH ret = ERR_BADPTR;
594     EH_DONE;
595
596     if (get_previous_mode() == USER_MODE) unmap_memory(safe_buffer);
597     dereference(&file->header);
598     return ret;
599 }
600
601 dword_t write_file(handle_t handle, const void *buffer, qword_t offset, size_t size, dword_t *bytes_written)
602 {
603     dword_t ret;
604     file_instance_t *file;
605     dword_t written_count = 0;
606     void *safe_buffer = NULL;
607
608     if (get_previous_mode() == USER_MODE)
609     {
610         if (!check_usermode(buffer, size)) return ERR_BADPTR;
611         if (!check_usermode(bytes_written, sizeof(dword_t))) return ERR_BADPTR;
612
613         ret = pin_memory(buffer, &safe_buffer, size, TRUE);
614         if (ret != ERR_SUCCESS) return ret;
615     }
616     else
617     {
618         safe_buffer = (void*)buffer;
619     }
620
621     if (!reference_by_handle(handle, OBJECT_FILE_INSTANCE, (object_t**)&file)) return ERR_INVALID;
622
623     if (!(file->mode & FILE_MODE_WRITE))
624     {
625         ret = ERR_FORBIDDEN;
626         goto cleanup;
627     }
628
629     if (!(file->global->volume->flags & MOUNT_FLAG_READONLY))
630     {
631         ret = ERR_WRITEPROT;
632         goto cleanup;
633     }
634
635     acquire_resource_exclusive(&file->global->volume->resource);
636     ret = file->global->volume->driver->write_file(file, safe_buffer, offset, size, &written_count);
637     release_resource(&file->global->volume->resource);
638
639     EH_TRY *bytes_written = written_count;
640     EH_DONE;
641
642 cleanup:
643     if (get_previous_mode() == USER_MODE) unmap_memory(safe_buffer);
644     dereference(&file->header);
645     return ret;
646 }