xref: /freebsd/sys/contrib/openzfs/tests/zfs-tests/cmd/mmap_seek.c (revision 6e6cde8f2bdb235b741061e3c6ee664752f25c18)
161145dc2SMartin Matuska // SPDX-License-Identifier: CDDL-1.0
2716fd348SMartin Matuska /*
3716fd348SMartin Matuska  * CDDL HEADER START
4716fd348SMartin Matuska  *
5716fd348SMartin Matuska  * The contents of this file are subject to the terms of the
6716fd348SMartin Matuska  * Common Development and Distribution License (the "License").
7716fd348SMartin Matuska  * You may not use this file except in compliance with the License.
8716fd348SMartin Matuska  *
9716fd348SMartin Matuska  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10271171e0SMartin Matuska  * or https://opensource.org/licenses/CDDL-1.0.
11716fd348SMartin Matuska  * See the License for the specific language governing permissions
12716fd348SMartin Matuska  * and limitations under the License.
13716fd348SMartin Matuska  *
14716fd348SMartin Matuska  * When distributing Covered Code, include this CDDL HEADER in each
15716fd348SMartin Matuska  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16716fd348SMartin Matuska  * If applicable, add the following below this CDDL HEADER, with the
17716fd348SMartin Matuska  * fields enclosed by brackets "[]" replaced with your own identifying
18716fd348SMartin Matuska  * information: Portions Copyright [yyyy] [name of copyright owner]
19716fd348SMartin Matuska  *
20716fd348SMartin Matuska  * CDDL HEADER END
21716fd348SMartin Matuska  */
22716fd348SMartin Matuska 
23716fd348SMartin Matuska /*
24716fd348SMartin Matuska  * Copyright (c) 2021 by Lawrence Livermore National Security, LLC.
25716fd348SMartin Matuska  */
26716fd348SMartin Matuska 
27716fd348SMartin Matuska #include <unistd.h>
28716fd348SMartin Matuska #include <fcntl.h>
29716fd348SMartin Matuska #include <stdio.h>
30716fd348SMartin Matuska #include <stdlib.h>
31716fd348SMartin Matuska #include <string.h>
32716fd348SMartin Matuska #include <sys/mman.h>
33716fd348SMartin Matuska #include <sys/sysmacros.h>
34716fd348SMartin Matuska #include <errno.h>
35716fd348SMartin Matuska #ifdef __linux__
36716fd348SMartin Matuska #include <linux/fs.h>
37716fd348SMartin Matuska #endif
38716fd348SMartin Matuska 
39ce4dcb97SMartin Matuska /* some older uClibc's lack the defines, so we'll manually define them */
40ce4dcb97SMartin Matuska #ifdef	__UCLIBC__
41ce4dcb97SMartin Matuska #ifndef	SEEK_DATA
42ce4dcb97SMartin Matuska #define	SEEK_DATA 3
43ce4dcb97SMartin Matuska #endif
44ce4dcb97SMartin Matuska #ifndef	SEEK_HOLE
45ce4dcb97SMartin Matuska #define	SEEK_HOLE 4
46ce4dcb97SMartin Matuska #endif
47ce4dcb97SMartin Matuska #endif
48ce4dcb97SMartin Matuska 
49716fd348SMartin Matuska static void
seek_expect(int fd,off_t offset,int whence,off_t expect_offset)50*6e6cde8fSMartin Matuska seek_expect(int fd, off_t offset, int whence, off_t expect_offset)
51716fd348SMartin Matuska {
52*6e6cde8fSMartin Matuska 	errno = 0;
53*6e6cde8fSMartin Matuska 	off_t seek_offset = lseek(fd, offset, whence);
54*6e6cde8fSMartin Matuska 	if (seek_offset == expect_offset)
55*6e6cde8fSMartin Matuska 		return;
56*6e6cde8fSMartin Matuska 
57*6e6cde8fSMartin Matuska 	int err = errno;
58*6e6cde8fSMartin Matuska 	fprintf(stderr, "lseek(fd, %ld, SEEK_%s) = %ld (expected %ld)",
59*6e6cde8fSMartin Matuska 	    offset, (whence == SEEK_DATA ? "DATA" : "HOLE"),
60*6e6cde8fSMartin Matuska 	    seek_offset, expect_offset);
61*6e6cde8fSMartin Matuska 	if (err != 0)
62*6e6cde8fSMartin Matuska 		fprintf(stderr, " (errno %d [%s])\n", err, strerror(err));
63*6e6cde8fSMartin Matuska 	else
64*6e6cde8fSMartin Matuska 		fputc('\n', stderr);
65716fd348SMartin Matuska 	exit(2);
66716fd348SMartin Matuska }
67716fd348SMartin Matuska 
68*6e6cde8fSMartin Matuska static inline void
seek_data(int fd,off_t offset,off_t expected)69*6e6cde8fSMartin Matuska seek_data(int fd, off_t offset, off_t expected)
70*6e6cde8fSMartin Matuska {
71*6e6cde8fSMartin Matuska 	seek_expect(fd, offset, SEEK_DATA, expected);
72*6e6cde8fSMartin Matuska }
73*6e6cde8fSMartin Matuska 
74*6e6cde8fSMartin Matuska static inline void
seek_hole(int fd,off_t offset,off_t expected)75716fd348SMartin Matuska seek_hole(int fd, off_t offset, off_t expected)
76716fd348SMartin Matuska {
77*6e6cde8fSMartin Matuska 	seek_expect(fd, offset, SEEK_HOLE, expected);
78716fd348SMartin Matuska }
79716fd348SMartin Matuska 
80716fd348SMartin Matuska int
main(int argc,char ** argv)81716fd348SMartin Matuska main(int argc, char **argv)
82716fd348SMartin Matuska {
83716fd348SMartin Matuska 	char *execname = argv[0];
84716fd348SMartin Matuska 	char *file_path = argv[1];
85716fd348SMartin Matuska 	char *buf = NULL;
86716fd348SMartin Matuska 	int err;
87716fd348SMartin Matuska 
88716fd348SMartin Matuska 	if (argc != 4) {
89716fd348SMartin Matuska 		(void) printf("usage: %s <file name> <file size> "
90716fd348SMartin Matuska 		    "<block size>\n", argv[0]);
91716fd348SMartin Matuska 		exit(1);
92716fd348SMartin Matuska 	}
93716fd348SMartin Matuska 
94716fd348SMartin Matuska 	int fd = open(file_path, O_RDWR | O_CREAT, 0666);
95716fd348SMartin Matuska 	if (fd == -1) {
96716fd348SMartin Matuska 		(void) fprintf(stderr, "%s: %s: ", execname, file_path);
97716fd348SMartin Matuska 		perror("open");
98716fd348SMartin Matuska 		exit(2);
99716fd348SMartin Matuska 	}
100716fd348SMartin Matuska 
101716fd348SMartin Matuska 	off_t file_size = atoi(argv[2]);
102716fd348SMartin Matuska 	off_t block_size = atoi(argv[3]);
103716fd348SMartin Matuska 
104716fd348SMartin Matuska 	if (block_size * 2 > file_size) {
105716fd348SMartin Matuska 		(void) fprintf(stderr, "file size must be at least "
106716fd348SMartin Matuska 		    "double the block size\n");
107716fd348SMartin Matuska 		exit(2);
108716fd348SMartin Matuska 	}
109716fd348SMartin Matuska 
110716fd348SMartin Matuska 	err = ftruncate(fd, file_size);
111716fd348SMartin Matuska 	if (err == -1) {
112716fd348SMartin Matuska 		perror("ftruncate");
113716fd348SMartin Matuska 		exit(2);
114716fd348SMartin Matuska 	}
115716fd348SMartin Matuska 
116716fd348SMartin Matuska 	if ((buf = mmap(NULL, file_size, PROT_READ | PROT_WRITE,
117716fd348SMartin Matuska 	    MAP_SHARED, fd, 0)) == MAP_FAILED) {
118716fd348SMartin Matuska 		perror("mmap");
119716fd348SMartin Matuska 		exit(2);
120716fd348SMartin Matuska 	}
121716fd348SMartin Matuska 
122716fd348SMartin Matuska 	/* Verify the file is sparse and reports no data. */
123716fd348SMartin Matuska 	seek_data(fd, 0, -1);
124716fd348SMartin Matuska 
125716fd348SMartin Matuska 	/* Verify the file is reported as a hole. */
126716fd348SMartin Matuska 	seek_hole(fd, 0, 0);
127716fd348SMartin Matuska 
128716fd348SMartin Matuska 	/* Verify search beyond end of file is an error. */
129716fd348SMartin Matuska 	seek_data(fd, 2 * file_size, -1);
130716fd348SMartin Matuska 	seek_hole(fd, 2 * file_size, -1);
131716fd348SMartin Matuska 
132716fd348SMartin Matuska 	/* Dirty the first byte. */
133716fd348SMartin Matuska 	memset(buf, 'a', 1);
134716fd348SMartin Matuska 	seek_data(fd, 0, 0);
135716fd348SMartin Matuska 	seek_data(fd, block_size, -1);
136716fd348SMartin Matuska 	seek_hole(fd, 0, block_size);
137716fd348SMartin Matuska 	seek_hole(fd, block_size, block_size);
138716fd348SMartin Matuska 
139716fd348SMartin Matuska 	/* Dirty the first half of the file. */
140716fd348SMartin Matuska 	memset(buf, 'b', file_size / 2);
141716fd348SMartin Matuska 	seek_data(fd, 0, 0);
142716fd348SMartin Matuska 	seek_data(fd, block_size, block_size);
143716fd348SMartin Matuska 	seek_hole(fd, 0, P2ROUNDUP(file_size / 2, block_size));
144716fd348SMartin Matuska 	seek_hole(fd, block_size, P2ROUNDUP(file_size / 2, block_size));
145716fd348SMartin Matuska 
146716fd348SMartin Matuska 	/* Dirty the whole file. */
147716fd348SMartin Matuska 	memset(buf, 'c', file_size);
148716fd348SMartin Matuska 	seek_data(fd, 0, 0);
149716fd348SMartin Matuska 	seek_data(fd, file_size * 3 / 4,
150716fd348SMartin Matuska 	    P2ROUNDUP(file_size * 3 / 4, block_size));
151716fd348SMartin Matuska 	seek_hole(fd, 0, file_size);
152716fd348SMartin Matuska 	seek_hole(fd, file_size / 2, file_size);
153716fd348SMartin Matuska 
154716fd348SMartin Matuska 	/* Punch a hole (required compression be enabled). */
155716fd348SMartin Matuska 	memset(buf + block_size, 0, block_size);
156716fd348SMartin Matuska 	seek_data(fd, 0, 0);
157716fd348SMartin Matuska 	seek_data(fd, block_size, 2 * block_size);
158716fd348SMartin Matuska 	seek_hole(fd, 0, block_size);
159716fd348SMartin Matuska 	seek_hole(fd, block_size, block_size);
160716fd348SMartin Matuska 	seek_hole(fd, 2 * block_size, file_size);
161716fd348SMartin Matuska 
162716fd348SMartin Matuska 	err = munmap(buf, file_size);
163716fd348SMartin Matuska 	if (err == -1) {
164716fd348SMartin Matuska 		perror("munmap");
165716fd348SMartin Matuska 		exit(2);
166716fd348SMartin Matuska 	}
167716fd348SMartin Matuska 
168716fd348SMartin Matuska 	close(fd);
169716fd348SMartin Matuska 
170716fd348SMartin Matuska 	return (0);
171716fd348SMartin Matuska }
172