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 "private.h"
6 #include "unistd.h"
7
8 #include <fuchsia/io/c/fidl.h>
9 #include <lib/fdio/io.h>
10 #include <lib/fdio/vfs.h>
11 #include <zircon/process.h>
12 #include <zircon/syscalls.h>
13
14 #define MIN_WINDOW (PAGE_SIZE * 4)
15 #define MAX_WINDOW ((size_t)64 << 20)
16
read_at(fdio_t * io,void * buf,size_t len,off_t offset,size_t * actual_len)17 static zx_status_t read_at(fdio_t* io, void* buf, size_t len, off_t offset,
18 size_t* actual_len) {
19 zx_status_t status;
20 while ((status = io->ops->read_at(io, buf, len, offset)) == ZX_ERR_SHOULD_WAIT) {
21 status = fdio_wait(io, FDIO_EVT_READABLE, ZX_TIME_INFINITE, NULL);
22 if (status != ZX_OK) {
23 return status;
24 }
25 }
26 if (status < 0) {
27 return status;
28 }
29 if (status == 0) { // EOF (?)
30 return ZX_ERR_OUT_OF_RANGE;
31 }
32 *actual_len = status;
33 return ZX_OK;
34 }
35
read_file_into_vmo(fdio_t * io,zx_handle_t * out_vmo)36 static zx_status_t read_file_into_vmo(fdio_t* io, zx_handle_t* out_vmo) {
37 zx_handle_t current_vmar_handle = zx_vmar_root_self();
38
39 fuchsia_io_NodeAttributes attr;
40 zx_status_t status = io->ops->get_attr(io, &attr);
41 if (status != ZX_OK) {
42 return ZX_ERR_BAD_HANDLE;
43 }
44
45 uint64_t size = attr.content_size;
46 uint64_t offset = 0;
47
48 status = zx_vmo_create(size, 0, out_vmo);
49 if (status != ZX_OK) {
50 return status;
51 }
52
53 while (size > 0) {
54 if (size < MIN_WINDOW) {
55 // There is little enough left that copying is less overhead
56 // than fiddling with the page tables.
57 char buffer[PAGE_SIZE];
58 size_t xfer = size < sizeof(buffer) ? size : sizeof(buffer);
59 size_t nread;
60 status = read_at(io, buffer, xfer, offset, &nread);
61 if (status != ZX_OK) {
62 zx_handle_close(*out_vmo);
63 return status;
64 }
65 status = zx_vmo_write(*out_vmo, buffer, offset, nread);
66 if (status < 0) {
67 zx_handle_close(*out_vmo);
68 return status;
69 }
70 offset += nread;
71 size -= nread;
72 } else {
73 // Map the VMO into our own address space so we can read into
74 // it directly and avoid double-buffering.
75 size_t chunk = size < MAX_WINDOW ? size : MAX_WINDOW;
76 size_t window = (chunk + PAGE_SIZE - 1) & -PAGE_SIZE;
77 uintptr_t start = 0;
78 status = zx_vmar_map(current_vmar_handle,
79 ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
80 0, *out_vmo, offset, window, &start);
81 if (status != ZX_OK) {
82 zx_handle_close(*out_vmo);
83 return status;
84 }
85 uint8_t* buffer = (void*)start;
86 while (chunk > 0) {
87 size_t nread;
88 status = read_at(io, buffer, chunk, offset, &nread);
89 if (status != ZX_OK) {
90 zx_vmar_unmap(current_vmar_handle, start, window);
91 zx_handle_close(*out_vmo);
92 return status;
93 }
94 buffer += nread;
95 offset += nread;
96 size -= nread;
97 chunk -= nread;
98 }
99 zx_vmar_unmap(current_vmar_handle, start, window);
100 }
101 }
102
103 return ZX_OK;
104 }
105
get_file_vmo(fdio_t * io,zx_handle_t * out_vmo)106 static zx_status_t get_file_vmo(fdio_t* io, zx_handle_t* out_vmo) {
107 return io->ops->get_vmo(io, fuchsia_io_VMO_FLAG_READ | fuchsia_io_VMO_FLAG_EXEC |
108 fuchsia_io_VMO_FLAG_PRIVATE, out_vmo);
109 }
110
copy_file_vmo(fdio_t * io,zx_handle_t * out_vmo)111 static zx_status_t copy_file_vmo(fdio_t* io, zx_handle_t* out_vmo) {
112 zx_status_t status = get_file_vmo(io, out_vmo);
113 if (status == ZX_OK) {
114 return ZX_OK;
115 }
116
117 zx_handle_t vmo;
118 if ((status = read_file_into_vmo(io, &vmo)) == ZX_OK) {
119 status = zx_handle_replace(
120 vmo,
121 ZX_RIGHTS_BASIC | ZX_RIGHTS_PROPERTY |
122 ZX_RIGHT_READ | ZX_RIGHT_MAP,
123 out_vmo);
124 }
125 return status;
126 }
127
128 __EXPORT
fdio_get_vmo_copy(int fd,zx_handle_t * out_vmo)129 zx_status_t fdio_get_vmo_copy(int fd, zx_handle_t* out_vmo) {
130 fdio_t* io = fd_to_io(fd);
131 if (io == NULL) {
132 return ZX_ERR_BAD_HANDLE;
133 }
134 zx_status_t status = copy_file_vmo(io, out_vmo);
135 fdio_release(io);
136 return status;
137 }
138
139 __EXPORT
fdio_get_vmo_clone(int fd,zx_handle_t * out_vmo)140 zx_status_t fdio_get_vmo_clone(int fd, zx_handle_t* out_vmo) {
141 fdio_t* io = fd_to_io(fd);
142 if (io == NULL) {
143 return ZX_ERR_BAD_HANDLE;
144 }
145 zx_status_t status = get_file_vmo(io, out_vmo);
146 fdio_release(io);
147 return status;
148 }
149
150 __EXPORT
fdio_get_vmo_exact(int fd,zx_handle_t * out_vmo)151 zx_status_t fdio_get_vmo_exact(int fd, zx_handle_t* out_vmo) {
152 fdio_t* io = fd_to_io(fd);
153 if (io == NULL) {
154 return ZX_ERR_BAD_HANDLE;
155 }
156
157 zx_status_t status = io->ops->get_vmo(io, fuchsia_io_VMO_FLAG_READ | fuchsia_io_VMO_FLAG_EXEC |
158 fuchsia_io_VMO_FLAG_EXACT, out_vmo);
159 fdio_release(io);
160
161 return status;
162 }
163