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