1716fd348SMartin Matuska /* 2716fd348SMartin Matuska * CDDL HEADER START 3716fd348SMartin Matuska * 4716fd348SMartin Matuska * The contents of this file are subject to the terms of the 5716fd348SMartin Matuska * Common Development and Distribution License (the "License"). 6716fd348SMartin Matuska * You may not use this file except in compliance with the License. 7716fd348SMartin Matuska * 8716fd348SMartin Matuska * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9271171e0SMartin Matuska * or https://opensource.org/licenses/CDDL-1.0. 10716fd348SMartin Matuska * See the License for the specific language governing permissions 11716fd348SMartin Matuska * and limitations under the License. 12716fd348SMartin Matuska * 13716fd348SMartin Matuska * When distributing Covered Code, include this CDDL HEADER in each 14716fd348SMartin Matuska * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15716fd348SMartin Matuska * If applicable, add the following below this CDDL HEADER, with the 16716fd348SMartin Matuska * fields enclosed by brackets "[]" replaced with your own identifying 17716fd348SMartin Matuska * information: Portions Copyright [yyyy] [name of copyright owner] 18716fd348SMartin Matuska * 19716fd348SMartin Matuska * CDDL HEADER END 20716fd348SMartin Matuska */ 21716fd348SMartin Matuska 22716fd348SMartin Matuska /* 23716fd348SMartin Matuska * Copyright (c) 2021 by Lawrence Livermore National Security, LLC. 24716fd348SMartin Matuska */ 25716fd348SMartin Matuska 26716fd348SMartin Matuska #include <unistd.h> 27716fd348SMartin Matuska #include <fcntl.h> 28716fd348SMartin Matuska #include <stdio.h> 29716fd348SMartin Matuska #include <stdlib.h> 30716fd348SMartin Matuska #include <string.h> 31716fd348SMartin Matuska #include <sys/mman.h> 32716fd348SMartin Matuska #include <sys/sysmacros.h> 33716fd348SMartin Matuska #include <errno.h> 34716fd348SMartin Matuska #ifdef __linux__ 35716fd348SMartin Matuska #include <linux/fs.h> 36716fd348SMartin Matuska #endif 37716fd348SMartin Matuska 38*ce4dcb97SMartin Matuska /* some older uClibc's lack the defines, so we'll manually define them */ 39*ce4dcb97SMartin Matuska #ifdef __UCLIBC__ 40*ce4dcb97SMartin Matuska #ifndef SEEK_DATA 41*ce4dcb97SMartin Matuska #define SEEK_DATA 3 42*ce4dcb97SMartin Matuska #endif 43*ce4dcb97SMartin Matuska #ifndef SEEK_HOLE 44*ce4dcb97SMartin Matuska #define SEEK_HOLE 4 45*ce4dcb97SMartin Matuska #endif 46*ce4dcb97SMartin Matuska #endif 47*ce4dcb97SMartin Matuska 48716fd348SMartin Matuska static void 49716fd348SMartin Matuska seek_data(int fd, off_t offset, off_t expected) 50716fd348SMartin Matuska { 51716fd348SMartin Matuska off_t data_offset = lseek(fd, offset, SEEK_DATA); 52716fd348SMartin Matuska if (data_offset != expected) { 53716fd348SMartin Matuska fprintf(stderr, "lseek(fd, %d, SEEK_DATA) = %d (expected %d)\n", 54716fd348SMartin Matuska (int)offset, (int)data_offset, (int)expected); 55716fd348SMartin Matuska exit(2); 56716fd348SMartin Matuska } 57716fd348SMartin Matuska } 58716fd348SMartin Matuska 59716fd348SMartin Matuska static void 60716fd348SMartin Matuska seek_hole(int fd, off_t offset, off_t expected) 61716fd348SMartin Matuska { 62716fd348SMartin Matuska off_t hole_offset = lseek(fd, offset, SEEK_HOLE); 63716fd348SMartin Matuska if (hole_offset != expected) { 64716fd348SMartin Matuska fprintf(stderr, "lseek(fd, %d, SEEK_HOLE) = %d (expected %d)\n", 65716fd348SMartin Matuska (int)offset, (int)hole_offset, (int)expected); 66716fd348SMartin Matuska exit(2); 67716fd348SMartin Matuska } 68716fd348SMartin Matuska } 69716fd348SMartin Matuska 70716fd348SMartin Matuska int 71716fd348SMartin Matuska main(int argc, char **argv) 72716fd348SMartin Matuska { 73716fd348SMartin Matuska char *execname = argv[0]; 74716fd348SMartin Matuska char *file_path = argv[1]; 75716fd348SMartin Matuska char *buf = NULL; 76716fd348SMartin Matuska int err; 77716fd348SMartin Matuska 78716fd348SMartin Matuska if (argc != 4) { 79716fd348SMartin Matuska (void) printf("usage: %s <file name> <file size> " 80716fd348SMartin Matuska "<block size>\n", argv[0]); 81716fd348SMartin Matuska exit(1); 82716fd348SMartin Matuska } 83716fd348SMartin Matuska 84716fd348SMartin Matuska int fd = open(file_path, O_RDWR | O_CREAT, 0666); 85716fd348SMartin Matuska if (fd == -1) { 86716fd348SMartin Matuska (void) fprintf(stderr, "%s: %s: ", execname, file_path); 87716fd348SMartin Matuska perror("open"); 88716fd348SMartin Matuska exit(2); 89716fd348SMartin Matuska } 90716fd348SMartin Matuska 91716fd348SMartin Matuska off_t file_size = atoi(argv[2]); 92716fd348SMartin Matuska off_t block_size = atoi(argv[3]); 93716fd348SMartin Matuska 94716fd348SMartin Matuska if (block_size * 2 > file_size) { 95716fd348SMartin Matuska (void) fprintf(stderr, "file size must be at least " 96716fd348SMartin Matuska "double the block size\n"); 97716fd348SMartin Matuska exit(2); 98716fd348SMartin Matuska } 99716fd348SMartin Matuska 100716fd348SMartin Matuska err = ftruncate(fd, file_size); 101716fd348SMartin Matuska if (err == -1) { 102716fd348SMartin Matuska perror("ftruncate"); 103716fd348SMartin Matuska exit(2); 104716fd348SMartin Matuska } 105716fd348SMartin Matuska 106716fd348SMartin Matuska if ((buf = mmap(NULL, file_size, PROT_READ | PROT_WRITE, 107716fd348SMartin Matuska MAP_SHARED, fd, 0)) == MAP_FAILED) { 108716fd348SMartin Matuska perror("mmap"); 109716fd348SMartin Matuska exit(2); 110716fd348SMartin Matuska } 111716fd348SMartin Matuska 112716fd348SMartin Matuska /* Verify the file is sparse and reports no data. */ 113716fd348SMartin Matuska seek_data(fd, 0, -1); 114716fd348SMartin Matuska 115716fd348SMartin Matuska /* Verify the file is reported as a hole. */ 116716fd348SMartin Matuska seek_hole(fd, 0, 0); 117716fd348SMartin Matuska 118716fd348SMartin Matuska /* Verify search beyond end of file is an error. */ 119716fd348SMartin Matuska seek_data(fd, 2 * file_size, -1); 120716fd348SMartin Matuska seek_hole(fd, 2 * file_size, -1); 121716fd348SMartin Matuska 122716fd348SMartin Matuska /* Dirty the first byte. */ 123716fd348SMartin Matuska memset(buf, 'a', 1); 124716fd348SMartin Matuska seek_data(fd, 0, 0); 125716fd348SMartin Matuska seek_data(fd, block_size, -1); 126716fd348SMartin Matuska seek_hole(fd, 0, block_size); 127716fd348SMartin Matuska seek_hole(fd, block_size, block_size); 128716fd348SMartin Matuska 129716fd348SMartin Matuska /* Dirty the first half of the file. */ 130716fd348SMartin Matuska memset(buf, 'b', file_size / 2); 131716fd348SMartin Matuska seek_data(fd, 0, 0); 132716fd348SMartin Matuska seek_data(fd, block_size, block_size); 133716fd348SMartin Matuska seek_hole(fd, 0, P2ROUNDUP(file_size / 2, block_size)); 134716fd348SMartin Matuska seek_hole(fd, block_size, P2ROUNDUP(file_size / 2, block_size)); 135716fd348SMartin Matuska 136716fd348SMartin Matuska /* Dirty the whole file. */ 137716fd348SMartin Matuska memset(buf, 'c', file_size); 138716fd348SMartin Matuska seek_data(fd, 0, 0); 139716fd348SMartin Matuska seek_data(fd, file_size * 3 / 4, 140716fd348SMartin Matuska P2ROUNDUP(file_size * 3 / 4, block_size)); 141716fd348SMartin Matuska seek_hole(fd, 0, file_size); 142716fd348SMartin Matuska seek_hole(fd, file_size / 2, file_size); 143716fd348SMartin Matuska 144716fd348SMartin Matuska /* Punch a hole (required compression be enabled). */ 145716fd348SMartin Matuska memset(buf + block_size, 0, block_size); 146716fd348SMartin Matuska seek_data(fd, 0, 0); 147716fd348SMartin Matuska seek_data(fd, block_size, 2 * block_size); 148716fd348SMartin Matuska seek_hole(fd, 0, block_size); 149716fd348SMartin Matuska seek_hole(fd, block_size, block_size); 150716fd348SMartin Matuska seek_hole(fd, 2 * block_size, file_size); 151716fd348SMartin Matuska 152716fd348SMartin Matuska err = munmap(buf, file_size); 153716fd348SMartin Matuska if (err == -1) { 154716fd348SMartin Matuska perror("munmap"); 155716fd348SMartin Matuska exit(2); 156716fd348SMartin Matuska } 157716fd348SMartin Matuska 158716fd348SMartin Matuska close(fd); 159716fd348SMartin Matuska 160716fd348SMartin Matuska return (0); 161716fd348SMartin Matuska } 162