summaryrefslogtreecommitdiff
path: root/src/kernel/handleset.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel/handleset.c')
-rw-r--r--src/kernel/handleset.c28
1 files changed, 28 insertions, 0 deletions
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++;