blob: e2f19e53e8367a311dbdaffdbf331dc714446494 [file] [log] [blame]
// Copyright 2020 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 <lib/zx/event.h>
#include <lib/zx/stream.h>
#include <lib/zx/vmo.h>
#include <limits.h>
#include <stdio.h>
#include <unistd.h>
#include <zircon/syscalls/object.h>
#include <thread>
#include <vector>
#include <zxtest/zxtest.h>
#include "zircon/system/utest/core/pager/userpager.h"
extern "C" __WEAK zx_handle_t get_root_resource(void);
namespace {
void CheckRights(const zx::stream& stream, zx_rights_t expected_rights, const char* message) {
zx_info_handle_basic_t info = {};
EXPECT_OK(stream.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr));
printf("CheckRights: %s\n", message);
EXPECT_EQ(expected_rights, info.rights);
}
TEST(StreamTestCase, Create) {
zx_handle_t raw_stream = ZX_HANDLE_INVALID;
ASSERT_EQ(ZX_ERR_BAD_HANDLE, zx_stream_create(0, ZX_HANDLE_INVALID, 0, &raw_stream));
zx::event event;
ASSERT_OK(zx::event::create(0, &event));
ASSERT_EQ(ZX_ERR_WRONG_TYPE, zx_stream_create(0, event.get(), 0, &raw_stream));
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(zx_system_get_page_size() * 4, 0, &vmo));
size_t content_size = 0u;
ASSERT_OK(vmo.set_property(ZX_PROP_VMO_CONTENT_SIZE, &content_size, sizeof(content_size)));
static_assert(!(ZX_DEFAULT_STREAM_RIGHTS & ZX_RIGHT_WRITE),
"Streams are not writable by default");
static_assert(!(ZX_DEFAULT_STREAM_RIGHTS & ZX_RIGHT_READ), "Streams are not readable by default");
zx::stream stream;
ASSERT_EQ(ZX_ERR_INVALID_ARGS, zx::stream::create(-42, vmo, 0, &stream));
ASSERT_OK(zx::stream::create(0, vmo, 0, &stream));
CheckRights(stream, ZX_DEFAULT_STREAM_RIGHTS, "Default");
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_READ, vmo, 0, &stream));
CheckRights(stream, ZX_DEFAULT_STREAM_RIGHTS | ZX_RIGHT_READ, "ZX_STREAM_MODE_READ");
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_WRITE, vmo, 0, &stream));
CheckRights(stream, ZX_DEFAULT_STREAM_RIGHTS | ZX_RIGHT_WRITE, "ZX_STREAM_MODE_WRITE");
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_READ | ZX_STREAM_MODE_WRITE, vmo, 0, &stream));
CheckRights(stream, ZX_DEFAULT_STREAM_RIGHTS | ZX_RIGHT_READ | ZX_RIGHT_WRITE,
"ZX_STREAM_MODE_READ | ZX_STREAM_MODE_WRITE");
{
zx::vmo read_only;
vmo.duplicate(ZX_RIGHT_READ, &read_only);
ASSERT_OK(zx::stream::create(0, read_only, 0, &stream));
CheckRights(stream, ZX_DEFAULT_STREAM_RIGHTS, "read_only: Default");
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_READ, read_only, 0, &stream));
CheckRights(stream, ZX_DEFAULT_STREAM_RIGHTS | ZX_RIGHT_READ, "read_only: ZX_STREAM_MODE_READ");
ASSERT_EQ(ZX_ERR_ACCESS_DENIED,
zx::stream::create(ZX_STREAM_MODE_WRITE, read_only, 0, &stream));
ASSERT_EQ(ZX_ERR_ACCESS_DENIED, zx::stream::create(ZX_STREAM_MODE_READ | ZX_STREAM_MODE_WRITE,
read_only, 0, &stream));
}
{
zx::vmo write_only;
vmo.duplicate(ZX_RIGHT_WRITE, &write_only);
ASSERT_OK(zx::stream::create(0, write_only, 0, &stream));
CheckRights(stream, ZX_DEFAULT_STREAM_RIGHTS, "write_only: Default");
ASSERT_EQ(ZX_ERR_ACCESS_DENIED,
zx::stream::create(ZX_STREAM_MODE_READ, write_only, 0, &stream));
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_WRITE, write_only, 0, &stream));
CheckRights(stream, ZX_DEFAULT_STREAM_RIGHTS | ZX_RIGHT_WRITE,
"write_only: ZX_STREAM_MODE_WRITE");
ASSERT_EQ(ZX_ERR_ACCESS_DENIED, zx::stream::create(ZX_STREAM_MODE_READ | ZX_STREAM_MODE_WRITE,
write_only, 0, &stream));
}
{
zx::vmo none;
vmo.duplicate(0, &none);
ASSERT_OK(zx::stream::create(0, none, 0, &stream));
CheckRights(stream, ZX_DEFAULT_STREAM_RIGHTS, "none: Default");
ASSERT_EQ(ZX_ERR_ACCESS_DENIED, zx::stream::create(ZX_STREAM_MODE_READ, none, 0, &stream));
ASSERT_EQ(ZX_ERR_ACCESS_DENIED, zx::stream::create(ZX_STREAM_MODE_WRITE, none, 0, &stream));
ASSERT_EQ(ZX_ERR_ACCESS_DENIED,
zx::stream::create(ZX_STREAM_MODE_READ | ZX_STREAM_MODE_WRITE, none, 0, &stream));
}
}
TEST(StreamTestCase, Seek) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(zx_system_get_page_size() * 4, 0, &vmo));
size_t content_size = 42u;
ASSERT_OK(vmo.set_property(ZX_PROP_VMO_CONTENT_SIZE, &content_size, sizeof(content_size)));
zx::stream stream;
zx_off_t result = 81u;
ASSERT_OK(zx::stream::create(0, vmo, 0, &stream));
ASSERT_EQ(ZX_ERR_ACCESS_DENIED, stream.seek(ZX_STREAM_SEEK_ORIGIN_START, 0, &result));
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_READ, vmo, 9, &stream));
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_CURRENT, 0, &result));
EXPECT_EQ(9u, result);
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_WRITE, vmo, 518, &stream));
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_CURRENT, 0, &result));
EXPECT_EQ(518u, result);
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_READ | ZX_STREAM_MODE_WRITE, vmo, 0, &stream));
ASSERT_EQ(ZX_ERR_INVALID_ARGS, stream.seek(34893, 12, &result));
ASSERT_EQ(ZX_ERR_INVALID_ARGS, stream.seek(ZX_STREAM_SEEK_ORIGIN_START, -10, &result));
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_START, 10, &result));
EXPECT_EQ(10u, result);
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_START, 12, &result));
EXPECT_EQ(12u, result);
ASSERT_EQ(ZX_ERR_INVALID_ARGS, stream.seek(ZX_STREAM_SEEK_ORIGIN_CURRENT, -21, &result));
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_CURRENT, 3, &result));
EXPECT_EQ(15u, result);
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_CURRENT, -15, &result));
EXPECT_EQ(0u, result);
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_CURRENT, INT64_MAX, &result));
EXPECT_EQ(static_cast<zx_off_t>(INT64_MAX), result);
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_CURRENT, 1038, &result));
EXPECT_EQ(static_cast<zx_off_t>(INT64_MAX) + 1038, result);
ASSERT_EQ(ZX_ERR_INVALID_ARGS, stream.seek(ZX_STREAM_SEEK_ORIGIN_CURRENT, INT64_MAX, &result));
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_END, 0, &result));
EXPECT_EQ(content_size, result);
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_END, -11, &result));
EXPECT_EQ(31u, result);
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_END, -13, &result));
EXPECT_EQ(29u, result);
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_END, -content_size, &result));
EXPECT_EQ(0u, result);
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_END, 24, &result));
EXPECT_EQ(66u, result);
ASSERT_EQ(ZX_ERR_INVALID_ARGS, stream.seek(ZX_STREAM_SEEK_ORIGIN_END, -1238, &result));
content_size = UINT64_MAX;
ASSERT_OK(vmo.set_property(ZX_PROP_VMO_CONTENT_SIZE, &content_size, sizeof(content_size)));
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_END, -11, &result));
EXPECT_EQ(UINT64_MAX - 11u, result);
ASSERT_EQ(ZX_ERR_INVALID_ARGS, stream.seek(ZX_STREAM_SEEK_ORIGIN_END, 5, &result));
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_START, 0, nullptr));
}
const char kAlphabet[] = "abcdefghijklmnopqrstuvwxyz";
TEST(StreamTestCase, ReadV) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo));
ASSERT_OK(vmo.write(kAlphabet, 0u, strlen(kAlphabet)));
size_t content_size = 26u;
ASSERT_OK(vmo.set_property(ZX_PROP_VMO_CONTENT_SIZE, &content_size, sizeof(content_size)));
zx::stream stream;
char buffer[16] = {};
zx_iovec_t vec = {
.buffer = buffer,
.capacity = sizeof(buffer),
};
size_t actual = 42u;
ASSERT_OK(zx::stream::create(0, vmo, 0, &stream));
ASSERT_EQ(ZX_ERR_ACCESS_DENIED, stream.readv(0, &vec, 1, &actual));
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_READ, vmo, 0, &stream));
vec.capacity = 7u;
ASSERT_OK(stream.readv(0, &vec, 1, &actual));
EXPECT_EQ(7u, actual);
EXPECT_STREQ("abcdefg", buffer);
memset(buffer, 0, sizeof(buffer));
ASSERT_EQ(ZX_ERR_INVALID_ARGS, stream.readv(24098, &vec, 1, &actual));
ASSERT_EQ(ZX_ERR_INVALID_ARGS, stream.readv(0, nullptr, 1, &actual));
ASSERT_EQ(ZX_ERR_INVALID_ARGS, stream.readv(0, nullptr, 0, &actual));
vec.capacity = 3u;
ASSERT_OK(stream.readv(0, &vec, 1, nullptr));
EXPECT_STREQ("hij", buffer);
memset(buffer, 0, sizeof(buffer));
vec.buffer = nullptr;
vec.capacity = 7u;
ASSERT_EQ(ZX_ERR_NOT_FOUND, stream.readv(0, &vec, 1, &actual));
vec.buffer = buffer;
const size_t kVectorCount = 7;
zx_iovec_t multivec[kVectorCount] = {};
for (size_t i = 0; i < kVectorCount; ++i) {
multivec[i].buffer = buffer;
multivec[i].capacity = INT64_MAX;
}
ASSERT_EQ(ZX_ERR_INVALID_ARGS, stream.readv(0, multivec, kVectorCount, &actual));
vec.capacity = sizeof(buffer);
ASSERT_OK(stream.readv(0, &vec, 1, &actual));
memset(buffer, 0, sizeof(buffer));
content_size = 6u;
ASSERT_OK(vmo.set_property(ZX_PROP_VMO_CONTENT_SIZE, &content_size, sizeof(content_size)));
vec.capacity = 3u;
actual = 42u;
ASSERT_OK(stream.readv(0, &vec, 1, &actual));
EXPECT_EQ(0u, actual);
memset(buffer, 0, sizeof(buffer));
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_START, 0, nullptr));
vec.capacity = 12u;
actual = 42u;
ASSERT_OK(stream.readv(0, &vec, 1, &actual));
EXPECT_EQ(6u, actual);
EXPECT_STREQ("abcdef", buffer);
memset(buffer, 0, sizeof(buffer));
content_size = 26u;
ASSERT_OK(vmo.set_property(ZX_PROP_VMO_CONTENT_SIZE, &content_size, sizeof(content_size)));
for (size_t i = 0; i < kVectorCount; ++i) {
multivec[kVectorCount - i - 1].buffer = &buffer[i];
multivec[kVectorCount - i - 1].capacity = 1;
}
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_START, 0, nullptr));
ASSERT_OK(stream.readv(0, multivec, kVectorCount, &actual));
EXPECT_EQ(kVectorCount, actual);
EXPECT_STREQ("gfedcba", buffer);
memset(buffer, 0, sizeof(buffer));
}
std::string GetData(const zx::vmo& vmo) {
std::vector<char> buffer(zx_system_get_page_size(), '\0');
EXPECT_OK(vmo.read(buffer.data(), 0, buffer.size()));
return std::string(buffer.data());
}
TEST(StreamTestCase, WriteV) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo));
ASSERT_OK(vmo.write(kAlphabet, 0u, strlen(kAlphabet)));
size_t content_size = 26u;
ASSERT_OK(vmo.set_property(ZX_PROP_VMO_CONTENT_SIZE, &content_size, sizeof(content_size)));
zx::stream stream;
char buffer[17] = "0123456789ABCDEF";
zx_iovec_t vec = {
.buffer = buffer,
.capacity = sizeof(buffer),
};
size_t actual = 42u;
ASSERT_OK(zx::stream::create(0, vmo, 0, &stream));
ASSERT_EQ(ZX_ERR_ACCESS_DENIED, stream.writev(0, &vec, 1, &actual));
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_WRITE, vmo, 0, &stream));
vec.capacity = 7u;
ASSERT_OK(stream.writev(0, &vec, 1, &actual));
EXPECT_EQ(7u, actual);
EXPECT_STREQ("0123456hijklmnopqrstuvwxyz", GetData(vmo).c_str());
ASSERT_OK(vmo.write(kAlphabet, 0u, strlen(kAlphabet)));
ASSERT_EQ(ZX_ERR_INVALID_ARGS, stream.writev(24098, &vec, 1, &actual));
ASSERT_EQ(ZX_ERR_INVALID_ARGS, stream.writev(0, nullptr, 1, &actual));
ASSERT_EQ(ZX_ERR_INVALID_ARGS, stream.writev(0, nullptr, 0, &actual));
vec.capacity = 3u;
ASSERT_OK(stream.writev(0, &vec, 1, nullptr));
EXPECT_STREQ("abcdefg012klmnopqrstuvwxyz", GetData(vmo).c_str());
ASSERT_OK(vmo.write(kAlphabet, 0u, strlen(kAlphabet)));
vec.buffer = nullptr;
vec.capacity = 7u;
ASSERT_EQ(ZX_ERR_NOT_FOUND, stream.writev(0, &vec, 1, &actual));
vec.buffer = buffer;
const size_t kVectorCount = 7;
zx_iovec_t multivec[kVectorCount] = {};
for (size_t i = 0; i < kVectorCount; ++i) {
multivec[i].buffer = buffer;
multivec[i].capacity = INT64_MAX;
}
ASSERT_EQ(ZX_ERR_INVALID_ARGS, stream.writev(0, multivec, kVectorCount, &actual));
for (size_t i = 0; i < kVectorCount; ++i) {
multivec[kVectorCount - i - 1].buffer = &buffer[i];
multivec[kVectorCount - i - 1].capacity = 1;
}
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_START, 0, nullptr));
ASSERT_OK(stream.writev(0, multivec, kVectorCount, &actual));
EXPECT_EQ(kVectorCount, actual);
EXPECT_STREQ("6543210hijklmnopqrstuvwxyz", GetData(vmo).c_str());
ASSERT_OK(vmo.write(kAlphabet, 0u, strlen(kAlphabet)));
}
size_t GetContentSize(const zx::vmo& vmo) {
size_t content_size = 45684651u;
EXPECT_OK(vmo.get_property(ZX_PROP_VMO_CONTENT_SIZE, &content_size, sizeof(content_size)));
return content_size;
}
TEST(StreamTestCase, WriteExtendsContentSize) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo));
ASSERT_OK(vmo.write(kAlphabet, 0u, strlen(kAlphabet)));
size_t content_size = 3u;
ASSERT_OK(vmo.set_property(ZX_PROP_VMO_CONTENT_SIZE, &content_size, sizeof(content_size)));
zx::stream stream;
char buffer[17] = "0123456789ABCDEF";
zx_iovec_t vec = {
.buffer = buffer,
.capacity = sizeof(buffer),
};
size_t actual = 42u;
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_WRITE, vmo, 0, &stream));
vec.capacity = 7u;
ASSERT_OK(stream.writev(0, &vec, 1, &actual));
EXPECT_EQ(7u, actual);
EXPECT_STREQ("0123456hijklmnopqrstuvwxyz", GetData(vmo).c_str());
EXPECT_EQ(7u, GetContentSize(vmo));
ASSERT_OK(vmo.write(kAlphabet, 0u, strlen(kAlphabet)));
vec.capacity = 2u;
ASSERT_OK(stream.writev(0, &vec, 1, &actual));
EXPECT_EQ(2u, actual);
EXPECT_STREQ("abcdefg01jklmnopqrstuvwxyz", GetData(vmo).c_str());
EXPECT_EQ(9u, GetContentSize(vmo));
ASSERT_OK(vmo.write(kAlphabet, 0u, strlen(kAlphabet)));
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_START, 0, nullptr));
vec.capacity = 10u;
for (size_t i = 1; i * 10 < zx_system_get_page_size(); ++i) {
ASSERT_OK(stream.writev(0, &vec, 1, &actual));
EXPECT_EQ(10u, actual);
}
EXPECT_EQ(4090u, GetContentSize(vmo));
actual = 9823u;
ASSERT_OK(stream.writev(0, &vec, 1, &actual));
EXPECT_EQ(6u, actual);
EXPECT_EQ(4096u, GetContentSize(vmo));
char scratch[17] = {};
ASSERT_OK(vmo.read(scratch, 4090u, 6u));
EXPECT_STREQ("012345", scratch);
actual = 9823u;
ASSERT_EQ(ZX_ERR_NO_SPACE, stream.writev(0, &vec, 1, &actual));
EXPECT_EQ(9823u, actual);
EXPECT_EQ(4096u, GetContentSize(vmo));
}
TEST(StreamTestCase, WriteExtendsVMOSize) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), ZX_VMO_RESIZABLE, &vmo));
size_t content_size = 0u;
ASSERT_OK(vmo.set_property(ZX_PROP_VMO_CONTENT_SIZE, &content_size, sizeof(content_size)));
zx::stream stream;
char buffer[17] = "0123456789ABCDEF";
zx_iovec_t vec = {
.buffer = buffer,
.capacity = sizeof(buffer),
};
size_t actual = 42u;
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_WRITE, vmo, 0, &stream));
vec.capacity = 10u;
for (size_t i = 1; i * 10 < zx_system_get_page_size(); ++i) {
ASSERT_OK(stream.writev(0, &vec, 1, &actual));
EXPECT_EQ(10u, actual);
}
EXPECT_EQ(4090u, GetContentSize(vmo));
actual = 9823u;
ASSERT_OK(stream.writev(0, &vec, 1, &actual));
EXPECT_EQ(10u, actual);
EXPECT_EQ(4100u, GetContentSize(vmo));
uint64_t vmo_size = 839u;
ASSERT_OK(vmo.get_size(&vmo_size));
EXPECT_EQ(zx_system_get_page_size() * 2, vmo_size);
vec.capacity = UINT64_MAX;
actual = 5423u;
ASSERT_EQ(ZX_ERR_FILE_BIG, stream.writev(0, &vec, 1, &actual));
EXPECT_EQ(5423u, actual);
ASSERT_OK(vmo.get_size(&vmo_size));
EXPECT_EQ(zx_system_get_page_size() * 2, vmo_size);
}
TEST(StreamTestCase, ReadVAt) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo));
ASSERT_OK(vmo.write(kAlphabet, 0u, strlen(kAlphabet)));
size_t content_size = 26u;
ASSERT_OK(vmo.set_property(ZX_PROP_VMO_CONTENT_SIZE, &content_size, sizeof(content_size)));
zx::stream stream;
char buffer[16] = {};
zx_iovec_t vec = {
.buffer = buffer,
.capacity = sizeof(buffer),
};
size_t actual = 42u;
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_READ, vmo, 0, &stream));
vec.capacity = 7u;
ASSERT_OK(stream.readv_at(0, 24u, &vec, 1, &actual));
EXPECT_EQ(2u, actual);
EXPECT_STREQ("yz", buffer);
memset(buffer, 0, sizeof(buffer));
zx_off_t seek = 39u;
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_CURRENT, 0, &seek));
EXPECT_EQ(0u, seek);
ASSERT_OK(stream.readv_at(0, 36u, &vec, 1, &actual));
EXPECT_EQ(0u, actual);
EXPECT_STREQ("", buffer);
memset(buffer, 0, sizeof(buffer));
ASSERT_OK(stream.readv_at(0, 3645651u, &vec, 1, &actual));
EXPECT_EQ(0u, actual);
EXPECT_STREQ("", buffer);
memset(buffer, 0, sizeof(buffer));
}
TEST(StreamTestCase, WriteVAt) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo));
ASSERT_OK(vmo.write(kAlphabet, 0u, strlen(kAlphabet)));
size_t content_size = 26u;
ASSERT_OK(vmo.set_property(ZX_PROP_VMO_CONTENT_SIZE, &content_size, sizeof(content_size)));
zx::stream stream;
char buffer[17] = "0123456789ABCDEF";
zx_iovec_t vec = {
.buffer = buffer,
.capacity = sizeof(buffer),
};
size_t actual = 42u;
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_WRITE, vmo, 0, &stream));
vec.capacity = 3u;
ASSERT_OK(stream.writev_at(0, 7, &vec, 1, &actual));
EXPECT_EQ(3u, actual);
EXPECT_STREQ("abcdefg012klmnopqrstuvwxyz", GetData(vmo).c_str());
ASSERT_OK(vmo.write(kAlphabet, 0u, strlen(kAlphabet)));
zx_off_t seek = 39u;
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_CURRENT, 0, &seek));
EXPECT_EQ(0u, seek);
vec.capacity = 10u;
actual = 9823u;
ASSERT_EQ(ZX_ERR_NO_SPACE, stream.writev_at(0, 4100u, &vec, 1, &actual));
EXPECT_EQ(9823u, actual);
ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), ZX_VMO_RESIZABLE, &vmo));
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_WRITE, vmo, 0, &stream));
vec.capacity = 10u;
actual = 9823u;
ASSERT_OK(stream.writev_at(0, 4090, &vec, 1, &actual));
EXPECT_EQ(10u, actual);
EXPECT_EQ(4100u, GetContentSize(vmo));
uint64_t vmo_size = 839u;
ASSERT_OK(vmo.get_size(&vmo_size));
EXPECT_EQ(zx_system_get_page_size() * 2, vmo_size);
vec.capacity = UINT64_MAX;
actual = 5423u;
ASSERT_EQ(ZX_ERR_FILE_BIG, stream.writev_at(0, 5414u, &vec, 1, &actual));
EXPECT_EQ(5423u, actual);
ASSERT_OK(vmo.get_size(&vmo_size));
EXPECT_EQ(zx_system_get_page_size() * 2, vmo_size);
}
TEST(StreamTestCase, ReadVectorAlias) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo));
ASSERT_OK(vmo.write(kAlphabet, 0u, strlen(kAlphabet)));
size_t content_size = 26u;
ASSERT_OK(vmo.set_property(ZX_PROP_VMO_CONTENT_SIZE, &content_size, sizeof(content_size)));
zx::stream stream;
const size_t kVectorCount = 7;
zx_iovec_t multivec[kVectorCount] = {};
for (size_t i = 0; i < kVectorCount; ++i) {
multivec[i].buffer = multivec; // Notice the alias.
multivec[i].capacity = sizeof(multivec);
}
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_READ, vmo, 0, &stream));
ASSERT_OK(stream.readv(0, multivec, kVectorCount, nullptr));
}
TEST(StreamTestCase, Append) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), 0, &vmo));
ASSERT_OK(vmo.write(kAlphabet, 0u, strlen(kAlphabet)));
size_t content_size = 26u;
ASSERT_OK(vmo.set_property(ZX_PROP_VMO_CONTENT_SIZE, &content_size, sizeof(content_size)));
zx::stream stream;
char buffer[17] = "0123456789ABCDEF";
zx_iovec_t vec = {
.buffer = buffer,
.capacity = sizeof(buffer),
};
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_WRITE, vmo, 0, &stream));
{
zx_info_stream_t info{};
ASSERT_OK(stream.get_info(ZX_INFO_STREAM, &info, sizeof(info), nullptr, nullptr));
EXPECT_EQ(ZX_STREAM_MODE_WRITE, info.options);
EXPECT_EQ(0u, info.seek);
EXPECT_EQ(26u, info.content_size);
}
vec.capacity = 7u;
size_t actual = 42u;
ASSERT_OK(stream.writev(ZX_STREAM_APPEND, &vec, 1, &actual));
EXPECT_EQ(7u, actual);
EXPECT_STREQ("abcdefghijklmnopqrstuvwxyz0123456", GetData(vmo).c_str());
{
zx_info_stream_t info{};
ASSERT_OK(stream.get_info(ZX_INFO_STREAM, &info, sizeof(info), nullptr, nullptr));
EXPECT_EQ(ZX_STREAM_MODE_WRITE, info.options);
EXPECT_EQ(33u, info.seek);
EXPECT_EQ(33u, info.content_size);
vec.capacity = 26u;
for (size_t size = info.content_size; size + vec.capacity < zx_system_get_page_size();
size += vec.capacity) {
ASSERT_OK(stream.writev(ZX_STREAM_APPEND, &vec, 1, &actual));
EXPECT_EQ(vec.capacity, actual);
}
}
{
zx_info_stream_t info{};
ASSERT_OK(stream.get_info(ZX_INFO_STREAM, &info, sizeof(info), nullptr, nullptr));
EXPECT_GT(zx_system_get_page_size(), info.content_size);
ASSERT_OK(stream.writev(ZX_STREAM_APPEND, &vec, 1, &actual));
EXPECT_EQ(zx_system_get_page_size() - info.content_size, actual);
}
ASSERT_EQ(ZX_ERR_NO_SPACE, stream.writev(ZX_STREAM_APPEND, &vec, 1, &actual));
vec.capacity = UINT64_MAX;
ASSERT_EQ(ZX_ERR_FILE_BIG, stream.writev(ZX_STREAM_APPEND, &vec, 1, &actual));
}
TEST(StreamTestCase, ExtendFillsWithZeros) {
const size_t kPageCount = 6;
const size_t kVmoSize = zx_system_get_page_size() * kPageCount;
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(kVmoSize, 0, &vmo));
size_t content_size = 0u;
ASSERT_OK(vmo.set_property(ZX_PROP_VMO_CONTENT_SIZE, &content_size, sizeof(content_size)));
zx::stream stream;
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_WRITE, vmo, 0, &stream));
char scratch[zx_system_get_page_size()];
memset(scratch, 'x', sizeof(scratch));
for (size_t i = 0; i < kPageCount; ++i) {
ASSERT_OK(vmo.write(scratch, zx_system_get_page_size() * i, sizeof(scratch)));
}
char buffer[17] = "0123456789ABCDEF";
zx_iovec_t vec = {
.buffer = buffer,
.capacity = 4,
};
size_t actual = 0u;
ASSERT_OK(stream.writev_at(0, zx_system_get_page_size() * 2 - 2, &vec, 1, &actual));
ASSERT_EQ(4, actual);
memset(scratch, 'a', sizeof(scratch));
ASSERT_OK(vmo.read(scratch, 0, sizeof(scratch)));
for (size_t i = 0; i < zx_system_get_page_size(); ++i) {
ASSERT_EQ(0, scratch[i], "The %zu byte should be zero.", i);
}
memset(scratch, 'a', sizeof(scratch));
ASSERT_OK(vmo.read(scratch, zx_system_get_page_size(), sizeof(scratch)));
for (size_t i = 0; i < zx_system_get_page_size() - 2; ++i) {
ASSERT_EQ(0, scratch[i], "The %zu byte of the second page should be zero.", i);
}
ASSERT_EQ('0', scratch[zx_system_get_page_size() - 2]);
ASSERT_EQ('1', scratch[zx_system_get_page_size() - 1]);
memset(scratch, 'a', sizeof(scratch));
ASSERT_OK(vmo.read(scratch, zx_system_get_page_size() * 2, sizeof(scratch)));
ASSERT_EQ('2', scratch[0]);
ASSERT_EQ('3', scratch[1]);
ASSERT_EQ('x', scratch[2]);
ASSERT_EQ('x', scratch[3]);
ASSERT_OK(stream.seek(ZX_STREAM_SEEK_ORIGIN_START, zx_system_get_page_size() * 5 - 2, nullptr));
actual = 0;
ASSERT_OK(stream.writev(0, &vec, 1, &actual));
ASSERT_EQ(4, actual);
memset(scratch, 'a', sizeof(scratch));
ASSERT_OK(vmo.read(scratch, zx_system_get_page_size() * 2, sizeof(scratch)));
ASSERT_EQ('2', scratch[0]);
ASSERT_EQ('3', scratch[1]);
ASSERT_EQ(0, scratch[2]);
ASSERT_EQ(0, scratch[3]);
memset(scratch, 'a', sizeof(scratch));
ASSERT_OK(vmo.read(scratch, zx_system_get_page_size() * 3, sizeof(scratch)));
for (size_t i = 0; i < zx_system_get_page_size(); ++i) {
ASSERT_EQ(0, scratch[i], "The %zu byte of the third page should be zero.", i);
}
memset(scratch, 'a', sizeof(scratch));
ASSERT_OK(vmo.read(scratch, zx_system_get_page_size() * 4, sizeof(scratch)));
for (size_t i = 0; i < zx_system_get_page_size() - 2; ++i) {
ASSERT_EQ(0, scratch[i], "The %zu byte of the fourth page should be zero.", i);
}
ASSERT_EQ('0', scratch[zx_system_get_page_size() - 2]);
ASSERT_EQ('1', scratch[zx_system_get_page_size() - 1]);
memset(scratch, 'a', sizeof(scratch));
ASSERT_OK(vmo.read(scratch, zx_system_get_page_size() * 5, sizeof(scratch)));
ASSERT_EQ('2', scratch[0]);
ASSERT_EQ('3', scratch[1]);
ASSERT_EQ('x', scratch[2]);
ASSERT_EQ('x', scratch[3]);
}
TEST(StreamTestCase, ShrinkGuard) {
pager_tests::UserPager pager;
ASSERT_TRUE(pager.Init());
pager_tests::Vmo* vmo;
ASSERT_TRUE(pager.CreateVmoWithOptions(80, ZX_VMO_RESIZABLE, &vmo));
zx::stream stream;
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_READ, vmo->vmo(), 0, &stream));
std::thread thread([&] {
char buffer[16] = {};
zx_iovec_t vec = {
.buffer = buffer,
.capacity = sizeof(buffer),
};
size_t actual = 42u;
ASSERT_OK(stream.readv(0, &vec, 1, &actual));
});
pager.WaitForPageRead(vmo, 0, 1, ZX_TIME_INFINITE);
// This should block until the read has finished.
bool done = false;
std::thread set_size_thread([&] {
vmo->vmo().set_size(0);
done = true;
});
// This is a halting problem so all we can do is sleep.
usleep(100000);
ASSERT_FALSE(done);
pager.SupplyPages(vmo, 0, 1);
set_size_thread.join();
thread.join();
// The only way to test that shrink holds up reads is to repeatedly issue reads and shrinks and we
// expect the reads to not fail with an error.
// Note that the user pager does not need to supply pages anymore. The vmo was resized to 0 above,
// so the kernel will supply zero pages.
std::thread read_thread([&] {
for (int i = 0; i < 100; ++i) {
char buffer[16] = {};
zx_iovec_t vec = {
.buffer = buffer,
.capacity = sizeof(buffer),
};
size_t actual = 42u;
ASSERT_OK(stream.readv(0, &vec, 1, &actual));
ASSERT_TRUE(actual == 0 || actual == 16);
}
});
std::thread shrink_thread([&] {
for (int i = 0; i < 100; ++i) {
vmo->vmo().set_size(16);
vmo->vmo().set_size(0);
}
});
read_thread.join();
shrink_thread.join();
}
// Regression test for fxbug.dev/94454. Writing to an offset that requires expansion should not
// result in an overflow when computing the new required VMO size.
TEST(StreamTestCase, ExpandOverflow) {
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(zx_system_get_page_size(), ZX_VMO_RESIZABLE, &vmo));
zx::stream stream;
ASSERT_OK(zx::stream::create(ZX_STREAM_MODE_WRITE, vmo, 0, &stream));
char buffer[] = "AAAA";
zx_iovec_t vec = {
.buffer = buffer,
.capacity = 4,
};
size_t actual = 0u;
// This write will require a content size of 0xfffffffffffffffc, which when rounded up to the page
// boundary to compute the VMO size will overflow. So content expansion should fail.
EXPECT_EQ(ZX_ERR_OUT_OF_RANGE, stream.writev_at(0, 0xfffffffffffffff8, &vec, 1, &actual));
EXPECT_EQ(0, actual);
// Verify the VMO and content sizes.
uint64_t vmo_size;
ASSERT_OK(vmo.get_size(&vmo_size));
EXPECT_EQ(zx_system_get_page_size(), vmo_size);
uint64_t content_size;
ASSERT_OK(vmo.get_prop_content_size(&content_size));
EXPECT_EQ(zx_system_get_page_size(), content_size);
// Verify that a subsequent resize succeeds.
EXPECT_OK(vmo.set_size(2 * zx_system_get_page_size()));
ASSERT_OK(vmo.get_size(&vmo_size));
EXPECT_EQ(2 * zx_system_get_page_size(), vmo_size);
ASSERT_OK(vmo.get_prop_content_size(&content_size));
EXPECT_EQ(2 * zx_system_get_page_size(), content_size);
}
} // namespace