1/* IBM Corporation */
2/* 01/03/2003	Port to LTP avenkat@us.ibm.com */
3/* 06/30/2001	Port to Linux	nsharoff@us.ibm.com */
4
5/*
6 *   Copyright (c) International Business Machines  Corp., 2003
7 *
8 *   This program is free software;  you can redistribute it and/or modify
9 *   it under the terms of the GNU General Public License as published by
10 *   the Free Software Foundation; either version 2 of the License, or
11 *   (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
16 *   the GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program;  if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
23/*
24 *	Mmap a sparse file and then fiddle with the hole in the middle.
25 *	Then check the file contents.
26 *
27 *  Usage: mmapstress07 filename holesize e_pageskip sparseoff
28 *  EXAMPLE: mmapstress07 myfile 4096 1 4096
29 */
30#include <stdio.h>
31#include <sys/types.h>
32#include <sys/mman.h>
33#include <stdlib.h>
34#include <unistd.h>
35#include <fcntl.h>
36#include <signal.h>
37#include <errno.h>
38#include <sys/wait.h>
39#include "test.h"
40#define FAILED 0
41#define PASSED 1
42
43static char *tmpname;
44
45#define ERROR(M)	(void)fprintf(stderr, "%s: errno = %d: " M "\n", \
46			argv[0], errno)
47
48#define CLEANERROR(M)	(void)close(rofd); \
49			(void)close(rwfd); \
50			(void)unlink(tmpname); \
51			ERROR(M)
52
53#define CATCH_SIG(SIG) \
54        if (sigaction(SIG, &sa, 0) == -1) { \
55                ERROR("couldn't catch signal " #SIG); \
56                exit(1); \
57        }
58
59extern time_t time(time_t *);
60extern char *ctime(const time_t *);
61extern void exit(int);
62static int checkchars(int fd, char val, int n);
63
64char *TCID = "mmapstress07";
65
66int local_flag = PASSED;
67int block_number;
68FILE *temp;
69int TST_TOTAL = 1;
70
71int anyfail();
72void ok_exit();
73
74 /*ARGSUSED*/ static
75void cleanup(int sig)
76{
77	/*
78	 * Don't check error codes - we could be signaled before the file is
79	 * created.
80	 */
81	(void)unlink(tmpname);
82	exit(1);
83}
84
85int main(int argc, char **argv)
86{
87	size_t pagesize = (size_t) sysconf(_SC_PAGE_SIZE);
88	caddr_t mapaddr;
89	time_t t;
90	int rofd, rwfd, i;
91	struct sigaction sa;
92	int e_pageskip;
93#ifdef LARGE_FILE
94	off64_t holesize;
95	off64_t sparseoff;
96#else /* LARGE_FILE */
97	off_t holesize;
98	off_t sparseoff;
99#endif /* LARGE_FILE */
100
101	(void)time(&t);
102//      (void)printf("%s: Started %s", argv[0], ctime(&t));
103	/* Test fsync & mmap over a hole in a sparse file & extend fragment */
104	if (argc < 2 || argc > 5) {
105		fprintf(stderr,
106			"Usage: mmapstress07 filename holesize e_pageskip sparseoff\n");
107		/*****	**	LTP Port 02/01/03	**	**** */
108		fprintf(stderr,
109			"\t*holesize should be a multiple of pagesize\n");
110		fprintf(stderr, "\t*e_pageskip should be 1 always \n");
111		fprintf(stderr,
112			"\t*sparseoff should be a multiple of pagesize\n");
113		fprintf(stderr, "Example: mmapstress07 myfile 4096 1 8192\n");
114		/*****	**	******	*****	*****	**	02/01/03 */
115		anyfail();	/* LTP Port */
116	}
117	tst_tmpdir();
118	tmpname = argv[1];
119
120	if (argc >= 3) {
121#ifdef LARGE_FILE
122		holesize = atoll(argv[2]);
123#else /* LARGE_FILE */
124		holesize = atoi(argv[2]);
125#endif /* LARGE_FILE */
126	} else
127		holesize = pagesize;
128
129	if (argc >= 4)
130		e_pageskip = atoi(argv[3]);
131	else
132		e_pageskip = 1;
133
134	if (argc >= 5) {
135#ifdef LARGE_FILE
136		sparseoff = atoll(argv[4]);
137#else /* LARGE_FILE */
138		sparseoff = atoi(argv[4]);
139#endif /* LARGE_FILE */
140	} else
141		sparseoff = pagesize * 2;
142
143	sa.sa_handler = cleanup;
144	sa.sa_flags = 0;
145	if (sigemptyset(&sa.sa_mask)) {
146		ERROR("sigemptyset failed");
147		return 1;
148	}
149	CATCH_SIG(SIGINT);
150	CATCH_SIG(SIGQUIT);
151	CATCH_SIG(SIGTERM);
152#ifdef LARGE_FILE
153	if ((rofd = open64(tmpname, O_RDONLY | O_CREAT, 0777)) == -1) {
154#else /* LARGE_FILE */
155	if ((rofd = open(tmpname, O_RDONLY | O_CREAT, 0777)) == -1) {
156#endif /* LARGE_FILE */
157		ERROR("couldn't reopen rofd for reading");
158		anyfail();	/* LTP Port */
159	}
160#ifdef LARGE_FILE
161	if ((rwfd = open64(tmpname, O_RDWR)) == -1) {
162#else /* LARGE_FILE */
163	if ((rwfd = open(tmpname, O_RDWR)) == -1) {
164#endif /* LARGE_FILE */
165		CLEANERROR("couldn't reopen rwfd for read/write");
166		anyfail();	/* LTP Port */
167	}
168#ifdef LARGE_FILE
169	if (lseek64(rwfd, sparseoff, SEEK_SET) < 0) {
170#else /* LARGE_FILE */
171	if (lseek(rwfd, sparseoff, SEEK_SET) < 0) {
172#endif /* LARGE_FILE */
173		perror("lseek");
174		anyfail();	/* LTP Port */
175	}
176	/* fill file with junk. */
177	i = 0;
178	while (i < pagesize && write(rwfd, "a", 1) == 1)
179		i++;
180	if (i != pagesize) {
181		CLEANERROR("couldn't fill first part of file with junk");
182		anyfail();	/* LTP Port */
183	}
184#ifdef LARGE_FILE
185	if (lseek64(rwfd, holesize, SEEK_CUR) == -1) {
186#else /* LARGE_FILE */
187	if (lseek(rwfd, holesize, SEEK_CUR) == -1) {
188#endif /* LARGE_FILE */
189		CLEANERROR("couldn't create hole in file");
190		anyfail();	/* LTP Port */
191	}
192	/* create fragment */
193	i = 0;
194	while (i < (pagesize >> 1) && write(rwfd, "b", 1) == 1)
195		i++;
196	if (i != (pagesize >> 1)) {
197		CLEANERROR("couldn't fill second part of file with junk");
198		anyfail();	/* LTP Port */
199	}
200	/* At this point fd contains 1 page of a's, holesize bytes skipped,
201	 * 1/2 page of b's.
202	 */
203
204#ifdef LARGE_FILE
205	if ((mapaddr = mmap64((caddr_t) 0, pagesize * 2 + holesize, PROT_READ,
206			      MAP_SHARED | MAP_FILE, rofd,
207			      sparseoff)) == (caddr_t) - 1) {
208#else /* LARGE_FILE */
209	if ((mapaddr = mmap((caddr_t) 0, pagesize * 2 + holesize, PROT_READ,
210			    MAP_SHARED | MAP_FILE, rofd,
211			    sparseoff)) == (caddr_t) - 1) {
212#endif /* LARGE_FILE */
213		CLEANERROR("mmap tmp file failed");
214		anyfail();	/* LTP Port */
215	}
216	/* fill out remainder of page + one more page to extend mmapped flag */
217	while (i < 2 * pagesize && write(rwfd, "c", 1) == 1)
218		i++;
219	if (i != 2 * pagesize) {
220		CLEANERROR("couldn't fill second part of file with junk");
221		anyfail();	/* LTP Port */
222	}
223	/* fiddle with mmapped hole */
224	if (*(mapaddr + pagesize + (holesize >> 1)) != 0) {
225		CLEANERROR("hole not filled with 0's");
226		anyfail();	/* LTP Port */
227	}
228#ifdef LARGE_FILE
229	if (lseek64(rwfd, sparseoff + e_pageskip * pagesize, SEEK_SET) == -1) {
230#else /* LARGE_FILE */
231	if (lseek(rwfd, sparseoff + e_pageskip * pagesize, SEEK_SET) == -1) {
232#endif /* LARGE_FILE */
233		CLEANERROR("couldn't lseek back to put e's in hole");
234		anyfail();	/*LTP Port */
235	}
236	i = 0;
237	while (i < pagesize && write(rwfd, "e", 1) == 1)
238		i++;
239	if (i != pagesize) {
240		CLEANERROR("couldn't part of hole with e's");
241		anyfail();	/*LTP Port */
242	}
243	if (fsync(rwfd) == -1) {
244		CLEANERROR("fsync failed");
245		anyfail();	/* LTP Port */
246	}
247#ifdef LARGE_FILE
248	if (lseek64(rofd, sparseoff, SEEK_SET) == -1) {
249#else /* LARGE_FILE */
250	if (lseek(rofd, sparseoff, SEEK_SET) == -1) {
251#endif /* LARGE_FILE */
252		CLEANERROR("couldn't lseek to begining to verify contents");
253		anyfail();	/* LTP Port */
254	}
255	if (munmap(mapaddr, holesize + 2 * pagesize) == -1) {
256		CLEANERROR("munmap of tmp file failed");
257		anyfail();	/* LTP Port */
258	}
259	/* check file's contents */
260	if (checkchars(rofd, 'a', pagesize)) {
261		CLEANERROR("first page not filled with a's");
262		anyfail();	/* LTP Port */
263	}
264	if (checkchars(rofd, '\0', (e_pageskip - 1) * pagesize)) {
265		CLEANERROR("e_skip not filled with 0's");
266		anyfail();	/* LTP Port */
267	}
268	if (checkchars(rofd, 'e', pagesize)) {
269		CLEANERROR("part after first 0's not filled with e's");
270		anyfail();	/* LTP Port */
271	}
272	if (checkchars(rofd, '\0', holesize - e_pageskip * pagesize)) {
273		CLEANERROR("second hole section not filled with 0's");
274		anyfail();	/* LTP Port */
275	}
276	if (checkchars(rofd, 'b', (pagesize >> 1))) {
277		CLEANERROR("next to last half page not filled with b's");
278		anyfail();	/* LTP Port */
279	}
280	if (checkchars(rofd, 'c', pagesize + (pagesize >> 1))) {
281		CLEANERROR("extended fragment not filled with c's");
282		anyfail();	/* LTP Port */
283	}
284	if (close(rofd) == -1) {
285		CLEANERROR("second close of rofd failed");
286		anyfail();	/* LTP Port */
287	}
288	if (unlink(tmpname) == -1) {
289		CLEANERROR("unlink failed");
290		anyfail();	/* LTP Port */
291	}
292	(void)time(&t);
293//      (void)printf("%s: Finished %s", argv[0], ctime(&t));
294	ok_exit();
295	tst_exit();
296}
297
298/* checkchars
299 * 	verrify that the next n characters of file fd are of value val.
300 *	0 = success; -1 = failure
301 */
302static int checkchars(int fd, char val, int n)
303{
304	int i;
305	char buf;
306
307	for (i = 0; i < n && read(fd, &buf, 1) == 1; i++)
308		if (buf != val)
309			return -1;
310	return 0;
311}
312
313/*****	**	LTP Port	**	*****/
314int anyfail(void)
315{
316	tst_brkm(TFAIL, tst_rmdir, "Test failed\n");
317}
318
319void ok_exit(void)
320{
321	tst_resm(TPASS, "Test passed\n");
322	tst_rmdir();
323	tst_exit();
324}
325
326/*****	**	******		**	*****/
327