blob: 9ea1e39da1cbead11f166c2518b64969fd821a52 [file] [log] [blame]
// Copyright 2022 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 <sys/mman.h>
#include <sys/uio.h>
#include <unistd.h>
#include <gtest/gtest.h>
#include "src/starnix/tests/syscalls/cpp/test_helper.h"
TEST(ReadWriteTest, preadv_pwritev) {
test_helper::ScopedTempFD temp_file;
ASSERT_TRUE(temp_file);
// Add some data to the file and seek back to the beginning so we know the offset took effect.
constexpr int kOffset = 7;
const char kOffsetBuffer[kOffset + 1] = "zzzzzzz";
EXPECT_EQ(kOffset, HANDLE_EINTR(write(temp_file.fd(), kOffsetBuffer, kOffset)));
constexpr size_t kWriteBufferSize = 32;
char kWriteBuffer[kWriteBufferSize + 1] = "aaaaaaaabbbbbbbbccccccccdddddddd";
struct iovec iov[] = {
{
.iov_base = &kWriteBuffer[8], // Pick 8 b's.
.iov_len = 8,
},
{
.iov_base = &kWriteBuffer[0], // a's followed by b's
.iov_len = 16,
},
{
.iov_base = &kWriteBuffer[16], // c's followed by d's.
.iov_len = 16,
},
};
// Technically this can do partial writes but our implementation doesn't support this.
constexpr ssize_t kExpectedSize = 40;
ssize_t result = HANDLE_EINTR(pwritev(temp_file.fd(), iov, std::size(iov), kOffset));
EXPECT_EQ(kExpectedSize, result);
lseek(temp_file.fd(), 0, SEEK_SET);
// Generate read the vector using the same offsets as above, but pointing into the read buffer.
char read_buffer[kWriteBufferSize + 1] = {0};
iov[0].iov_base = &read_buffer[8];
iov[1].iov_base = &read_buffer[0];
iov[2].iov_base = &read_buffer[16];
result = HANDLE_EINTR(preadv(temp_file.fd(), iov, std::size(iov), kOffset));
EXPECT_EQ(kExpectedSize, result);
read_buffer[kWriteBufferSize] = 0; // Null terminate input for comparison.
EXPECT_STREQ(read_buffer, kWriteBuffer);
}
// TODO(https://fxbug.dev/42068846) implement partial read/write support (we'll also need tests for read(),
// writev(), and readv()).
TEST(ReadWriteTest, DISABLED_PartialWrite) {
test_helper::ScopedTempFD temp_file;
ASSERT_TRUE(temp_file);
// Allocate 2 pages and remove permission from the second.
constexpr size_t kPageSize = 4096;
const size_t size = 2 * kPageSize;
void* addr = mmap(0, size, PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
ASSERT_TRUE(addr);
void* bad_addr = reinterpret_cast<char*>(addr) + kPageSize;
ASSERT_EQ(0, mprotect(bad_addr, kPageSize, PROT_NONE));
// Complete bad write.
errno = 0;
ssize_t sresult = write(temp_file.fd(), bad_addr, 2);
EXPECT_EQ(-1, sresult);
EXPECT_EQ(EFAULT, errno);
// Partial write, should write the first page and stop on the (invalid) second one.
errno = 0;
sresult = write(temp_file.fd(), addr, size);
EXPECT_EQ(ssize_t(kPageSize), sresult);
EXPECT_EQ(0, errno);
// The seek offset should reflect the last partial write.
off_t off = lseek(temp_file.fd(), 0, SEEK_CUR);
EXPECT_EQ(off_t(kPageSize), off);
munmap(addr, size);
}