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 43 no_memory(void) { 44 (void) fprintf(stderr, "malloc failed\n"); 45 return (UMEM_CALLBACK_EXIT(255)); 46 } 47 48 static void 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 * 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 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 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