blob: 72c23265022cb2ea64b9b0115de6dada7ab1520a [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2024 SUSE Linux Products GmbH. All Rights Reserved.
*/
/*
* Test a direct IO write in append mode with a buffer that was not faulted in
* (or just partially) before the write.
*/
/* Get the O_DIRECT definition. */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
static ssize_t do_write(int fd, const void *buf, size_t count)
{
while (count > 0) {
ssize_t ret;
ret = write(fd, buf, count);
if (ret < 0) {
if (errno == EINTR)
continue;
return ret;
}
count -= ret;
buf += ret;
}
return 0;
}
int main(int argc, char *argv[])
{
struct stat stbuf;
int fd;
long pagesize;
void *buf;
ssize_t ret;
if (argc != 2) {
fprintf(stderr, "Use: %s <file path>\n", argv[0]);
return 1;
}
/*
* First try an append write against an empty file of a buffer with a
* size matching the page size. The buffer is not faulted in before
* attempting the write.
*/
fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC | O_DIRECT | O_APPEND, 0666);
if (fd == -1) {
perror("Failed to open/create file");
return 2;
}
pagesize = sysconf(_SC_PAGE_SIZE);
if (pagesize == -1) {
perror("Failed to get page size");
return 3;
}
buf = mmap(NULL, pagesize, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (buf == MAP_FAILED) {
perror("Failed to allocate first buffer");
return 4;
}
ret = do_write(fd, buf, pagesize);
if (ret < 0) {
perror("First write failed");
return 5;
}
ret = fstat(fd, &stbuf);
if (ret < 0) {
perror("First stat failed");
return 6;
}
/* Don't exit on failure so that we run the second test below too. */
if (stbuf.st_size != pagesize)
fprintf(stderr,
"Wrong file size after first write, got %jd expected %ld\n",
(intmax_t)stbuf.st_size, pagesize);
munmap(buf, pagesize);
close(fd);
/*
* Now try an append write against an empty file of a buffer with a
* size matching twice the page size. Only the first page of the buffer
* is faulted in before attempting the write, so that the second page
* should be faulted in during the write.
*/
fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC | O_DIRECT | O_APPEND, 0666);
if (fd == -1) {
perror("Failed to open/create file");
return 7;
}
buf = mmap(NULL, pagesize * 2, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (buf == MAP_FAILED) {
perror("Failed to allocate second buffer");
return 8;
}
/* Fault in first page of the buffer before the write. */
memset(buf, 0, 1);
ret = do_write(fd, buf, pagesize * 2);
if (ret < 0) {
perror("Second write failed");
return 9;
}
ret = fstat(fd, &stbuf);
if (ret < 0) {
perror("Second stat failed");
return 10;
}
if (stbuf.st_size != pagesize * 2)
fprintf(stderr,
"Wrong file size after second write, got %jd expected %ld\n",
(intmax_t)stbuf.st_size, pagesize * 2);
munmap(buf, pagesize * 2);
close(fd);
return 0;
}