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 (c) 2014 by Delphix. All rights reserved.
14  */
15 
16 #include <stdio.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <stdlib.h>
20 #include <umem.h>
21 #include <stddef.h>
22 #include <string.h>
23 #include <sys/types.h>
24 #include <sys/errno.h>
25 #include <sys/list.h>
26 
27 extern int errno;
28 
29 typedef enum {
30 	SEG_HOLE,
31 	SEG_DATA,
32 	SEG_TYPES
33 } seg_type_t;
34 
35 typedef struct segment {
36 	list_node_t	seg_node;
37 	seg_type_t	seg_type;
38 	off_t		seg_offset;
39 	off_t		seg_len;
40 } seg_t;
41 
42 static int
no_memory(void)43 no_memory(void) {
44 	(void) fprintf(stderr, "malloc failed\n");
45 	return (UMEM_CALLBACK_EXIT(255));
46 }
47 
48 static void
usage(char * msg,int exit_value)49 usage(char *msg, int exit_value)
50 {
51 	(void) fprintf(stderr, "mkholes [-d|h offset:length] ... filename\n");
52 	(void) fprintf(stderr, "%s\n", msg);
53 	exit(exit_value);
54 }
55 
56 static char *
get_random_buffer(size_t len)57 get_random_buffer(size_t len)
58 {
59 	int	rand_fd;
60 	char	*buf;
61 
62 	buf = umem_alloc(len, UMEM_NOFAIL);
63 
64 	/*
65 	 * Fill the buffer from /dev/urandom to counteract the
66 	 * effects of compression.
67 	 */
68 	if ((rand_fd = open("/dev/urandom", O_RDONLY)) < 0) {
69 		perror("open /dev/urandom failed");
70 		exit(1);
71 	}
72 
73 	if (read(rand_fd, buf, len) < 0) {
74 		perror("read /dev/urandom failed");
75 		exit(1);
76 	}
77 
78 	(void) close(rand_fd);
79 
80 	return (buf);
81 }
82 
83 static void
push_segment(list_t * seg_list,seg_type_t seg_type,char * optarg)84 push_segment(list_t *seg_list, seg_type_t seg_type, char *optarg)
85 {
86 	char		*off_str, *len_str;
87 	static off_t	file_size = 0;
88 	off_t		off, len;
89 	seg_t		*seg;
90 
91 	off_str = strtok(optarg, ":");
92 	len_str = strtok(NULL, ":");
93 
94 	if (off_str == NULL || len_str == NULL)
95 		usage("Bad offset or length", 1);
96 
97 	off = strtoull(off_str, NULL, 0);
98 	len = strtoull(len_str, NULL, 0);
99 
100 	if (file_size >= off + len)
101 		usage("Ranges must ascend and may not overlap.", 1);
102 	file_size = off + len;
103 
104 	seg = umem_alloc(sizeof (seg_t), UMEM_NOFAIL);
105 	seg->seg_type = seg_type;
106 	seg->seg_offset = off;
107 	seg->seg_len = len;
108 
109 	list_insert_tail(seg_list, seg);
110 }
111 
112 int
main(int argc,char * argv[])113 main(int argc, char *argv[])
114 {
115 	int	c, fd;
116 	char	*fname;
117 	list_t	seg_list;
118 	seg_t	*seg;
119 
120 	umem_nofail_callback(no_memory);
121 	list_create(&seg_list, sizeof (seg_t), offsetof(seg_t, seg_node));
122 
123 	while ((c = getopt(argc, argv, "d:h:")) != -1) {
124 		switch (c) {
125 		case 'd':
126 			push_segment(&seg_list, SEG_DATA, optarg);
127 			break;
128 		case 'h':
129 			push_segment(&seg_list, SEG_HOLE, optarg);
130 			break;
131 		}
132 	}
133 	argc -= optind;
134 	argv += optind;
135 
136 	if ((fname = argv[0]) == NULL)
137 		usage("No filename specified", 1);
138 	fname = argv[0];
139 
140 	if ((fd = open(fname, O_LARGEFILE | O_RDWR | O_CREAT | O_SYNC,
141 	    00666)) < 0) {
142 		perror("open failed");
143 		exit(1);
144 	}
145 
146 	while ((seg = list_remove_head(&seg_list)) != NULL) {
147 		char	*buf, *vbuf;
148 		off_t	off = seg->seg_offset;
149 		off_t	len = seg->seg_len;
150 
151 		if (seg->seg_type == SEG_HOLE) {
152 			struct flock	fl;
153 			off_t bytes_read = 0;
154 			ssize_t readlen = 1024 * 1024 * 16;
155 
156 			fl.l_whence = SEEK_SET;
157 			fl.l_start = off;
158 			fl.l_len = len;
159 			if (fcntl(fd, F_FREESP, &fl) != 0) {
160 				perror("freesp failed");
161 				exit(1);
162 			}
163 
164 			buf = (char *)umem_alloc(readlen, UMEM_NOFAIL);
165 			vbuf = (char *)umem_zalloc(readlen, UMEM_NOFAIL);
166 			while (bytes_read < len) {
167 				ssize_t bytes = pread(fd, buf, readlen, off);
168 				if (bytes < 0) {
169 					perror("pread hole failed");
170 					exit(1);
171 				}
172 
173 				if (memcmp(buf, vbuf, bytes) != 0) {
174 					(void) fprintf(stderr, "Read back hole "
175 					    "didn't match.\n");
176 					exit(1);
177 				}
178 				bytes_read += bytes;
179 				off += bytes;
180 			}
181 
182 			umem_free(buf, readlen);
183 			umem_free(vbuf, readlen);
184 			umem_free(seg, sizeof (seg_t));
185 		} else if (seg->seg_type == SEG_DATA) {
186 			buf = get_random_buffer(len);
187 			vbuf = (char *)umem_alloc(len, UMEM_NOFAIL);
188 			if ((pwrite(fd, buf, len, off)) < 0) {
189 				perror("pwrite failed");
190 				exit(1);
191 			}
192 
193 			if ((pread(fd, vbuf, len, off)) != len) {
194 				perror("pread failed");
195 				exit(1);
196 			}
197 
198 			if (memcmp(buf, vbuf, len) != 0) {
199 				(void) fprintf(stderr, "Read back buf didn't "
200 				    "match.\n");
201 				exit(1);
202 			}
203 
204 			umem_free(buf, len);
205 			umem_free(vbuf, len);
206 			umem_free(seg, sizeof (seg_t));
207 		}
208 	}
209 
210 	(void) close(fd);
211 	return (0);
212 }
213