diff options
author | dzwdz | 2022-07-29 16:16:24 +0200 |
---|---|---|
committer | dzwdz | 2022-07-29 16:16:24 +0200 |
commit | c6424fbc55298399f133ca1ede11e1f0b4a5c824 (patch) | |
tree | d8dd1c160a7184a99ea3c72779dce95b4bbee692 /src | |
parent | a0b16620e1699504e8d21a481e019dec40d7ee1b (diff) |
use a shared fs_normslice() function to handle offsets
Diffstat (limited to 'src')
-rw-r--r-- | src/kernel/arch/amd64/driver/fsroot.c | 28 | ||||
-rw-r--r-- | src/kernel/vfs/request.c | 4 | ||||
-rw-r--r-- | src/shared/fsutil.c | 55 | ||||
-rw-r--r-- | src/shared/include/camellia/fsutil.h | 13 | ||||
-rw-r--r-- | src/user/app/init/driver/tmpfs.c | 27 | ||||
-rw-r--r-- | src/user/bootstrap/tar.c | 15 | ||||
-rw-r--r-- | src/user/lib/fs/misc.c | 2 |
7 files changed, 99 insertions, 45 deletions
diff --git a/src/kernel/arch/amd64/driver/fsroot.c b/src/kernel/arch/amd64/driver/fsroot.c index 1e3dc1f..c4c3de4 100644 --- a/src/kernel/arch/amd64/driver/fsroot.c +++ b/src/kernel/arch/amd64/driver/fsroot.c @@ -1,4 +1,5 @@ #include <camellia/errno.h> +#include <camellia/fsutil.h> #include <kernel/arch/amd64/ata.h> #include <kernel/arch/amd64/driver/fsroot.h> #include <kernel/mem/virt.h> @@ -22,30 +23,9 @@ static bool exacteq(struct vfs_request *req, const char *str) { return req->input.len == len && !memcmp(req->input.buf_kern, str, len); } -/* truncates the length */ -static void req_preprocess(struct vfs_request *req, size_t max_len) { - if (req->offset < 0) { - // TODO negative offsets - req->offset = 0; - } - - if (req->offset >= capped_cast32(max_len)) { - req->input.len = 0; - req->output.len = 0; - req->offset = max_len; - return; - } - - req->input.len = min(req->input.len, max_len - req->offset); - req->output.len = min(req->output.len, max_len - req->offset); - - assert(req->input.len + req->offset <= max_len); - assert(req->input.len + req->offset <= max_len); -} - static int req_readcopy(struct vfs_request *req, const void *buf, size_t len) { assert(req->type == VFSOP_READ); - req_preprocess(req, len); + fs_normslice(&req->offset, &req->output.len, len, false); virt_cpy_to( req->caller->pages, req->output.buf, buf + req->offset, req->output.len); @@ -100,7 +80,7 @@ static int handle(struct vfs_request *req) { } case HANDLE_ATA: case HANDLE_ATA+1: case HANDLE_ATA+2: case HANDLE_ATA+3: - if (req->offset < 0) return 0; + if (req->offset < 0) return -1; char buf[512]; uint32_t sector = req->offset / 512; size_t len = min(req->output.len, 512 - ((size_t)req->offset & 511)); @@ -114,7 +94,7 @@ static int handle(struct vfs_request *req) { switch (id) { case HANDLE_VGA: { void *vga = (void*)0xB8000; - req_preprocess(req, 80*25*2); + fs_normslice(&req->offset, &req->input.len, 80*25*2, false); if (!virt_cpy_from(req->caller->pages, vga + req->offset, req->input.buf, req->input.len)) { diff --git a/src/kernel/vfs/request.c b/src/kernel/vfs/request.c index 83a104f..0164dfd 100644 --- a/src/kernel/vfs/request.c +++ b/src/kernel/vfs/request.c @@ -109,7 +109,7 @@ void vfs_backend_user_accept(struct vfs_request *req) { struct process *handler; struct fs_wait_response res = {0}; struct virt_cpy_error cpyerr; - int len = 0; + int len; assert(req && req->backend && req->backend->user.handler); handler = req->backend->user.handler; @@ -131,6 +131,8 @@ void vfs_backend_user_accept(struct vfs_request *req) { vfsreq_finish_short(req, -EFAULT); return; } + } else { + len = req->output.len; } res.len = len; diff --git a/src/shared/fsutil.c b/src/shared/fsutil.c new file mode 100644 index 0000000..a1dabfd --- /dev/null +++ b/src/shared/fsutil.c @@ -0,0 +1,55 @@ +#include <camellia/fsutil.h> +#include <limits.h> + +// TODO shared assert +#define assert(...) {} + +void fs_normslice(long *restrict offset, size_t *restrict length, size_t max, bool expand) +{ + assert(max <= (size_t)LONG_MAX); + + if (*offset < 0) { + /* Negative offsets are relative to EOF + 1. + * Thus: + * write(-1) writes right after the file ends; atomic append + * write(-n) writes, overriding the last (n-1) bytes + * read(-n) reads the last (n-1) bytes + */ + *offset += max + 1; + if (*offset < 0) { + /* cursor went before the file, EOF */ + *length = *offset = 0; + goto end; + } + } + + if (expand) { + /* This is a write() to a file with a dynamic size. + * We don't care if it goes past the current size, because the + * driver can handle expanding it. */ + } else { + /* This operation can't extend the file, it's either: + * - any read() + * - a write() to a file with a static size (e.g. a framebuffer) + * *offset and *length describe a slice of a buffer with length max, + * so their sum must not overflow it. */ + if ((size_t)*offset <= max) { + size_t maxlen = max - (size_t)*offset; + if (true || *length > maxlen) + *length = maxlen; + } else { + /* regular EOF */ + *length = *offset = 0; + goto end; + } + } + +end: + if (*length > 0) { + assert(0 <= *offset); + if (!expand) + assert(*offset + *length <= max); + } else { + /* EOF, *offset is undefined. */ + } +} diff --git a/src/shared/include/camellia/fsutil.h b/src/shared/include/camellia/fsutil.h new file mode 100644 index 0000000..eb2b23e --- /dev/null +++ b/src/shared/include/camellia/fsutil.h @@ -0,0 +1,13 @@ +#pragma once +#include <stdbool.h> +#include <stddef.h> + +/** Normalizes the offset and length passed to a fs into safe values. + * If returns *length == 0, *offset is meaningless. + * + * @param expand Can this operation expand the target file? + * true if writing to a file with an adjustable size + * false if reading any sort of file + * or writing to a file with static size + */ +void fs_normslice(long *restrict offset, size_t *restrict length, size_t max, bool expand); diff --git a/src/user/app/init/driver/tmpfs.c b/src/user/app/init/driver/tmpfs.c index d9f73ab..e4c079c 100644 --- a/src/user/app/init/driver/tmpfs.c +++ b/src/user/app/init/driver/tmpfs.c @@ -1,3 +1,4 @@ +#include <camellia/fsutil.h> #include <camellia/syscalls.h> #include <shared/mem.h> #include <stddef.h> @@ -64,6 +65,7 @@ void tmpfs_drv(void) { case VFSOP_READ: ptr = (void*)res.id; if (ptr == &special_root) { + // TODO directory offset handling size_t buf_pos = 0; size_t to_skip = res.offset; @@ -85,11 +87,8 @@ void tmpfs_drv(void) { } _syscall_fs_respond(buf, buf_pos, 0); } else { - // TODO offset - if (res.offset) - _syscall_fs_respond(NULL, 0, 0); - else - _syscall_fs_respond(ptr->buf, ptr->size, 0); + fs_normslice(&res.offset, &res.len, ptr->size, false); + _syscall_fs_respond(ptr->buf + res.offset, res.len, 0); break; } break; @@ -114,13 +113,17 @@ void tmpfs_drv(void) { ptr->capacity = 256; } - size_t len = res.len; - if (len > ptr->capacity - res.offset) - len = ptr->capacity - res.offset; - memcpy(ptr->buf + res.offset, buf, len); - if (ptr->size < res.offset + len) - ptr->size = res.offset + len; - _syscall_fs_respond(NULL, len, 0); + fs_normslice(&res.offset, &res.len, ptr->size, true); + if (res.offset + res.len >= ptr->capacity) { + // TODO + _syscall_fs_respond(NULL, -1, 0); + break; + } + + memcpy(ptr->buf + res.offset, buf, res.len); + if (ptr->size < res.offset + res.len) + ptr->size = res.offset + res.len; + _syscall_fs_respond(NULL, res.len, 0); break; default: diff --git a/src/user/bootstrap/tar.c b/src/user/bootstrap/tar.c index 75883e6..c690f2c 100644 --- a/src/user/bootstrap/tar.c +++ b/src/user/bootstrap/tar.c @@ -1,5 +1,6 @@ #include "tar.h" #include <camellia/flags.h> +#include <camellia/fsutil.h> #include <camellia/syscalls.h> #include <shared/mem.h> #include <stdint.h> @@ -68,17 +69,17 @@ static void tar_read(struct fs_wait_response *res, void *base, size_t base_len) case '\0': case '0': /* normal files */ size = tar_size(meta); - if (res->offset < 0 || res->offset > size) { - // TODO support negative offsets - _syscall_fs_respond(NULL, -1, 0); - } else { - _syscall_fs_respond(meta + 512 + res->offset, size - res->offset, 0); - } + fs_normslice(&res->offset, &res->len, size, false); + _syscall_fs_respond(meta + 512 + res->offset, res->len, 0); break; case '5': /* directory */ - meta_len = strlen(meta); + if (res->offset < 0) { + _syscall_fs_respond(NULL, -1, 0); + break; + } size_t to_skip = res->offset; + meta_len = strlen(meta); /* find files in dir */ for (size_t off = 0; off < base_len;) { diff --git a/src/user/lib/fs/misc.c b/src/user/lib/fs/misc.c index 3a732af..32464b3 100644 --- a/src/user/lib/fs/misc.c +++ b/src/user/lib/fs/misc.c @@ -132,7 +132,7 @@ void fs_dir_inject(const char *path) { break; case VFSOP_READ: - if (res.offset > 0) _syscall_fs_respond(NULL, 0, 0); // TODO working offsets + if (res.offset != 0) _syscall_fs_respond(NULL, -1, 0); // TODO working offsets int out_len = data->inject_len; memcpy(buf, data->inject, out_len); |