|  | // 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; | 
|  | } |