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