/* * Copyright (c) 2009, 2011, ETH Zurich. * All rights reserved. * * This file is distributed under the terms in the attached LICENSE file. * If you do not find this file, copies can be found by writing to: * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group. */ #include #include #include #include #include #include "fs_internal.h" #define BULK_MEM_SIZE (1U << 16) // 64kB #define BULK_BLOCK_SIZE BULK_MEM_SIZE // (it's RPC) /** * @brief an entry in the ramfs */ struct ramfs_dirent { char *name; ///< name of the file or directoyr size_t size; ///< the size of the direntry in bytes or files size_t refcount; ///< reference count for open handles struct ramfs_dirent *parent; ///< parent directory struct ramfs_dirent *next; ///< parent directory struct ramfs_dirent *prev; ///< parent directory bool is_dir; ///< flag indicationg this is a dir union { void *data; ///< file data pointer struct ramfs_dirent *dir; ///< directory pointer }; }; /** * @brief a handle to the open */ struct ramfs_handle { struct fs_handle common; char *path; bool isdir; struct ramfs_dirent *dirent; union { off_t file_pos; struct ramfs_dirent *dir_pos; }; }; struct ramfs_mount { struct ramfs_dirent *root; }; static struct ramfs_handle *handle_open(struct ramfs_dirent *d) { struct ramfs_handle *h = calloc(1, sizeof(*h)); if (h == NULL) { return NULL; } d->refcount++; h->isdir = d->is_dir; h->dirent = d; return h; } static inline void handle_close(struct ramfs_handle *h) { assert(h->dirent->refcount > 0); h->dirent->refcount--; free(h->path); free(h); } static void dirent_remove(struct ramfs_dirent *entry) { if (entry->prev == NULL) { /* entry was the first in list, update parent pointer */ if (entry->parent) { assert(entry->parent->is_dir); entry->parent->dir = entry->next; } } else { /* there are entries before that one */ entry->prev->next = entry->next; } if (entry->next) { /* update prev pointer */ entry->next->prev = entry->prev; } } static void dirent_remove_and_free(struct ramfs_dirent *entry) { dirent_remove(entry); free(entry->name); if (!entry->is_dir) { free(entry->data); } memset(entry, 0x00, sizeof(*entry)); free(entry); } static void dirent_insert(struct ramfs_dirent *parent, struct ramfs_dirent *entry) { assert(parent); assert(parent->is_dir); entry->next = NULL; entry->prev = NULL; entry->parent = parent; if (parent->dir) { entry->next = parent->dir; parent->dir->prev= entry; } parent->dir = entry; } static struct ramfs_dirent *dirent_create(const char *name, bool is_dir) { struct ramfs_dirent *d = calloc(1, sizeof(*d)); if (d == NULL) { return NULL; } d->is_dir = is_dir; d->name = strdup(name); return d; } static errval_t find_dirent(struct ramfs_dirent *root, const char *name, struct ramfs_dirent **ret_de) { if (!root->is_dir) { return FS_ERR_NOTDIR; } struct ramfs_dirent *d = root->dir; while(d) { if (strcmp(d->name, name) == 0) { *ret_de = d; return SYS_ERR_OK; } d = d->next; } return FS_ERR_NOTFOUND; } static errval_t resolve_path(struct ramfs_dirent *root, const char *path, struct ramfs_handle **ret_fh) { errval_t err; // skip leading / size_t pos = 0; if (path[0] == FS_PATH_SEP) { pos++; } struct ramfs_dirent *next_dirent; while (path[pos] != '\0') { char *nextsep = strchr(&path[pos], FS_PATH_SEP); size_t nextlen; if (nextsep == NULL) { nextlen = strlen(&path[pos]); } else { nextlen = nextsep - &path[pos]; } char pathbuf[nextlen + 1]; memcpy(pathbuf, &path[pos], nextlen); pathbuf[nextlen] = '\0'; err = find_dirent(root, pathbuf, &next_dirent); if (err_is_fail(err)) { return err; } if (!next_dirent->is_dir && nextsep != NULL) { return FS_ERR_NOTDIR; } root = next_dirent; if (nextsep == NULL) { break; } pos += nextlen + 1; } /* create the handle */ if (ret_fh) { struct ramfs_handle *fh = handle_open(root); if (fh == NULL) { return LIB_ERR_MALLOC_FAIL; } fh->path = strdup(path); //fh->common.mount = root; *ret_fh = fh; } return SYS_ERR_OK; } errval_t ramfs_open(void *st, const char *path, ramfs_handle_t *rethandle) { errval_t err; struct ramfs_mount *mount = st; struct ramfs_handle *handle; err = resolve_path(mount->root, path, &handle); if (err_is_fail(err)) { return err; } if (handle->isdir) { handle_close(handle); return FS_ERR_NOTFILE; } *rethandle = handle; return SYS_ERR_OK; } errval_t ramfs_create(void *st, const char *path, ramfs_handle_t *rethandle) { errval_t err; struct ramfs_mount *mount = st; err = resolve_path(mount->root, path, NULL); if (err_is_ok(err)) { return FS_ERR_EXISTS; } struct ramfs_handle *parent = NULL; const char *childname; // find parent directory char *lastsep = strrchr(path, FS_PATH_SEP); if (lastsep != NULL) { childname = lastsep + 1; size_t pathlen = lastsep - path; char pathbuf[pathlen + 1]; memcpy(pathbuf, path, pathlen); pathbuf[pathlen] = '\0'; // resolve parent directory err = resolve_path(mount->root, pathbuf, &parent); if (err_is_fail(err)) { return err; } else if (!parent->isdir) { return FS_ERR_NOTDIR; // parent is not a directory } } else { childname = path; } struct ramfs_dirent *dirent = dirent_create(childname, false); if (dirent == NULL) { return LIB_ERR_MALLOC_FAIL; } if (parent) { dirent_insert(parent->dirent, dirent); handle_close(parent); } else { dirent_insert(mount->root, dirent); } if (rethandle) { struct ramfs_handle *fh = handle_open(dirent); if (fh == NULL) { return LIB_ERR_MALLOC_FAIL; } fh->path = strdup(path); *rethandle = fh; } return SYS_ERR_OK; } errval_t ramfs_remove(void *st, const char *path) { errval_t err; struct ramfs_mount *mount = st; struct ramfs_handle *handle; err = resolve_path(mount->root, path, &handle); if (err_is_fail(err)) { return err; } if (handle->isdir) { return FS_ERR_NOTFILE; } struct ramfs_dirent *dirent = handle->dirent; if (dirent-> refcount != 1) { handle_close(handle); return FS_ERR_BUSY; } dirent_remove_and_free(dirent); return SYS_ERR_OK; } errval_t ramfs_read(void *st, ramfs_handle_t handle, void *buffer, size_t bytes, size_t *bytes_read) { struct ramfs_handle *h = handle; if (h->isdir) { return FS_ERR_NOTFILE; } assert(h->file_pos >= 0); if (h->dirent->data == NULL) { bytes = 0; } else if (h->dirent->size < h->file_pos) { bytes = 0; } else if (h->dirent->size < h->file_pos + bytes) { bytes = h->dirent->size - h->file_pos; assert(h->file_pos + bytes == h->dirent->size); } memcpy(buffer, h->dirent->data + h->file_pos, bytes); h->file_pos += bytes; *bytes_read = bytes; return SYS_ERR_OK; } errval_t ramfs_write(void *st, ramfs_handle_t handle, const void *buffer, size_t bytes, size_t *bytes_written) { struct ramfs_handle *h = handle; assert(h->file_pos >= 0); size_t offset = h->file_pos; if (h->isdir) { return FS_ERR_NOTFILE; } if (h->dirent->size < offset + bytes) { /* need to realloc the buffer */ void *newbuf = realloc(h->dirent->data, offset + bytes); if (newbuf == NULL) { return LIB_ERR_MALLOC_FAIL; } h->dirent->data = newbuf; } memcpy(h->dirent->data + offset, buffer, bytes); if (bytes_written) { *bytes_written = bytes; } h->file_pos += bytes; h->dirent->size += bytes; return SYS_ERR_OK; } errval_t ramfs_truncate(void *st, ramfs_handle_t handle, size_t bytes) { struct ramfs_handle *h = handle; if (h->isdir) { return FS_ERR_NOTFILE; } void *newdata = realloc(h->dirent->data, bytes); if (newdata == NULL) { return LIB_ERR_MALLOC_FAIL; } h->dirent->data = newdata; h->dirent->size = bytes; return SYS_ERR_OK; } errval_t ramfs_tell(void *st, ramfs_handle_t handle, size_t *pos) { struct ramfs_handle *h = handle; if (h->isdir) { *pos = 0; } else { *pos = h->file_pos; } return SYS_ERR_OK; } errval_t ramfs_stat(void *st, ramfs_handle_t inhandle, struct fs_fileinfo *info) { struct ramfs_handle *h = inhandle; assert(info != NULL); info->type = h->isdir ? FS_DIRECTORY : FS_FILE; info->size = h->dirent->size; return SYS_ERR_OK; } errval_t ramfs_seek(void *st, ramfs_handle_t handle, enum fs_seekpos whence, off_t offset) { struct ramfs_handle *h = handle; struct fs_fileinfo info; errval_t err; switch (whence) { case FS_SEEK_SET: assert(offset >= 0); if (h->isdir) { h->dir_pos = h->dirent->parent->dir; for (size_t i = 0; i < offset; i++) { if (h->dir_pos == NULL) { break; } h->dir_pos = h->dir_pos->next; } } else { h->file_pos = offset; } break; case FS_SEEK_CUR: if (h->isdir) { assert(!"NYI"); } else { assert(offset >= 0 || -offset <= h->file_pos); h->file_pos += offset; } break; case FS_SEEK_END: if (h->isdir) { assert(!"NYI"); } else { err = ramfs_stat(st, handle, &info); if (err_is_fail(err)) { return err; } assert(offset >= 0 || -offset <= info.size); h->file_pos = info.size + offset; } break; default: USER_PANIC("invalid whence argument to ramfs seek"); } return SYS_ERR_OK; } errval_t ramfs_close(void *st, ramfs_handle_t inhandle) { struct ramfs_handle *handle = inhandle; if (handle->isdir) { return FS_ERR_NOTFILE; } handle_close(handle); return SYS_ERR_OK; } errval_t ramfs_opendir(void *st, const char *path, ramfs_handle_t *rethandle) { errval_t err; struct ramfs_mount *mount = st; struct ramfs_handle *handle; err = resolve_path(mount->root, path, &handle); if (err_is_fail(err)) { return err; } if (!handle->isdir) { handle_close(handle); return FS_ERR_NOTDIR; } handle->dir_pos = handle->dirent->dir; *rethandle = handle; return SYS_ERR_OK; } errval_t ramfs_dir_read_next(void *st, ramfs_handle_t inhandle, char **retname, struct fs_fileinfo *info) { struct ramfs_handle *h = inhandle; if (!h->isdir) { return FS_ERR_NOTDIR; } struct ramfs_dirent *d = h->dir_pos; if (d == NULL) { return FS_ERR_INDEX_BOUNDS; } if (retname != NULL) { *retname = strdup(d->name); } if (info != NULL) { info->type = d->is_dir ? FS_DIRECTORY : FS_FILE; info->size = d->size; } h->dir_pos = d->next; return SYS_ERR_OK; } errval_t ramfs_closedir(void *st, ramfs_handle_t dhandle) { struct ramfs_handle *handle = dhandle; if (!handle->isdir) { return FS_ERR_NOTDIR; } free(handle->path); free(handle); return SYS_ERR_OK; } // fails if already present errval_t ramfs_mkdir(void *st, const char *path) { errval_t err; struct ramfs_mount *mount = st; err = resolve_path(mount->root, path, NULL); if (err_is_ok(err)) { return FS_ERR_EXISTS; } struct ramfs_handle *parent = NULL; const char *childname; // find parent directory char *lastsep = strrchr(path, FS_PATH_SEP); if (lastsep != NULL) { childname = lastsep + 1; size_t pathlen = lastsep - path; char pathbuf[pathlen + 1]; memcpy(pathbuf, path, pathlen); pathbuf[pathlen] = '\0'; // resolve parent directory err = resolve_path(mount->root, pathbuf, &parent); if (err_is_fail(err)) { handle_close(parent); return err; } else if (!parent->isdir) { handle_close(parent); return FS_ERR_NOTDIR; // parent is not a directory } } else { childname = path; } struct ramfs_dirent *dirent = dirent_create(childname, true); if (dirent == NULL) { return LIB_ERR_MALLOC_FAIL; } if (parent) { dirent_insert(parent->dirent, dirent); handle_close(parent); } else { dirent_insert(mount->root, dirent); } return SYS_ERR_OK; } errval_t ramfs_rmdir(void *st, const char *path) { errval_t err; struct ramfs_mount *mount = st; struct ramfs_handle *handle; err = resolve_path(mount->root, path, &handle); if (err_is_fail(err)) { return err; } if (!handle->isdir) { goto out; err = FS_ERR_NOTDIR; } if (handle->dirent->refcount != 1) { handle_close(handle); return FS_ERR_BUSY; } assert(handle->dirent->is_dir); if (handle->dirent->dir) { err = FS_ERR_NOTEMPTY; goto out; } dirent_remove_and_free(handle->dirent); out: free(handle); return err; } errval_t ramfs_mount(const char *uri, ramfs_mount_t *retst) { /* Setup channel and connect ot service */ /* TODO: setup channel to init for multiboot files */ struct ramfs_mount *mount = calloc(1, sizeof(struct ramfs_mount)); if (mount == NULL) { return LIB_ERR_MALLOC_FAIL; } struct ramfs_dirent *ramfs_root; ramfs_root = calloc(1, sizeof(*ramfs_root)); if (ramfs_root == NULL) { free(mount); return LIB_ERR_MALLOC_FAIL; } ramfs_root->size = 0; ramfs_root->is_dir = true; ramfs_root->name = "/"; ramfs_root->parent = NULL; mount->root = ramfs_root; *retst = mount; return SYS_ERR_OK; }