dd.c (6564B)
1 /* (C) 2011-2012 Sebastian Krahmer all rights reserved. 2 * 3 * Optimized dd, to speed up backups etc. 4 * 5 * Permission has been granted to release this code under MIT/X. 6 * The original code is at https://github.com/stealth/odd. This 7 * version of the code has been modified by sin@2f30.org. 8 */ 9 #include <sys/ioctl.h> 10 #include <sys/mount.h> 11 #include <sys/stat.h> 12 #include <sys/time.h> 13 #include <sys/types.h> 14 #include <sys/vfs.h> 15 16 #include <errno.h> 17 #include <fcntl.h> 18 #include <inttypes.h> 19 #include <signal.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <stdint.h> 23 #include <string.h> 24 #include <time.h> 25 #include <unistd.h> 26 27 #include "util.h" 28 29 struct dd_config { 30 const char *in, *out; 31 uint64_t skip, seek, count, b_in, b_out, rec_in, rec_out; 32 off_t fsize; 33 blksize_t bs; 34 char quiet, nosync, notrunc, direct; 35 time_t t_start, t_end; 36 }; 37 38 static int sigint = 0; 39 40 static void 41 sig_int(int unused_1, siginfo_t *unused_2, void *unused_3) 42 { 43 (void) unused_1; 44 (void) unused_2; 45 (void) unused_3; 46 sigint = 1; 47 } 48 49 static int 50 prepare_copy(struct dd_config *ddc, int *ifd, int *ofd) 51 { 52 struct stat st; 53 int fli = O_RDONLY|O_LARGEFILE|O_NOCTTY, flo = O_WRONLY|O_LARGEFILE|O_CREAT|O_NOATIME|O_NOCTTY; 54 long pagesize; 55 56 if (ddc->direct) { 57 fli |= O_DIRECT; 58 flo |= O_DIRECT; 59 } 60 61 if (!ddc->in) *ifd = 0; 62 else if ((*ifd = open(ddc->in, fli)) < 0) 63 return -1; 64 65 if (fstat(*ifd, &st) < 0) 66 return -1; 67 68 ddc->fsize = st.st_size; 69 70 /* If "bsize" is not given, use optimum of both FS' */ 71 if (!ddc->bs) { 72 struct statfs fst; 73 memset(&fst, 0, sizeof(fst)); 74 pagesize = sysconf(_SC_PAGESIZE); 75 if (pagesize <= 0) 76 pagesize = 0x1000; 77 if (statfs(ddc->out, &fst) < 0 || fst.f_bsize == 0) 78 fst.f_bsize = pagesize; 79 if ((unsigned long)fst.f_bsize > (unsigned long)st.st_blksize) 80 ddc->bs = fst.f_bsize; 81 else 82 ddc->bs = st.st_blksize; 83 if (ddc->bs == 0) 84 ddc->bs = pagesize; 85 } 86 87 /* check if device or regular file */ 88 if (!S_ISREG(st.st_mode)) { 89 if (S_ISBLK(st.st_mode)) { 90 if (ioctl(*ifd, BLKGETSIZE64, &ddc->fsize) < 0) { 91 close(*ifd); 92 return -1; 93 } 94 } else { 95 ddc->fsize = (off_t)-1; 96 } 97 } 98 99 /* skip and seek are in block items */ 100 ddc->skip *= ddc->bs; 101 ddc->seek *= ddc->bs; 102 103 /* skip more bytes than are inside source file? */ 104 if (ddc->fsize != (off_t)-1 && ddc->skip >= (uint64_t)ddc->fsize) { 105 errno = EINVAL; 106 close(*ifd); 107 return -1; 108 } 109 110 if (!ddc->seek && !ddc->notrunc) 111 flo |= O_TRUNC; 112 113 if (!ddc->out) *ofd = 1; 114 else if ((*ofd = open(ddc->out, flo, st.st_mode)) < 0) { 115 close(*ifd); 116 return -1; 117 } 118 119 if (ddc->seek && !ddc->notrunc) { 120 if (fstat(*ofd, &st) < 0) 121 return -1; 122 if (!S_ISREG(st.st_mode)) 123 ; 124 else if (ftruncate(*ofd, ddc->seek) < 0) 125 return -1; 126 } 127 128 if (lseek(*ifd, ddc->skip, SEEK_CUR) < 0) { 129 char buffer[ddc->bs]; 130 for (uint64_t i = 0; i < ddc->skip; i += ddc->bs) { 131 if (read(*ifd, &buffer, ddc->bs) < 0) { 132 errno = EINVAL; 133 close(*ifd); 134 return -1; 135 } 136 } 137 } 138 lseek(*ofd, ddc->seek, SEEK_CUR); 139 posix_fadvise(*ifd, ddc->skip, 0, POSIX_FADV_SEQUENTIAL); 140 posix_fadvise(*ofd, 0, 0, POSIX_FADV_DONTNEED); 141 142 /* count is in block items too */ 143 ddc->count *= ddc->bs; 144 145 /* If no count is given, its the filesize minus skip offset */ 146 if (ddc->count == (uint64_t) -1) 147 ddc->count = ddc->fsize - ddc->skip; 148 149 return 0; 150 } 151 152 static int 153 copy_splice(struct dd_config *ddc) 154 { 155 int ifd, ofd, p[2] = {-1, -1}; 156 ssize_t r = 0; 157 size_t n = 0; 158 159 if (prepare_copy(ddc, &ifd, &ofd) < 0) 160 return -1; 161 if (pipe(p) < 0) { 162 close(ifd); close(ofd); 163 close(p[0]); close(p[1]); 164 return -1; 165 } 166 #ifdef F_SETPIPE_SZ 167 for (n = 29; n >= 20; --n) { 168 if (fcntl(p[0], F_SETPIPE_SZ, 1<<n) != -1) 169 break; 170 } 171 errno = 0; 172 #endif 173 n = ddc->bs; 174 for (;ddc->b_out != ddc->count && !sigint;) { 175 if (r < 0) 176 break; 177 if (n > ddc->count - ddc->b_out) 178 n = ddc->count - ddc->b_out; 179 r = splice(ifd, NULL, p[1], NULL, n, SPLICE_F_MORE); 180 if (r <= 0) 181 break; 182 ++ddc->rec_in; 183 r = splice(p[0], NULL, ofd, NULL, r, SPLICE_F_MORE); 184 if (r <= 0) 185 break; 186 ddc->b_out += r; 187 ++ddc->rec_out; 188 } 189 190 if (sigint) 191 fprintf(stderr, "SIGINT! Aborting ...\n"); 192 193 close(ifd); 194 close(ofd); 195 close(p[0]); 196 close(p[1]); 197 if (r < 0) 198 return -1; 199 return 0; 200 } 201 202 static int 203 copy(struct dd_config *ddc) 204 { 205 int r = 0; 206 207 ddc->t_start = time(NULL); 208 209 r = copy_splice(ddc); 210 ddc->t_end = time(NULL); 211 212 /* avoid div by zero */ 213 if (ddc->t_start == ddc->t_end) 214 ++ddc->t_end; 215 return r; 216 } 217 218 static void 219 print_stat(const struct dd_config *ddc) 220 { 221 if (ddc->quiet) 222 return; 223 224 fprintf(stderr, "%"PRIu64" records in\n", ddc->rec_in); 225 fprintf(stderr, "%"PRIu64" records out\n", ddc->rec_out); 226 fprintf(stderr, "%"PRIu64" bytes (%"PRIu64" MB) copied", ddc->b_out, 227 ddc->b_out/(1<<20)); 228 fprintf(stderr, ", %lu s, %f MB/s\n", 229 (unsigned long)ddc->t_end - ddc->t_start, 230 ((double)(ddc->b_out/(1<<20)))/(ddc->t_end - ddc->t_start)); 231 } 232 233 static void 234 usage(void) 235 { 236 eprintf("usage: %s [-h] [if=infile] [of=outfile] [bs[=N]] [seek=N] " 237 "[skip=N] [count=N] [direct] [quiet] [nosync]" 238 "[conv=notrunc]\n", argv0); 239 } 240 241 int 242 main(int argc, char *argv[]) 243 { 244 int i = 0; 245 char buf[1024]; 246 struct dd_config config; 247 struct sigaction sa; 248 249 argv0 = argv[0]; 250 memset(&config, 0, sizeof(config)); 251 config.bs = 1<<16; 252 config.in = NULL; 253 config.out = NULL; 254 config.count = (uint64_t) -1; 255 256 /* emulate 'dd' argument parsing */ 257 for (i = 1; i < argc; ++i) { 258 memset(buf, 0, sizeof(buf)); 259 if (strncmp(argv[i], "if=", 3) == 0) 260 config.in = argv[i]+3; 261 else if (strncmp(argv[i], "of=", 3) == 0) 262 config.out = argv[i]+3; 263 else if (sscanf(argv[i], "skip=%1023s", buf) == 1) 264 config.skip = estrtoul(buf, 0); 265 else if (sscanf(argv[i], "seek=%1023s", buf) == 1) 266 config.seek = estrtoul(buf, 0); 267 else if (sscanf(argv[i], "count=%1023s", buf) == 1) 268 config.count = estrtoul(buf, 0); 269 else if (strcmp(argv[i], "direct") == 0) 270 config.direct = 1; 271 else if (sscanf(argv[i], "bs=%1023s", buf) == 1) 272 config.bs = estrtoul(buf, 0); 273 else if (strcmp(argv[i], "bs") == 0) 274 config.bs = 0; 275 else if (strcmp(argv[i], "quiet") == 0) 276 config.quiet = 1; 277 else if (strcmp(argv[i], "nosync") == 0) 278 config.nosync = 1; 279 else if (strcmp(argv[i], "conv=notrunc") == 0) 280 config.notrunc = 1; 281 else if (strcmp(argv[i], "-h") == 0) 282 usage(); 283 } 284 285 signal(SIGPIPE, SIG_IGN); 286 287 sa.sa_flags = SA_SIGINFO; 288 sigemptyset(&sa.sa_mask); 289 sa.sa_sigaction = sig_int; 290 291 if (sigaction(SIGINT, &sa, NULL) == -1) 292 weprintf("sigaction"); 293 294 if (copy(&config) < 0) 295 weprintf("copy:"); 296 print_stat(&config); 297 298 if (config.nosync == 0) 299 sync(); 300 return errno; 301 }