blob: 775e59724f6b8a585cce7f3a459170d99838139e [file] [edit]
/*
* Copyright (c) 2013 The Native Client 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 <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
static char const *g_dirname = "/tmp/pwrite_test";
/*
* At least one test case bake in knowledge of relative sizes of these
* quotes.
*/
static char const quote[] =
"From a little after two oclock until almost sundown of the long still "
"hot weary dead September afternoon they sat in what Miss Coldfield still "
"called the office because her father had called it that---a dim hot "
"airless room with the blinds all closed and fastened for forty-three "
"summers because when she was a girl someone had believed that light and "
"moving air carried heat and that dark was always cooler, and which (as "
"the sun shone fuller and fuller on that side of the house) became "
"latticed with yellow slashes full of dust motes which Quentin thought of "
"as being flecks of the dead old dried paint itself blown inward from the "
"scaling blinds as wind might have blown them. There was a wistaria vine "
"blooming for the second time that summer on a wooden trellis before one "
"window, into which sparrows came now and then in random gusts, making a "
"dry vivid dusty sound before going away: and opposite Quentin, Miss "
"Coldfield in the eternal black which she had worn for forty-three years "
"now, whether for her sister, father, or nothusband none knew, sitting so "
"bolt upright in the straight hard chair that was so tall for her that "
"her legs hung straight and rigid as if she had iron shinbones and "
"ankles, clear of the floor with that air of impotent and static rage "
"like children's feet, and talking in that grim haggard amazed voice "
"until at last listening would renege and hearing-sense self-confound and "
"the long-dead object of her impotent yet indomitable frustration wuld "
"appear, as though by outraged recapitulation evoked, quiet inattentive "
"and harmless, out of the biding and dreamy and victorious dust.";
static char const overwrite[] =
"It was about eleven o'clock in the morning, mid October, with the sun "
"not shining and a look of hard wet rain in the clearness of the "
"foothills. I was wearing my powder-blue suit, with dark blue shirt, tie "
"and display handkerchief, black brogues, black wool socks with dark blue "
"clocks on them. I was neat, clean, shaved and sober, and I didn't care "
"who knew it. I was everything the well-dressed private detective ought "
"to be. I was calling on four million dollars.";
static char const first_seq_write[] =
"Mr and Mrs Dursley, of number four, Privet Drive, were proud to say that "
"they were perfectly normal, thank you very much. ";
static char const second_seq_write[] =
"Not for the first time, an argument had broken out over breakfast at "
"number four, Privet Drive.";
struct TestCase {
char const *test_name;
int (*test_func)(int wr_fd, int rd_fd, void *test_specifics);
int wr_fd_open_flag;
void *test_specifics;
};
static void InitializeFile(char const *filename,
struct TestCase const *test_case,
int *wr_fd,
int *rd_fd) {
FILE *iob = fopen(filename, "w");
size_t len = sizeof quote - 1;
if (NULL == iob) {
fprintf(stderr, "Could not open \"%s\" for initialization.\n", filename);
exit(1);
}
if (fwrite(quote, 1, len, iob) != len) {
fprintf(stderr, "Could not initialize contents of \"%s\".\n", filename);
exit(1);
}
if (fclose(iob)) {
fprintf(stderr, "Could not close \"%s\" post initialization.\n", filename);
exit(1);
}
*wr_fd = open(filename, test_case->wr_fd_open_flag, 0);
if (-1 == *wr_fd) {
fprintf(stderr, "Could not open \"%s\" for test write access\n", filename);
exit(1);
}
*rd_fd = open(filename, O_RDONLY, 0);
if (-1 == *rd_fd) {
fprintf(stderr, "Could not open \"%s\" for test read verfication access\n",
filename);
exit(1);
}
}
static int VerifyContents(int rd_fd, char const *expected_data, size_t nbytes,
off_t offset) {
char buffer[4096];
size_t chunk_size;
ssize_t result;
while (nbytes > 0) {
chunk_size = nbytes;
if (chunk_size > sizeof buffer) {
chunk_size = sizeof buffer;
}
result = pread(rd_fd, buffer, chunk_size, offset);
if (result != chunk_size) {
fprintf(stderr,
"VerifyContents: pread failed, got %zd, errno %d, expected %zu\n",
result, errno, chunk_size);
return 0;
}
if (0 != memcmp(expected_data, buffer, chunk_size)) {
fprintf(stderr,
"VerifyContents: unexpected data starting at offset %llu\n"
"Got:\n%.*s\nExpected:\n%.*s\n",
offset,
chunk_size, buffer, chunk_size, expected_data);
return 0;
}
expected_data += chunk_size;
nbytes -= chunk_size;
offset += chunk_size;
}
return 1;
}
static int PwriteObeysOffset(int wr_fd, int rd_fd, void *test_specifics) {
char buffer[4096];
ssize_t nbytes;
if (sizeof buffer < sizeof overwrite - 1) {
fprintf(stderr,
"PwriteObeysOffset: configuration error: buffer too small\n");
return 1;
}
if (lseek(wr_fd, 0, SEEK_END) == (off_t) -1) {
fprintf(stderr, "PwriteObeysOffset: seek to end before write failed\n");
return 1;
}
if (write(wr_fd, "Z", 1) != 1) {
fprintf(stderr, "PwriteObeysOffset: write at end failed\n");
return 1;
}
nbytes = pwrite(wr_fd, overwrite, sizeof overwrite - 1, (off_t) 0);
if (nbytes != sizeof overwrite - 1) {
fprintf(stderr,
"PwriteObeysOffset: pwrite to beginning failed,"
" expected %zu, got %zd\n",
sizeof overwrite - 1, nbytes);
return 1;
}
if (!VerifyContents(rd_fd, overwrite, sizeof overwrite - 1, 0)) {
fprintf(stderr,
"PwriteObeysOffset: expected overwritten data wrong\n");
return 1;
}
if (!VerifyContents(rd_fd, quote + sizeof overwrite - 1,
sizeof quote - sizeof overwrite,
sizeof overwrite - 1)) {
fprintf(stderr,
"PwriteObeysOffset: bytes should have been unchanged\n");
return 1;
}
if (!VerifyContents(rd_fd, "Z", 1, sizeof quote - 1)) {
fprintf(stderr,
"PwriteObeysOffset: trailing Z missing?!?\n");
}
return 0;
}
static int PwriteDoesNotAffectReadPos(int wr_fd, int rd_fd,
void *test_specifics) {
size_t const overwrite_overlap = 100;
size_t read_count;
char buffer[4096];
ssize_t nbytes;
read_count = ((sizeof overwrite - 1) - overwrite_overlap);
nbytes = read(wr_fd, buffer, read_count);
if (nbytes != read_count) {
fprintf(stderr,
"PwriteDoesNotAffectReadPos: initial read to set file pos"
" failed, got %zd, errno %d\n", nbytes, errno);
return 1;
}
if (0 != memcmp(buffer, quote, read_count)) {
fprintf(stderr,
"PwriteDoesNotAffectReadPos: initial read result unexpected, got:\n"
"%.*s\nexpected:\n%.*s\n",
read_count, buffer, read_count, quote);
return 1;
}
nbytes = pwrite(wr_fd, overwrite, sizeof overwrite - 1, 0);
if (nbytes != sizeof overwrite - 1) {
fprintf(stderr,
"PwriteDoesNotAffectReadPos: pwrite failed, expected %zu,"
" got %zd\n", sizeof overwrite - 1, nbytes);
return 1;
}
read_count = sizeof quote - 1 - read_count;
nbytes = read(wr_fd, buffer, read_count);
if (nbytes != read_count) {
fprintf(stderr,
"PwriteDoesNotAffectReadPos: read after pwrite failed, got"
" %zd, errno %d; expected %zu\n",
nbytes, errno, read_count);
return 1;
}
if (0 != memcmp(overwrite + sizeof overwrite - 1 - overwrite_overlap,
buffer, overwrite_overlap)) {
fprintf(stderr,
"PwriteDoesNotAffectReadPos: overwritten data did not show up.\n"
"Got:\n%.*s\nExpected:\n%.*s\n",
overwrite_overlap,
buffer,
overwrite_overlap,
overwrite + sizeof overwrite - 1 - overwrite_overlap);
return 1;
}
if (0 != memcmp(buffer + overwrite_overlap,
quote + sizeof overwrite - 1,
read_count - overwrite_overlap)) {
fprintf(stderr,
"PwriteDoesNotAffectReadPos: data beyond overwritten changed.\n"
"Got:\n%.*s\nExpected:\n%.*s\n",
read_count - overwrite_overlap,
buffer + overwrite_overlap,
read_count - overwrite_overlap,
quote + sizeof overwrite - 1);
return 1;
}
if (!VerifyContents(rd_fd,
overwrite, sizeof overwrite - 1,
0)) {
fprintf(stderr, "PwriteDoesNotAffectReadPos: overwritten data bad\n");
return 1;
}
if (!VerifyContents(rd_fd,
quote + sizeof overwrite - 1,
sizeof quote - sizeof overwrite,
sizeof overwrite - 1)) {
fprintf(stderr, "PwriteDoesNotAffectReadPos: non-overwritten data bad\n");
return 1;
}
return 0;
}
static int PwriteDoesNotAffectWritePos(int wr_fd, int rd_fd,
void *test_specifics) {
/*
* Write first_seq_write to file, overwriting initial contents.
* Next, pwrite to some a non-overlapping location (end of file).
* Write second_seq_write to file, expecting that it would follow
* the contents of first_seq_write. Read file to verify.
*/
ssize_t result;
size_t seq_bytes;
int expect_append = (int) test_specifics;
result = write(wr_fd, first_seq_write, sizeof first_seq_write - 1);
if (result != sizeof first_seq_write - 1) {
fprintf(stderr,
"PwriteDoesNotAffectWritePos: writing first_seq_write failed:"
" got %zd, errno %d, expected %zu\n",
result, errno, sizeof first_seq_write - 1);
return 1;
}
result = pwrite(wr_fd, overwrite, sizeof overwrite - 1, sizeof quote - 1);
if (result != sizeof overwrite - 1) {
fprintf(stderr,
"PwriteDoesNotAffectWritePos: pwriting overwrite failed:"
" got %zd, errno %d, expected %zu\n",
result, errno, sizeof overwrite - 1);
return 1;
}
result = write(wr_fd, second_seq_write, sizeof second_seq_write - 1);
if (result != sizeof second_seq_write - 1) {
fprintf(stderr,
"PwriteDoesNotAffectWritePos: writing second_seq_write failed:"
" got %zd, errno %d, expected %zu\n",
result, errno, sizeof second_seq_write - 1);
return 1;
}
if (expect_append) {
if (!VerifyContents(rd_fd, quote, sizeof quote - 1, 0)) {
fprintf(stderr,
"PwriteDoesNotAffectWritePos: original data overwritten?!?\n");
return 1;
}
if (!VerifyContents(rd_fd, overwrite,
sizeof overwrite - 1,
sizeof quote - 1)) {
fprintf(stderr,
"PwriteDoesNotAffectWritePos: pwrite content clobbered?\n");
return 1;
}
if (!VerifyContents(rd_fd, second_seq_write,
sizeof second_seq_write - 1,
sizeof quote - 1 + sizeof overwrite - 1)) {
fprintf(stderr,
"PwriteDoesNotAffectWritePos: second_seq_write content"
" clobbered?\n");
return 1;
}
} else {
if (!VerifyContents(rd_fd, first_seq_write, sizeof first_seq_write - 1,
0)) {
fprintf(stderr,
"PwriteDoesNotAffectWritePos: first_seq_write data clobbered!\n");
return 1;
}
if (!VerifyContents(rd_fd, second_seq_write, sizeof second_seq_write - 1,
sizeof first_seq_write - 1)) {
fprintf(stderr,
"PwriteDoesNotAffectWritePos: second_seq_write data"
" clobbered!\n");
return 1;
}
seq_bytes = sizeof first_seq_write - 1 + sizeof second_seq_write - 1;
if (!VerifyContents(rd_fd,
quote + seq_bytes,
sizeof quote - 1 - seq_bytes,
seq_bytes)) {
fprintf(stderr,
"PwriteDoesNotAffectWritePos: original quote data clobbered!\n");
return 1;
}
if (!VerifyContents(rd_fd, overwrite, sizeof overwrite - 1,
sizeof quote - 1)) {
fprintf(stderr,
"PwriteDoesNotAffectWritePos: pwrite data clobbered!\n");
return 1;
}
}
return 0;
}
static int PreadObeysOffsetAndDoesNotAffectReadPtr(int wr_fd, int rd_fd,
void *test_specifics) {
ssize_t io_rv;
static size_t const kFirstReadBytes = 128;
static off_t const kPreadOffset = 256;
static size_t const kPreadBytes = 128;
char buffer[4096];
assert(kFirstReadBytes <= sizeof buffer);
assert(kFirstReadBytes <= sizeof quote - 1);
io_rv = read(wr_fd, buffer, kFirstReadBytes);
if (io_rv != kFirstReadBytes) {
fprintf(stderr,
"PreadObeysOffsetAndDoesNotAffectReadPtr: first read failed\n");
return 1;
}
if (0 != memcmp(buffer, quote, kFirstReadBytes)) {
fprintf(stderr,
"PreadObeysOffsetAndDoesNotAffectReadPtr: first read contents bad\n"
"Got:\n"
"%.*s\n"
"Expected:\n"
"%.*s\n",
(int) io_rv, buffer,
(int) kFirstReadBytes, quote);
return 1;
}
assert(kPreadBytes <= sizeof buffer);
io_rv = pread(wr_fd, buffer, kPreadBytes, kPreadOffset);
if (io_rv != kPreadBytes) {
fprintf(stderr,
"PreadObeysOffsetAndDoesNotAffectReadPtr:"
" pread returned %d, errno %d, expected %d\n",
(int) io_rv, errno, (int) kPreadBytes);
return 1;
}
if (0 != memcmp(buffer, quote + kPreadOffset, kPreadBytes)) {
fprintf(stderr,
"PreadObeysOffsetAndDoesNotAffectReadPtr: pread content bad\n"
"Got:\n"
"%.*s\n"
"Expected:\n"
"%.*s\n",
(int) io_rv, buffer,
(int) kPreadBytes, quote + kPreadOffset);
return 1;
}
assert(sizeof quote - 1 - kFirstReadBytes <= sizeof buffer);
io_rv = read(wr_fd, buffer, sizeof buffer);
if (io_rv != sizeof quote - 1 - kFirstReadBytes) {
fprintf(stderr,
"PreadObeysOffsetAndDoesNotAffectReadPtr:"
" bad byte count on 2nd read\n");
return 1;
}
if (0 != memcmp(buffer, quote + kFirstReadBytes,
sizeof quote - 1 - kFirstReadBytes)) {
fprintf(stderr,
"PreadObeysOffsetAndDoesNotAffectReadPtr: 2nd read contents bad\n"
"Got:\n"
"%.*s\n"
"Expected:\n"
"%.*s\n",
(int) io_rv, buffer,
(int) sizeof quote - 1 - kFirstReadBytes, quote + kFirstReadBytes);
return 1;
}
return 0;
}
static struct TestCase const g_test_cases[] = {
{
"Pwrite obeys offset, O_RDWR",
PwriteObeysOffset,
O_RDWR,
(void *) NULL
}, {
"Pwrite obeys offset, O_WRONLY",
PwriteObeysOffset,
O_WRONLY,
(void *) NULL
}, {
"Pwrite obeys offset, O_RDWR | O_APPEND",
PwriteObeysOffset,
O_RDWR | O_APPEND,
(void *) NULL
}, {
"Pwrite obeys offset, O_WRONLY | O_APPEND",
PwriteObeysOffset,
O_WRONLY | O_APPEND,
(void *) NULL
}, {
"Pwrite does not affect shared file pos (read), O_RDWR",
PwriteDoesNotAffectReadPos,
O_RDWR,
(void *) NULL
}, {
"Pwrite does not affect shared file pos (read), O_RDWR | O_APPEND",
PwriteDoesNotAffectReadPos,
O_RDWR | O_APPEND,
(void *) NULL
}, {
"Pwrite does not affect shared file pos (write), O_RDWR",
PwriteDoesNotAffectWritePos,
O_RDWR,
(void *) NULL
}, {
"Pwrite does not affect shared file pos (write), O_WRONLY",
PwriteDoesNotAffectWritePos,
O_WRONLY,
(void *) NULL
}, {
"Pwrite does not affect shared file pos (write), O_RDWR | O_APPEND",
PwriteDoesNotAffectWritePos,
O_RDWR | O_APPEND,
(void *) 1
}, {
"Pwrite does not affect shared file pos (write), O_WRONLY | O_APPEND",
PwriteDoesNotAffectWritePos,
O_WRONLY | O_APPEND,
(void *) 1
}, {
"PreadObeysOffsetAndDoesNotAffectReadPtr, O_RDONLY",
PreadObeysOffsetAndDoesNotAffectReadPtr,
O_RDONLY,
(void *) NULL
}, {
"PreadObeysOffsetAndDoesNotAffectReadPtr, O_RDWR",
PreadObeysOffsetAndDoesNotAffectReadPtr,
O_RDWR,
(void *) NULL
}, {
"PreadObeysOffsetAndDoesNotAffectReadPtr, O_RDWR | O_APPEND",
PreadObeysOffsetAndDoesNotAffectReadPtr,
O_RDWR | O_APPEND,
(void *) NULL
},
};
int main(int ac, char **av) {
int opt;
char test_file_name[PATH_MAX];
size_t ix;
int error_occurred = 0;
int wr_fd;
int rd_fd;
while (-1 != (opt = getopt(ac, av, "t:"))) {
switch (opt) {
case 't':
g_dirname = optarg;
break;
default:
fprintf(stderr, "Usage: pwrite_test [-t temp_dir]\n");
return 1;
}
}
for (ix = 0; ix < sizeof g_test_cases / sizeof g_test_cases[0]; ++ix) {
printf("%s\n", g_test_cases[ix].test_name);
snprintf(test_file_name, sizeof test_file_name,
"%s/absalom%zu.txt", g_dirname, ix);
/*
* For the case of pwrite() with O_APPEND, SFI NaCl tries to match
* the POSIX/Mac behaviour, which differs from Linux's behaviour.
* SFI NaCl contains a workaround to implement the POSIX/Mac
* behaviour on Linux. Non-SFI NaCl currently does not implement
* this workaround, so the tests for POSIX/Mac behaviour don't
* pass.
*/
if (NONSFI_MODE && (g_test_cases[ix].wr_fd_open_flag & O_APPEND)) {
printf("Skipped\n");
continue;
}
InitializeFile(test_file_name, &g_test_cases[ix], &wr_fd, &rd_fd);
if ((*g_test_cases[ix].test_func)(wr_fd, rd_fd,
g_test_cases[ix].test_specifics)) {
printf("Failed test %zu: %s\n", ix, g_test_cases[ix].test_name);
error_occurred = 1;
} else {
printf("OK\n");
}
(void) close(wr_fd);
(void) close(rd_fd);
}
printf("%s\n", error_occurred ? "FAILED" : "PASSED");
return error_occurred;
}