| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "private.h" |
| #include "unistd.h" |
| |
| #include <magenta/process.h> |
| #include <magenta/syscalls.h> |
| |
| #include <mxio/io.h> |
| #include <mxio/remoteio.h> |
| #include <mxio/vfs.h> |
| |
| #define MIN_WINDOW (PAGE_SIZE * 4) |
| #define MAX_WINDOW ((size_t)64 << 20) |
| |
| static mx_status_t read_at(mxio_t* io, void* buf, size_t len, off_t offset, |
| size_t* actual_len) { |
| mx_status_t status; |
| while ((status = mxio_read_at(io, buf, len, offset)) == MX_ERR_SHOULD_WAIT) { |
| status = mxio_wait(io, MXIO_EVT_READABLE, MX_TIME_INFINITE, NULL); |
| if (status != MX_OK) |
| return status; |
| } |
| if (status < 0) |
| return status; |
| if (status == 0) // EOF (?) |
| return MX_ERR_OUT_OF_RANGE; |
| *actual_len = status; |
| return MX_OK; |
| } |
| |
| static mx_status_t read_file_into_vmo(mxio_t* io, mx_handle_t* out_vmo) { |
| mx_handle_t current_vmar_handle = mx_vmar_root_self(); |
| |
| vnattr_t attr; |
| int r = io->ops->misc(io, MXRIO_STAT, 0, sizeof(attr), &attr, 0); |
| if (r < 0) |
| return MX_ERR_BAD_HANDLE; |
| if (r < (int)sizeof(attr)) |
| return MX_ERR_IO; |
| |
| uint64_t size = attr.size; |
| uint64_t offset = 0; |
| |
| mx_status_t status = mx_vmo_create(size, 0, out_vmo); |
| if (status != MX_OK) |
| return status; |
| |
| while (size > 0) { |
| if (size < MIN_WINDOW) { |
| // There is little enough left that copying is less overhead |
| // than fiddling with the page tables. |
| char buffer[PAGE_SIZE]; |
| size_t xfer = size < sizeof(buffer) ? size : sizeof(buffer); |
| size_t nread; |
| status = read_at(io, buffer, xfer, offset, &nread); |
| if (status != MX_OK) { |
| mx_handle_close(*out_vmo); |
| return status; |
| } |
| size_t n; |
| status = mx_vmo_write(*out_vmo, buffer, offset, nread, &n); |
| if (status < 0) { |
| mx_handle_close(*out_vmo); |
| return status; |
| } |
| if (n != (size_t)nread) { |
| mx_handle_close(*out_vmo); |
| return MX_ERR_IO; |
| } |
| offset += nread; |
| size -= nread; |
| } else { |
| // Map the VMO into our own address space so we can read into |
| // it directly and avoid double-buffering. |
| size_t chunk = size < MAX_WINDOW ? size : MAX_WINDOW; |
| size_t window = (chunk + PAGE_SIZE - 1) & -PAGE_SIZE; |
| uintptr_t start = 0; |
| status = mx_vmar_map( |
| current_vmar_handle, 0, *out_vmo, offset, window, |
| MX_VM_FLAG_PERM_READ | MX_VM_FLAG_PERM_WRITE, &start); |
| if (status != MX_OK) { |
| mx_handle_close(*out_vmo); |
| return status; |
| } |
| uint8_t* buffer = (void*)start; |
| while (chunk > 0) { |
| size_t nread; |
| status = read_at(io, buffer, chunk, offset, &nread); |
| if (status != MX_OK) { |
| mx_vmar_unmap(current_vmar_handle, start, window); |
| mx_handle_close(*out_vmo); |
| return status; |
| } |
| buffer += nread; |
| offset += nread; |
| size -= nread; |
| chunk -= nread; |
| } |
| mx_vmar_unmap(current_vmar_handle, start, window); |
| } |
| } |
| |
| return MX_OK; |
| } |
| |
| static mx_status_t get_file_vmo(mxio_t* io, mx_handle_t* out_vmo) { |
| mx_handle_t vmo; |
| size_t offset, len; |
| mx_status_t status = io->ops->get_vmo(io, &vmo, &offset, &len); |
| if (status != MX_OK) |
| return status; |
| // Clone a private copy of it at the offset/length returned with |
| // the handle. |
| // TODO(mcgrathr): Create a plain read only clone when the feature |
| // is implemented in the VM. |
| status = mx_vmo_clone(vmo, MX_VMO_CLONE_COPY_ON_WRITE, offset, len, out_vmo); |
| mx_handle_close(vmo); |
| return status; |
| } |
| |
| mx_status_t mxio_get_vmo(int fd, mx_handle_t* out_vmo) { |
| mxio_t* io = fd_to_io(fd); |
| if (io == NULL) |
| return MX_ERR_BAD_HANDLE; |
| |
| mx_handle_t vmo; |
| mx_status_t status = get_file_vmo(io, &vmo); |
| if (status != MX_OK) |
| status = read_file_into_vmo(io, &vmo); |
| mxio_release(io); |
| |
| if (status == MX_OK) { |
| status = mx_handle_replace( |
| vmo, |
| MX_RIGHT_READ | MX_RIGHT_EXECUTE | MX_RIGHT_MAP | |
| MX_RIGHT_TRANSFER | MX_RIGHT_DUPLICATE | |
| MX_RIGHT_GET_PROPERTY | MX_RIGHT_SET_PROPERTY, |
| out_vmo); |
| if (status != MX_OK) |
| mx_handle_close(vmo); |
| } |
| |
| return status; |
| } |
| |
| mx_status_t mxio_get_exact_vmo(int fd, mx_handle_t* out_vmo) { |
| mxio_t* io = fd_to_io(fd); |
| if (io == NULL) |
| return MX_ERR_BAD_HANDLE; |
| |
| mx_handle_t vmo; |
| size_t offset, len; |
| mx_status_t status = io->ops->get_vmo(io, &vmo, &offset, &len); |
| mxio_release(io); |
| |
| if (status != MX_OK) |
| return status; |
| |
| size_t vmo_size; |
| if (offset != 0 || mx_vmo_get_size(vmo, &vmo_size) != MX_OK || vmo_size != len) { |
| mx_handle_close(vmo); |
| return MX_ERR_NOT_FOUND; |
| } |
| |
| *out_vmo = vmo; |
| return MX_OK; |
| } |