blob: ff6956de361613eadfde8616d08dece103c56cad [file] [log] [blame]
// 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;
}