4 * Copyright (C) 2016 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
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.
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.
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/>.
20 #include <filesystem.h>
22 #include <exception.h>
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;
32 static inline int count_delimiters(const char *string)
35 while ((string = strchr(string, PATH_DELIMITER_CHAR)) != NULL) count++;
39 void file_cleanup(file_t *file)
41 acquire_resource_exclusive(&file->volume->resource);
42 file->volume->driver->unload_file(file);
43 release_resource(&file->volume->resource);
45 file->volume->open_files--;
48 void file_instance_cleanup(file_instance_t *instance)
50 file_t *file = instance->global;
51 file->volume->driver->close_file(instance);
53 if (instance->mode & FILE_MODE_DELETE_ON_CLOSE)
55 if (file->volume->driver->delete_file(file->volume, file->path, FALSE) == ERR_SUCCESS)
57 file->attributes |= FILE_ATTR_DELETED;
61 dereference(&file->header);
64 void register_filesystem_driver(fs_driver_t *driver)
66 acquire_lock(&fs_driver_list_lock);
67 list_append(&fs_driver_list, &driver->list);
68 release_lock(&fs_driver_list_lock);
71 bool_t unregister_filesystem_driver(fs_driver_t *driver)
73 acquire_lock(&fs_driver_list_lock);
74 list_remove(&driver->list);
75 release_lock(&fs_driver_list_lock);
80 dword_t register_mounted_volume(mounted_volume_t *volume)
82 acquire_resource_exclusive(&volume_list_res);
85 volume->open_files = 0;
87 int delimiters = count_delimiters(volume->mountpoint);
90 for (ptr = volumes.next; ptr != &volumes; ptr = ptr->next)
92 mounted_volume_t *current = CONTAINER_OF(ptr, mounted_volume_t, list);
93 if (delimiters >= count_delimiters(current->mountpoint)) break;
96 list_put_before(ptr, &volume->list);
98 release_resource(&volume_list_res);
102 dword_t unregister_mounted_volume(mounted_volume_t *volume)
104 if (volume->open_files > 0) return ERR_BUSY;
105 acquire_resource_exclusive(&volume_list_res);
106 acquire_resource_exclusive(&volume->resource);
108 list_remove(&volume->list);
110 release_resource(&volume->resource);
111 release_resource(&volume_list_res);
115 mounted_volume_t *get_volume_from_path(const char *path)
117 mounted_volume_t *volume = NULL;
119 acquire_resource_shared(&volume_list_res);
121 for (ptr = volumes.next; ptr != &volumes; ptr = ptr->next)
123 mounted_volume_t *vol = CONTAINER_OF(ptr, mounted_volume_t, list);
125 if (strstr(path, vol->mountpoint) == path
126 && (path[strlen(vol->mountpoint)] == '\0' || path[strlen(vol->mountpoint)] == PATH_DELIMITER_CHAR))
133 release_resource(&volume_list_res);
137 dword_t normalize_path(const char *path, char *normalized_path)
139 static const char *block_dev_prefix = "BlockDevices/";
140 static const char *char_dev_prefix = "CharDevices/";
142 dword_t ret = ERR_SUCCESS;
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);
152 else if (*path == '@')
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);
160 path_copy = strdup(path);
164 *normalized_path = '\0';
166 for (token = strtok(path_copy, PATH_DELIMITER_STRING, &endptr);
168 token = strtok(NULL, PATH_DELIMITER_STRING, &endptr))
170 if (strcmp(token, ".") == 0) continue;
171 if (strcmp(token, "..") == 0)
173 char *ptr = strrchr(normalized_path, PATH_DELIMITER_CHAR);
174 if (ptr) *ptr = '\0';
177 if ((strlen(normalized_path) + strlen(token) + 2) > MAX_PATH)
183 if (normalized_path[0]) strcat(normalized_path, PATH_DELIMITER_STRING);
184 strcat(normalized_path, token);
191 dword_t mount(const char *device, const char *mountpoint, const char *filesystem, dword_t flags)
193 dword_t ret = ERR_NOTFOUND;
196 char *safe_mountpoint;
197 char *safe_filesystem;
199 if (get_previous_mode() == USER_MODE)
201 safe_device = copy_user_string(device);
202 safe_mountpoint = copy_user_string(mountpoint);
203 safe_filesystem = copy_user_string(filesystem);
207 safe_device = (char*)device;
208 safe_mountpoint = (char*)mountpoint;
209 safe_filesystem = (char*)filesystem;
212 acquire_lock(&fs_driver_list_lock);
214 for (i = fs_driver_list.next; i != &fs_driver_list; i = i->next)
216 fs_driver_t *fs = CONTAINER_OF(i, fs_driver_t, list);
218 if (filesystem == NULL || strcmp(fs->name, safe_filesystem) == 0)
220 ret = fs->mount(safe_device, safe_mountpoint, flags);
221 if (ret == ERR_SUCCESS) break;
225 release_lock(&fs_driver_list_lock);
227 if (get_previous_mode() == USER_MODE)
230 free(safe_mountpoint);
231 free(safe_filesystem);
237 dword_t unmount(const char *mountpoint)
239 acquire_resource_exclusive(&volume_list_res);
241 mounted_volume_t *vol = NULL;
243 for (ptr = volumes.next; ptr != &volumes; ptr = ptr->next)
245 mounted_volume_t *current = CONTAINER_OF(ptr, mounted_volume_t, list);
247 if (strcmp(current->mountpoint, mountpoint) == 0)
254 release_resource(&volume_list_res);
256 if (vol->open_files) return ERR_BUSY;
257 return vol->driver->unmount(vol);
260 dword_t open_file_internal(const char *path, file_instance_t **file_instance, dword_t mode, dword_t attributes)
262 char normalized_path[MAX_PATH];
264 file_instance_t *instance = NULL;
266 dword_t ret = normalize_path(path, normalized_path);
267 if (ret != ERR_SUCCESS) return ret;
269 mounted_volume_t *vol = get_volume_from_path(normalized_path);
270 if (vol == NULL) return ERR_NOTFOUND;
273 if (reference_by_name(normalized_path, OBJECT_FILE, (object_t**)&file))
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))))
279 dereference(&file->header);
280 return ERR_FORBIDDEN;
285 file = (file_t*)malloc(sizeof(file_t));
286 if (file == NULL) return ERR_NOMEMORY;
288 file->header.name = strdup(normalized_path);
289 file->header.type = OBJECT_FILE;
292 file->path = &file->header.name[strlen(vol->mountpoint)];
293 if (*file->path == PATH_DELIMITER_CHAR) file->path++;
295 file->global_mode = mode;
296 file->attributes = attributes;
298 ret = vol->driver->load_file(&file);
299 if (ret != ERR_SUCCESS)
301 free(file->header.name);
306 ret = create_object(&file->header);
307 if (ret != ERR_SUCCESS)
309 free(file->header.name);
315 instance = (file_instance_t*)malloc(sizeof(file_instance_t));
316 if (instance == NULL)
318 dereference(&file->header);
322 instance->header.name = NULL;
323 instance->header.type = OBJECT_FILE_INSTANCE;
324 instance->global = file;
325 instance->mode = mode;
327 ret = vol->driver->open_file(&instance);
328 if (ret != ERR_SUCCESS)
331 dereference(&file->header);
335 ret = create_object(&instance->header);
336 if (ret == ERR_SUCCESS)
338 *file_instance = instance;
343 dereference(&file->header);
349 dword_t open_file(const char *path, handle_t *handle, dword_t mode, dword_t attributes)
351 dword_t ret = ERR_SUCCESS;
352 file_instance_t *file;
354 handle_t safe_handle = 0;
356 if (get_previous_mode() == USER_MODE)
358 if (!check_usermode(handle, sizeof(handle_t))) return ERR_BADPTR;
359 safe_path = copy_user_string(path);
360 if (safe_path == NULL)
366 else safe_path = (char*)path;
368 ret = open_file_internal(safe_path, &file, mode, attributes);
369 if (ret != ERR_SUCCESS) goto cleanup;
371 ret = open_object(&file->header, &safe_handle);
372 if (ret != ERR_SUCCESS)
374 dereference(&file->header);
380 *handle = safe_handle;
384 close_object(safe_handle);
390 if (get_previous_mode() == USER_MODE) free(safe_path);
394 dword_t delete_file(const char *path)
396 dword_t ret = ERR_SUCCESS;
397 char normalized_path[MAX_PATH];
400 if (get_previous_mode() == USER_MODE)
402 safe_path = copy_user_string(path);
403 if (safe_path == NULL) return ERR_NOMEMORY;
407 safe_path = (char*)path;
410 ret = normalize_path(safe_path, normalized_path);
411 if (ret != ERR_SUCCESS) goto cleanup;
413 mounted_volume_t *vol = get_volume_from_path(normalized_path);
414 if (vol == NULL) return ERR_NOTFOUND;
416 acquire_resource_exclusive(&vol->resource);
419 reference_by_name(normalized_path, OBJECT_FILE, (object_t**)&file);
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);
425 if (ret == ERR_SUCCESS && file != NULL)
427 file->attributes |= FILE_ATTR_DELETED;
428 dereference(&file->header);
431 release_resource(&vol->resource);
434 if (get_previous_mode() == USER_MODE) free(safe_path);
438 dword_t query_file(handle_t handle, file_info_type_t type, void *buffer, size_t size)
440 dword_t ret = ERR_SUCCESS;
442 file_instance_t *file = NULL;
444 if (get_previous_mode() == USER_MODE)
446 if (!check_usermode(buffer, size)) return ERR_BADPTR;
448 safe_buffer = malloc(size);
449 if (safe_buffer == NULL) return ERR_NOMEMORY;
450 memset(safe_buffer, 0, size);
454 safe_buffer = buffer;
457 if (!reference_by_handle(handle, OBJECT_FILE_INSTANCE, (object_t**)&file)) return ERR_INVALID;
459 acquire_resource_shared(&file->global->volume->resource);
463 case FILE_INFO_ATTRIBUTES:
464 if (size >= sizeof(dword_t)) *((dword_t*)safe_buffer) = file->global->attributes;
465 else ret = ERR_SMALLBUF;
469 if (size >= (strlen(file->global->header.name) + 1)) strncpy(safe_buffer, file->global->header.name, size);
470 else ret = ERR_SMALLBUF;
474 ret = ERR_NOSYSCALL; // TODO
478 if (size >= sizeof(qword_t)) *((qword_t*)safe_buffer) = file->global->size;
479 else ret = ERR_SMALLBUF;
482 case FILE_INFO_OWNER:
483 if (size >= sizeof(dword_t)) *((dword_t*)safe_buffer) = file->global->owner_uid;
484 else ret = ERR_SMALLBUF;
491 release_resource(&file->global->volume->resource);
493 if (get_previous_mode() == USER_MODE)
495 EH_TRY memcpy(buffer, safe_buffer, size);
496 EH_CATCH ret = ERR_BADPTR;
500 if (file) dereference(&file->header);
501 if (get_previous_mode() == USER_MODE) free(safe_buffer);
505 dword_t set_file(handle_t handle, file_info_type_t set_type, void *buffer, size_t size)
508 file_instance_t *file = NULL;
510 if (get_previous_mode() == USER_MODE)
512 if (!check_usermode(buffer, size)) return ERR_BADPTR;
514 safe_buffer = malloc(size);
515 if (safe_buffer == NULL) return ERR_NOMEMORY;
516 memcpy(safe_buffer, buffer, size);
520 safe_buffer = buffer;
523 if (!reference_by_handle(handle, OBJECT_FILE_INSTANCE, (object_t**)&file)) return ERR_INVALID;
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);
529 if (file) dereference(&file->header);
530 if (get_previous_mode() == USER_MODE) free(safe_buffer);
534 dword_t list_directory(handle_t handle, char *filename, bool_t continue_scan)
536 file_instance_t *directory;
537 char safe_filename[MAX_PATH];
539 if (!reference_by_handle(handle, OBJECT_FILE_INSTANCE, (object_t**)&directory)) return ERR_INVALID;
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);
545 if (get_previous_mode() != USER_MODE || check_usermode(filename, strlen(safe_filename) + 1))
547 EH_TRY strcpy(filename, safe_filename);
548 EH_CATCH ret = ERR_BADPTR;
559 dword_t read_file(handle_t handle, void *buffer, qword_t offset, size_t size, size_t *bytes_read)
562 file_instance_t *file;
563 dword_t read_count = 0;
564 void *safe_buffer = NULL;
566 if (get_previous_mode() == USER_MODE)
568 if (!check_usermode(buffer, size)) return ERR_BADPTR;
569 if (!check_usermode(bytes_read, sizeof(dword_t))) return ERR_BADPTR;
571 ret = pin_memory(buffer, &safe_buffer, size, FALSE);
572 if (ret != ERR_SUCCESS) return ret;
576 safe_buffer = buffer;
579 if (!reference_by_handle(handle, OBJECT_FILE_INSTANCE, (object_t**)&file)) return ERR_INVALID;
581 if (!(file->mode & FILE_MODE_READ))
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);
592 EH_TRY *bytes_read = read_count;
593 EH_CATCH ret = ERR_BADPTR;
596 if (get_previous_mode() == USER_MODE) unmap_memory(safe_buffer);
597 dereference(&file->header);
601 dword_t write_file(handle_t handle, const void *buffer, qword_t offset, size_t size, dword_t *bytes_written)
604 file_instance_t *file;
605 dword_t written_count = 0;
606 void *safe_buffer = NULL;
608 if (get_previous_mode() == USER_MODE)
610 if (!check_usermode(buffer, size)) return ERR_BADPTR;
611 if (!check_usermode(bytes_written, sizeof(dword_t))) return ERR_BADPTR;
613 ret = pin_memory(buffer, &safe_buffer, size, TRUE);
614 if (ret != ERR_SUCCESS) return ret;
618 safe_buffer = (void*)buffer;
621 if (!reference_by_handle(handle, OBJECT_FILE_INSTANCE, (object_t**)&file)) return ERR_INVALID;
623 if (!(file->mode & FILE_MODE_WRITE))
629 if (!(file->global->volume->flags & MOUNT_FLAG_READONLY))
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);
639 EH_TRY *bytes_written = written_count;
643 if (get_previous_mode() == USER_MODE) unmap_memory(safe_buffer);
644 dereference(&file->header);