1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <errno.h>
6#include <stdatomic.h>
7#include <stdlib.h>
8
9#include <zircon/errors.h>
10#include <zircon/syscalls.h>
11
12#include "private.h"
13#include "private-remoteio.h"
14#include "unistd.h"
15
16typedef struct {
17    fdio_t io;
18    zx_handle_t h;
19} zxsvc_t;
20
21static zx_status_t zxsvc_close(fdio_t* io) {
22    zxsvc_t* svc = (zxsvc_t*) io;
23    zx_handle_close(svc->h);
24    svc->h = ZX_HANDLE_INVALID;
25    return ZX_OK;
26}
27
28static fdio_ops_t zx_svc_ops = {
29    .read = fdio_default_read,
30    .read_at = fdio_default_read_at,
31    .write = fdio_default_write,
32    .write_at = fdio_default_write_at,
33    .seek = fdio_default_seek,
34    .misc = fdio_default_misc,
35    .close = zxsvc_close,
36    .open = fdio_default_open,
37    .clone = fdio_default_clone,
38    .ioctl = fdio_default_ioctl,
39    .wait_begin = fdio_default_wait_begin,
40    .wait_end = fdio_default_wait_end,
41    .unwrap = fdio_default_unwrap,
42    .posix_ioctl = fdio_default_posix_ioctl,
43    .get_vmo = fdio_default_get_vmo,
44    .get_token = fdio_default_get_token,
45    .get_attr = fdio_default_get_attr,
46    .set_attr = fdio_default_set_attr,
47    .sync = fdio_default_sync,
48    .readdir = fdio_default_readdir,
49    .rewind = fdio_default_rewind,
50    .unlink = fdio_default_unlink,
51    .truncate = fdio_default_truncate,
52    .rename = fdio_default_rename,
53    .link = fdio_default_link,
54    .get_flags = fdio_default_get_flags,
55    .set_flags = fdio_default_set_flags,
56    .recvfrom = fdio_default_recvfrom,
57    .sendto = fdio_default_sendto,
58    .recvmsg = fdio_default_recvmsg,
59    .sendmsg = fdio_default_sendmsg,
60    .shutdown = fdio_default_shutdown,
61};
62
63__EXPORT
64fdio_t* fdio_service_create(zx_handle_t h) {
65    zxsvc_t* svc = fdio_alloc(sizeof(*svc));
66    if (svc == NULL) {
67        zx_handle_close(h);
68        return NULL;
69    }
70    svc->io.ops = &zx_svc_ops;
71    svc->io.magic = FDIO_MAGIC;
72    svc->h = h;
73    atomic_init(&svc->io.refcount, 1);
74    return &svc->io;
75}
76
77__EXPORT
78zx_status_t fdio_get_service_handle(int fd, zx_handle_t* out) {
79    mtx_lock(&fdio_lock);
80    if ((fd < 0) || (fd >= FDIO_MAX_FD) || (fdio_fdtab[fd] == NULL)) {
81        mtx_unlock(&fdio_lock);
82        return ZX_ERR_NOT_FOUND;
83    }
84    fdio_t* io = fdio_fdtab[fd];
85    io->dupcount--;
86    fdio_fdtab[fd] = NULL;
87    if (io->dupcount > 0) {
88        // still alive in other fdtab slots
89        // this fd goes away but we can't give away the handle
90        mtx_unlock(&fdio_lock);
91        fdio_release(io);
92        return ZX_ERR_UNAVAILABLE;
93    } else {
94        mtx_unlock(&fdio_lock);
95        zx_status_t r;
96        if (io->ops == &zx_svc_ops) {
97            // is an unknown service, extract handle
98            zxsvc_t* svc = (zxsvc_t*) io;
99            *out = svc->h;
100            svc->h = ZX_HANDLE_INVALID;
101            r = ZX_OK;
102        } else if (io->ops == &zx_remote_ops) {
103            // is a fuchsia.io.* service, extract handle
104            zxrio_t* rio = (zxrio_t*) io;
105            *out = rio->h;
106            rio->h = ZX_HANDLE_INVALID;
107            zx_handle_close(rio->event);
108            rio->event = ZX_HANDLE_INVALID;
109            r = ZX_OK;
110        } else {
111            r = ZX_ERR_NOT_SUPPORTED;
112            io->ops->close(io);
113        }
114        fdio_release(io);
115        return r;
116    }
117}
118
119__EXPORT
120zx_handle_t __fdio_borrow_channel(fdio_t* io) {
121    if (io == NULL) {
122        return ZX_HANDLE_INVALID;
123    }
124
125    if (io->ops == &zx_svc_ops) {
126        zxsvc_t* svc = (zxsvc_t*) io;
127        return svc->h;
128    } else if (io->ops == &zx_remote_ops) {
129        zxrio_t* rio = (zxrio_t*) io;
130        return rio->h;
131    }
132    return ZX_HANDLE_INVALID;
133}
134