1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2012 Jilin Xpd <jilinxpd@gmail.com>
14  * Copyright 2018 Nexenta Systems, Inc.
15  */
16 
17 /*
18  * Test if file read/write is coherent with mmap, perform 2 tests:
19  * modify file through mmap, and check the result through file read.
20  * modify file through file write, and check the result through mmap.
21  */
22 
23 #include <sys/mman.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <unistd.h>
30 #include <string.h>
31 #include <errno.h>
32 
33 void
usage(void)34 usage(void)
35 {
36 	fprintf(stderr,
37 	    "usage: rw_mmap -n <size>[b|k|m|g] -f <filename>\n");
38 	exit(1);
39 }
40 
41 int
main(int argc,char ** argv)42 main(int argc, char **argv)
43 {
44 	char *suffix;
45 	char *filename = NULL;
46 	char *file_addr;
47 	char *p;
48 	size_t filesize;
49 	ssize_t blksize;
50 	size_t cnt = 1;
51 	size_t mul = 1;
52 	int c, fid;
53 	char *buf;
54 
55 	/*
56 	 * parse arguments
57 	 */
58 	while ((c = getopt(argc, argv, "n:f:")) != -1) {
59 		switch (c) {
60 		case 'n':
61 			cnt = (size_t)strtoul(optarg, &suffix, 0);
62 			if (cnt == 0)
63 				goto bad_n_arg;
64 			switch (*suffix) {
65 			case '\0':
66 			case 'b':
67 				mul = 1;
68 				break;
69 			case 'k':
70 				mul = 1024;
71 				break;
72 			case 'm':
73 				mul = (1024 * 1024);
74 				break;
75 			case 'g':
76 				mul = (1024 * 1024 * 1024);
77 				break;
78 			default:
79 			bad_n_arg:
80 				fprintf(stderr, "-n %s: invalid size\n",
81 				    optarg);
82 				return (1);
83 			}
84 			cnt = cnt * mul;
85 			break;
86 
87 		case 'f': /* target file */
88 			filename = optarg;
89 			break;
90 
91 		case ':':   /* missing optarg */
92 			fprintf(stderr,
93 			    "Option -%c requires an arg\n", optopt);
94 			usage();
95 			break;
96 		case '?':
97 			fprintf(stderr,
98 			    "Unrecognized option: -%c\n", optopt);
99 			usage();
100 			break;
101 		}
102 	}
103 
104 	/* open test file */
105 	fid = open(filename, O_RDWR | O_CREAT | O_TRUNC,
106 	    S_IRUSR | S_IWUSR | S_IROTH | S_IWOTH);
107 	if (fid == -1) {
108 		fprintf(stderr, "open %s error=%d\n", filename, errno);
109 		return (1);
110 	}
111 
112 	/* extend file */
113 	filesize = cnt;
114 	if (ftruncate(fid, filesize) == -1) {
115 		fprintf(stderr, "ftrunc %s error=%d\n", filename, errno);
116 		return (1);
117 	}
118 
119 	/* map file */
120 	file_addr = mmap(NULL, filesize,
121 	    PROT_READ | PROT_WRITE, MAP_SHARED, fid, 0);
122 	if (file_addr == MAP_FAILED) {
123 		fprintf(stderr, "mmap %s error=%d\n", filename, errno);
124 		return (1);
125 	}
126 
127 	blksize = 4096;
128 	buf = malloc(blksize);
129 	if (buf == NULL) {
130 		fprintf(stderr, "malloc failed\n");
131 		return (1);
132 	}
133 
134 	/* verify fread and mmap see the same data */
135 	p = file_addr + 2013; /* not aligned to 4KB, on purpose */
136 	lseek(fid, 2013, SEEK_SET);
137 	while (p < file_addr + filesize) {
138 		blksize = read(fid, buf, blksize);
139 		if (blksize < 0) {
140 			perror(filename);
141 			return (1);
142 		}
143 		if (blksize == 0)
144 			break;
145 		if (memcmp(buf, p, blksize) != 0) {
146 			fprintf(stderr, "memcmp failed 1\n");
147 			return (1);
148 		}
149 		p += blksize;
150 	}
151 
152 	/* modify file through mmap, verify fread can see the change */
153 	blksize = 4096;
154 	p = file_addr + 2013; /* not aligned to 4KB */
155 	lseek(fid, 2013, SEEK_SET);
156 	c = 'a';
157 	while (p < file_addr + filesize) {
158 		if (p + blksize > file_addr + filesize)
159 			blksize = file_addr + filesize - p;
160 		memset(p, c++, blksize);
161 		blksize = read(fid, buf, blksize);
162 		if (blksize < 0) {
163 			perror(filename);
164 			return (1);
165 		}
166 		if (blksize == 0)
167 			break;
168 		if (memcmp(buf, p, blksize) != 0) {
169 			fprintf(stderr, "memcmp failed 2\n");
170 			return (1);
171 		}
172 		p += blksize;
173 	}
174 
175 	/* modify file through fwrite, verify mmap can see the change */
176 	blksize = 4096;
177 	p = file_addr + 2013; /* not aligned to 4KB */
178 	lseek(fid, 2013, SEEK_SET);
179 	c = 'Z';
180 	while (p < file_addr + filesize) {
181 		if (p + blksize > file_addr + filesize)
182 			blksize = file_addr + filesize - p;
183 		memset(buf, c--, blksize);
184 		blksize = write(fid, buf, blksize);
185 		if (blksize < 0) {
186 			perror(filename);
187 			return (1);
188 		}
189 		if (blksize == 0)
190 			break;
191 		if (memcmp(buf, p, blksize) != 0) {
192 			fprintf(stderr, "memcmp failed 3\n");
193 			return (1);
194 		}
195 		p += blksize;
196 	}
197 
198 	/* sync pages to file */
199 	if (msync(file_addr, filesize, MS_SYNC) == -1) {
200 		fprintf(stderr, "msync %s error=%d\n", filename, errno);
201 		return (1);
202 	}
203 
204 	/* unmap file */
205 	if (munmap(file_addr, filesize) == -1) {
206 		fprintf(stderr, "munmap %s error=%d\n", filename, errno);
207 		return (1);
208 	}
209 
210 	/* close file */
211 	if (close(fid) == -1) {
212 		fprintf(stderr, "close %s error=%d\n", filename, errno);
213 		return (1);
214 	}
215 
216 	return (0);
217 }
218