summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordzwdz2024-05-11 22:05:04 +0200
committerdzwdz2024-05-11 22:05:04 +0200
commit15e02a470652a5d4ef87485b5ae12afc06dc53f8 (patch)
treee27e410885abe69d4fd20211a27e0196392993a4
parentec3d2400db15e6911138d88f95cae141a9da2130 (diff)
kernel: DUP_RDONLY and DUP_WRONLY
I probably should've tested DUP_WRONLY too, now that I think about it. TODO?
-rw-r--r--src/cmd/tests/kernel/miscsyscall.c35
-rw-r--r--src/kernel/handle.c6
-rw-r--r--src/kernel/handle.h7
-rw-r--r--src/kernel/handleset.c28
-rw-r--r--src/libk/include/camellia/flags.h2
5 files changed, 75 insertions, 3 deletions
diff --git a/src/cmd/tests/kernel/miscsyscall.c b/src/cmd/tests/kernel/miscsyscall.c
index 1317720..883fcee 100644
--- a/src/cmd/tests/kernel/miscsyscall.c
+++ b/src/cmd/tests/kernel/miscsyscall.c
@@ -326,6 +326,40 @@ static void test_getnull(void) {
close(h2);
}
+static void test_dup_ro(void) {
+ char buf[2];
+
+ /* runs two times, trying to close either the original handle or the dup'd
+ * one. */
+ for (int i = 0; i < 2; i++) {
+ hid_t h = _sys_open(TMPFILEPATH, strlen(TMPFILEPATH), OPEN_CREATE | OPEN_RW);
+ test(0 <= h);
+ hid_t h2 = _sys_dup(h, 0, DUP_SEARCH | DUP_RDONLY);
+ test(0 <= h2);
+
+ test(_sys_write(h, "hi", 2, 0, 0) == 2);
+ test(_sys_write(h2, "no", 2, 0, 0) == -EACCES);
+
+ test(_sys_read(h, buf, 2, 0) == 2);
+ test(memcmp(buf, "hi", 2) == 0);
+ test(_sys_read(h2, buf, 2, 0) == 2);
+ test(memcmp(buf, "hi", 2) == 0);
+
+ if (i == 0) {
+ _sys_close(h);
+ test(_sys_read(h2, buf, 2, 0) == 2);
+ test(memcmp(buf, "hi", 2) == 0);
+ _sys_close(h2);
+ } else {
+ _sys_close(h2);
+ test(_sys_write(h, "ay", 2, 0, 0) == 2);
+ test(_sys_read(h, buf, 2, 0) == 2);
+ test(memcmp(buf, "ay", 2) == 0);
+ _sys_close(h);
+ }
+ }
+}
+
void r_k_miscsyscall(void) {
run_test(test_await);
run_test(test_await2);
@@ -338,4 +372,5 @@ void r_k_miscsyscall(void) {
run_test(test_badopen);
run_test(test_timer);
run_test(test_getnull);
+ run_test(test_dup_ro);
}
diff --git a/src/kernel/handle.c b/src/kernel/handle.c
index ca4c4c4..882a4fa 100644
--- a/src/kernel/handle.c
+++ b/src/kernel/handle.c
@@ -19,12 +19,16 @@ void handle_close(Handle *h) {
if (--(h->refcount) > 0) return;
if (h->type == HANDLE_FILE) {
- vfsreq_create((VfsReq) {
+ if (h->base) {
+ handle_close(h->base);
+ } else {
+ vfsreq_create((VfsReq) {
.type = VFSOP_CLOSE,
.id = h->file_id,
.caller = NULL,
.backend = h->backend,
});
+ }
} else if (h->type == HANDLE_PIPE) {
assert(!h->pipe.queued);
if (h->pipe.sister) {
diff --git a/src/kernel/handle.h b/src/kernel/handle.h
index d502d5d..af9d45a 100644
--- a/src/kernel/handle.h
+++ b/src/kernel/handle.h
@@ -13,16 +13,19 @@ enum handle_type {
HANDLE_NULL,
};
+// TODO use unions
+// when making changes, keep in mind that dup creates copies of HANDLE_FILEs
struct Handle {
enum handle_type type;
- VfsBackend *backend; // HANDLE_FILE | HANDLE_FS_FRONT
- void __user *file_id; // only applicable to HANDLE_FILE
+ VfsBackend *backend; /* HANDLE_FILE | HANDLE_FS_FRONT */
+ void __user *file_id; /* only applicable to HANDLE_FILE */
bool readable, writeable; /* HANDLE_FILE | HANDLE_PIPE */
VfsReq *req; /* HANDLE_FS_REQ */
struct {
Proc *queued;
Handle *sister; // the other end, not included in refcount
} pipe;
+ Handle *base; /* used by dup'd HANDLE_FILEs. */
size_t refcount;
};
diff --git a/src/kernel/handleset.c b/src/kernel/handleset.c
index a25f5c7..fc2d426 100644
--- a/src/kernel/handleset.c
+++ b/src/kernel/handleset.c
@@ -89,6 +89,34 @@ hs_dup(HandleSet *hs, hid_t from, hid_t to, int flags)
fromh = hs_get(hs, from);
if (*toh) handle_close(*toh);
+
+ if (flags & (DUP_RDONLY | DUP_WRONLY)) {
+ if (!fromh || fromh->type != HANDLE_FILE) {
+ /* only errors out after closing the old handle */
+ *toh = NULL;
+ return -ENOSYS;
+ }
+
+ /* figure out if we need a need handle */
+ bool different = false;
+ different = different || ((flags & DUP_RDONLY) && fromh->writeable);
+ different = different || ((flags & DUP_WRONLY) && fromh->readable);
+
+ if (different) {
+ Handle *new = handle_init(HANDLE_FILE);
+ new->backend = fromh->backend;
+ if (new->backend) new->backend->usehcnt++;
+ new->file_id = fromh->file_id;
+ new->readable = fromh->readable && !(flags & DUP_WRONLY);
+ new->writeable = fromh->writeable && !(flags & DUP_RDONLY);
+ new->base = fromh;
+ fromh->refcount++;
+
+ *toh = new;
+ return to;
+ }
+ }
+
*toh = fromh;
if (fromh) fromh->refcount++;
diff --git a/src/libk/include/camellia/flags.h b/src/libk/include/camellia/flags.h
index a708f73..472215b 100644
--- a/src/libk/include/camellia/flags.h
+++ b/src/libk/include/camellia/flags.h
@@ -13,6 +13,8 @@
#define FSR_DELEGATE 1
#define DUP_SEARCH 1
+#define DUP_RDONLY 2
+#define DUP_WRONLY 4
#define OPEN_READ 1
#define OPEN_WRITE 2