summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/kernel/arch/amd64/driver/fsroot.c28
-rw-r--r--src/kernel/vfs/request.c4
-rw-r--r--src/shared/fsutil.c55
-rw-r--r--src/shared/include/camellia/fsutil.h13
-rw-r--r--src/user/app/init/driver/tmpfs.c27
-rw-r--r--src/user/bootstrap/tar.c15
-rw-r--r--src/user/lib/fs/misc.c2
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);