| // Copyright 2019 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 <fuchsia/io/c/fidl.h> |
| #include <lib/zxio/inception.h> |
| #include <lib/zxio/null.h> |
| #include <lib/zxio/ops.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <zircon/syscalls.h> |
| |
| typedef struct zxio_vmo { |
| // The |zxio_t| control structure for this object. |
| zxio_t io; |
| |
| // The underlying VMO that stores the data. |
| zx_handle_t vmo; |
| |
| // The size of the VMO in bytes. |
| // |
| // This value is read from the kernel during |zxio_vmo_init|, is always a |
| // multiple of the page size, and is never changed. |
| zx_off_t size; |
| |
| // The current seek offset within the file. |
| // |
| // Protected by |lock|. |
| zx_off_t offset; |
| |
| // The lock that protects |offset|. |
| // |
| // TODO: Migrate to sync_mutex_t. |
| mtx_t lock; |
| } zxio_vmo_t; |
| |
| static_assert(sizeof(zxio_vmo_t) <= sizeof(zxio_storage_t), |
| "zxio_vmo_t must fit inside zxio_storage_t."); |
| |
| static zx_status_t zxio_vmo_close(zxio_t* io) { |
| zxio_vmo_t* file = reinterpret_cast<zxio_vmo_t*>(io); |
| zx_handle_t vmo = file->vmo; |
| file->vmo = ZX_HANDLE_INVALID; |
| zx_handle_close(vmo); |
| return ZX_OK; |
| } |
| |
| static zx_status_t zxio_vmo_release(zxio_t* io, zx_handle_t* out_handle) { |
| zxio_vmo_t* file = reinterpret_cast<zxio_vmo_t*>(io); |
| zx_handle_t vmo = file->vmo; |
| file->vmo = ZX_HANDLE_INVALID; |
| *out_handle = vmo; |
| return ZX_OK; |
| } |
| |
| static zx_status_t zxio_vmo_clone(zxio_t* io, zx_handle_t* out_handle) { |
| zxio_vmo_t* file = reinterpret_cast<zxio_vmo_t*>(io); |
| return zx_handle_duplicate(file->vmo, ZX_RIGHT_SAME_RIGHTS, out_handle); |
| } |
| |
| static zx_status_t zxio_vmo_attr_get(zxio_t* io, zxio_node_attr_t* out_attr) { |
| zxio_vmo_t* file = reinterpret_cast<zxio_vmo_t*>(io); |
| memset(out_attr, 0, sizeof(*out_attr)); |
| out_attr->mode = S_IFREG | S_IRUSR; |
| out_attr->content_size = file->size; |
| return ZX_OK; |
| } |
| |
| static zx_status_t zxio_vmo_read(zxio_t* io, void* buffer, size_t capacity, |
| size_t* out_actual) { |
| zxio_vmo_t* file = reinterpret_cast<zxio_vmo_t*>(io); |
| |
| mtx_lock(&file->lock); |
| if (capacity > (file->size - file->offset)) { |
| capacity = file->size - file->offset; |
| } |
| zx_off_t offset = file->offset; |
| file->offset += capacity; |
| mtx_unlock(&file->lock); |
| |
| zx_status_t status = zx_vmo_read(file->vmo, buffer, offset, capacity); |
| if (status == ZX_OK) { |
| *out_actual = capacity; |
| } |
| return status; |
| } |
| |
| static zx_status_t zxio_vmo_read_at(zxio_t* io, size_t offset, void* buffer, |
| size_t capacity, size_t* out_actual) { |
| zxio_vmo_t* file = reinterpret_cast<zxio_vmo_t*>(io); |
| |
| if (offset > file->size) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| if (capacity > file->size - offset) { |
| capacity = file->size - offset; |
| } |
| |
| zx_status_t status = zx_vmo_read(file->vmo, buffer, offset, capacity); |
| if (status == ZX_OK) { |
| *out_actual = capacity; |
| } |
| return status; |
| } |
| |
| zx_status_t zxio_vmo_write(zxio_t* io, const void* buffer, size_t capacity, |
| size_t* out_actual) { |
| zxio_vmo_t* file = reinterpret_cast<zxio_vmo_t*>(io); |
| |
| mtx_lock(&file->lock); |
| if (capacity > (file->size - file->offset)) { |
| capacity = file->size - file->offset; |
| } |
| zx_off_t offset = file->offset; |
| file->offset += capacity; |
| mtx_unlock(&file->lock); |
| |
| zx_status_t status = zx_vmo_write(file->vmo, buffer, offset, capacity); |
| if (status == ZX_OK) { |
| *out_actual = capacity; |
| } |
| return status; |
| } |
| |
| zx_status_t zxio_vmo_write_at(zxio_t* io, size_t offset, const void* buffer, |
| size_t capacity, size_t* out_actual) { |
| zxio_vmo_t* file = reinterpret_cast<zxio_vmo_t*>(io); |
| |
| if (offset > file->size) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| if (capacity > file->size - offset) { |
| capacity = file->size - offset; |
| } |
| |
| zx_status_t status = zx_vmo_write(file->vmo, buffer, offset, capacity); |
| if (status == ZX_OK) { |
| *out_actual = capacity; |
| } |
| return status; |
| } |
| |
| static zx_status_t zxio_vmo_seek(zxio_t* io, size_t offset, |
| zxio_seek_origin_t start, |
| size_t* out_offset) { |
| zxio_vmo_t* file = reinterpret_cast<zxio_vmo_t*>(io); |
| |
| mtx_lock(&file->lock); |
| zx_off_t at = 0u; |
| switch (start) { |
| case fuchsia_io_SeekOrigin_START: |
| at = offset; |
| break; |
| case fuchsia_io_SeekOrigin_CURRENT: |
| at = file->offset + offset; |
| break; |
| case fuchsia_io_SeekOrigin_END: |
| at = file->size + offset; |
| break; |
| default: |
| mtx_unlock(&file->lock); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (at > file->size) { |
| at = ZX_ERR_OUT_OF_RANGE; |
| } else { |
| file->offset = at; |
| } |
| mtx_unlock(&file->lock); |
| |
| *out_offset = at; |
| return ZX_OK; |
| } |
| |
| static constexpr zxio_ops_t zxio_vmo_ops = []() { |
| zxio_ops_t ops = zxio_default_ops; |
| ops.close = zxio_vmo_close; |
| ops.release = zxio_vmo_release; |
| ops.clone = zxio_vmo_clone; |
| ops.attr_get = zxio_vmo_attr_get; |
| ops.read = zxio_vmo_read; |
| ops.read_at = zxio_vmo_read_at; |
| ops.write = zxio_vmo_write; |
| ops.write_at = zxio_vmo_write_at; |
| ops.seek = zxio_vmo_seek; |
| return ops; |
| }(); |
| |
| zx_status_t zxio_vmo_init(zxio_storage_t* storage, zx_handle_t vmo, |
| zx_off_t offset) { |
| uint64_t size = 0u; |
| zx_status_t status = zx_vmo_get_size(vmo, &size); |
| if (status != ZX_OK) { |
| zx_handle_close(vmo); |
| return status; |
| } |
| |
| zxio_vmo_t* file = reinterpret_cast<zxio_vmo_t*>(storage); |
| zxio_init(&file->io, &zxio_vmo_ops); |
| if (offset > size) |
| offset = size; |
| file->vmo = vmo; |
| file->size = size; |
| file->offset = offset; |
| mtx_init(&file->lock, mtx_plain); |
| return ZX_OK; |
| } |