diff options
-rw-r--r-- | src/cmd/tests/kernel/miscsyscall.c | 35 | ||||
-rw-r--r-- | src/kernel/handle.c | 6 | ||||
-rw-r--r-- | src/kernel/handle.h | 7 | ||||
-rw-r--r-- | src/kernel/handleset.c | 28 | ||||
-rw-r--r-- | src/libk/include/camellia/flags.h | 2 |
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 |