xref: /freebsd/sys/contrib/openzfs/tests/zfs-tests/cmd/clone_mmap_write.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or https://opensource.org/licenses/CDDL-1.0.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * This program clones the file, mmap it, and writes from the map into
24  * file. This scenario triggers a panic on Linux in dbuf_redirty(),
25  * which is fixed under PR#15656. On FreeBSD, the same test causes data
26  * corruption, which is fixed by PR#15665.
27  *
28  * It would be good to test for this scenario in ZTS. This program and
29  * issue was initially produced by @robn.
30  */
31 #ifndef _GNU_SOURCE
32 #define	_GNU_SOURCE
33 #endif
34 
35 #include <fcntl.h>
36 #include <string.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <errno.h>
41 #include <sys/stat.h>
42 #include <sys/mman.h>
43 
44 #ifdef __FreeBSD__
45 #define	loff_t	off_t
46 #endif
47 
48 ssize_t
49 copy_file_range(int, loff_t *, int, loff_t *, size_t, unsigned int)
50     __attribute__((weak));
51 
52 static int
53 open_file(const char *source)
54 {
55 	int fd;
56 	if ((fd = open(source, O_RDWR | O_APPEND)) < 0) {
57 		(void) fprintf(stderr, "Error opening %s\n", source);
58 		exit(1);
59 	}
60 	sync();
61 	return (fd);
62 }
63 
64 static int
65 clone_file(int sfd, long long size, const char *dest)
66 {
67 	int dfd;
68 
69 	if ((dfd = open(dest, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
70 		(void) fprintf(stderr, "Error opening %s\n", dest);
71 		exit(1);
72 	}
73 
74 	if (copy_file_range(sfd, 0, dfd, 0, size, 0) < 0) {
75 		(void) fprintf(stderr, "copy_file_range failed\n");
76 		exit(1);
77 	}
78 
79 	return (dfd);
80 }
81 
82 static void *
83 map_file(int fd, long long size)
84 {
85 	void *p = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
86 	if (p == MAP_FAILED) {
87 		(void) fprintf(stderr, "mmap failed\n");
88 		exit(1);
89 	}
90 
91 	return (p);
92 }
93 
94 static void
95 map_write(void *p, int fd)
96 {
97 	if (pwrite(fd, p, 1024*128, 0) < 0) {
98 		(void) fprintf(stderr, "write failed\n");
99 		exit(1);
100 	}
101 }
102 
103 int
104 main(int argc, char **argv)
105 {
106 	int sfd, dfd;
107 	void *p;
108 	struct stat sb;
109 	if (argc != 3) {
110 		(void) printf("usage: %s <input source file> "
111 		    "<clone destination file>\n", argv[0]);
112 		exit(1);
113 	}
114 	sfd = open_file(argv[1]);
115 	if (fstat(sfd, &sb) == -1) {
116 		(void) fprintf(stderr, "fstat failed\n");
117 		exit(1);
118 	}
119 	dfd = clone_file(sfd, sb.st_size, argv[2]);
120 	p = map_file(dfd, sb.st_size);
121 	map_write(p, dfd);
122 	return (0);
123 }
124