#include <assert.h>
#include <err.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#define BUFSIZE (16 * 1024)
#define HOLESIZE (16 * BUFSIZE)
static int debug = 0;
static int fd = -1;
static off_t curpos = 0;
static char *fname;
static char *gbuf;
static char *mbuf;
void
gen_data(void *buf, size_t size, uint32_t seed)
{
assert(size % 4 == 0);
if (debug)
printf("%s: size %zd seed %#08x\n", __func__, size, seed);
uint32_t *ibuf = buf;
for (size_t i = 0; i < size / 4; i++)
ibuf[i] = seed + i;
}
void
check_data(const void *buf, size_t size, uint32_t seed)
{
assert(size % 4 == 0);
const uint32_t *ibuf = buf;
for (size_t i = 0; i < size / 4; i++) {
if (ibuf[i] != seed + i) {
errx(3, "%s: pos %zd/%zd: expected %#08zx got %#08x",
__func__, 4 * i, size, seed + i, ibuf[i]);
}
}
}
void
check_zero(const void *buf, size_t size)
{
assert(size % 4 == 0);
const uint32_t *ibuf = buf;
for (size_t i = 0; i < size / 4; i++) {
if (ibuf[i] != 0) {
errx(3, "%s: pos %zd/%zd: expected 0 got %#08x",
__func__, 4 * i, size, ibuf[i]);
}
}
}
void
check(const char *what, int64_t have, int64_t want)
{
if (have != want) {
if (have == -1)
err(2, "%s returned %lld, expected %lld",
what, have, want);
else
errx(2, "%s returned %lld, expected %lld",
what, have, want);
}
if (debug)
printf("%s returned %lld\n", what, have);
}
void
c_write(void *buf, size_t size)
{
ssize_t ret = write(fd, buf, size);
check("write", ret, size);
curpos += ret;
}
void
c_read(void *buf, size_t size)
{
ssize_t ret = read(fd, buf, size);
check("read", ret, size);
curpos += ret;
}
void
c_fsync(void)
{
int ret = fsync(fd);
check("fsync", ret, 0);
}
void
c_lseek(off_t offset, int whence)
{
off_t ret = lseek(fd, offset, whence);
switch (whence) {
case SEEK_SET:
curpos = offset;
break;
case SEEK_CUR:
curpos += offset;
break;
default:
errx(1, "c_lseek not supported");
}
check("lseek", ret, curpos);
if (debug)
printf("curpos: %lld\n", (long long int)curpos);
}
void
c_mmap(size_t size)
{
mbuf = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, curpos);
if (mbuf == MAP_FAILED)
err(2, "mmap %zd pos %lld failed", size, (long long)curpos);
curpos += size;
if (debug)
printf("mmap: %p\n", mbuf);
}
void
c_munmap(size_t size)
{
int ret = munmap(mbuf, size);
if (ret != 0)
err(2, "munmap");
}
void
c_open(int flags)
{
fd = open(fname, flags, S_IRUSR|S_IWUSR);
if (fd == -1)
err(1, "open");
}
void
check_read(size_t size, int hole)
{
size_t pos = 0;
while (pos < size) {
size_t to_read = size - pos;
uint32_t seed = curpos;
if (to_read > BUFSIZE)
to_read = BUFSIZE;
c_read(gbuf, to_read);
if (hole)
check_zero(gbuf, to_read);
else
check_data(gbuf, to_read, seed);
pos += to_read;
}
}
void
check_mmap(size_t size, int hole)
{
size_t pos = 0;
while (pos < size) {
size_t to_read = size - pos;
uint32_t seed = curpos;
if (to_read > BUFSIZE)
to_read = BUFSIZE;
c_mmap(to_read);
if (hole)
check_zero(mbuf, to_read);
else
check_data(mbuf, to_read, seed);
c_munmap(to_read);
pos += to_read;
}
}
void
do_create(void)
{
unlink(fname);
c_open(O_EXCL|O_CREAT|O_RDWR);
gen_data(gbuf, BUFSIZE, curpos);
c_write(gbuf, BUFSIZE);
c_lseek(HOLESIZE, SEEK_CUR);
gen_data(gbuf, BUFSIZE, curpos);
c_write(gbuf, BUFSIZE);
c_fsync();
}
void
do_read(void)
{
c_open(O_RDWR);
check_read(BUFSIZE, 0);
check_read(HOLESIZE, 1);
check_read(BUFSIZE, 0);
}
void
do_mmap(void)
{
c_open(O_RDWR);
check_mmap(BUFSIZE, 0);
check_mmap(HOLESIZE, 1);
check_mmap(BUFSIZE, 0);
}
void
usage(void)
{
errx(1, "usage: fileops (create|read|mmap) filename");
}
int main(int argc, char **argv)
{
if (argc != 3)
usage();
fname = argv[2];
gbuf = malloc(BUFSIZE);
if (gbuf == NULL)
err(1, "malloc");
if (strcmp(argv[1], "create") == 0) {
do_create();
} else if (strcmp(argv[1], "read") == 0) {
do_read();
} else if (strcmp(argv[1], "mmap") == 0) {
do_mmap();
} else {
usage();
}
printf("pass\n");
return 0;
}