├── .gitignore ├── .travis.yml ├── COPYING ├── Makefile.am ├── README.md ├── autogen.sh ├── configure.ac └── mmccopy.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | .deps 3 | Makefile 4 | autom4te.cache/ 5 | config.h 6 | config.log 7 | config.status 8 | mmccopy 9 | stamp-h1 10 | *.tar.gz 11 | Makefile.in 12 | aclocal.m4 13 | config.h.in 14 | configure 15 | depcomp 16 | install-sh 17 | missing 18 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | compiler: 4 | - gcc 5 | # - clang 6 | 7 | env: 8 | global: 9 | # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created 10 | # via the "travis encrypt" command using the project repo's public key 11 | - secure: "OL++M6JOhQ4R22AGtKij3vKw2AOUapAGGb6m0TBdkO+lx0FMNOSYWOPxNDdWmC/W3hMLTq6U6avhXSsKyB1WCjtlv4QhYhmWv43dO3bN7iNIgxLa9rQ6VX8GPEUxo1mCtOgxaysb27RjW6SYTR0sRHWEaM7ZDeJ9Yja9tPULBpQ=" 12 | 13 | before_install: 14 | - sudo apt-get update -qq 15 | - sudo apt-get install -qq libconfuse-dev libarchive-dev 16 | 17 | addons: 18 | coverity_scan: 19 | project: 20 | name: "fhunleth/mmccopy" 21 | description: "Convenient alternative to dd for writing images to SDCards" 22 | notification_email: fhunleth@troodon-software.com 23 | build_command_prepend: "./autogen.sh && ./configure" 24 | build_command: "make" 25 | branch_pattern: coverity_scan 26 | 27 | script: 28 | - ./autogen.sh 29 | - ./configure 30 | - make 31 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Frank Hunleth 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS=mmccopy 2 | mmccopy_SOURCES=mmccopy.c 3 | EXTRA_DIST=README.md 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | [![Build Status](https://travis-ci.org/fhunleth/mmccopy.png)](https://travis-ci.org/fhunleth/mmccopy) [![Coverity Scan Build Status](https://scan.coverity.com/projects/4100/badge.svg)](https://scan.coverity.com/projects/4100) 3 | 4 | The `mmccopy` utility is an easier-to-use alternative to `dd` for 5 | writing images to SDCards and MMC memory. It has the following 6 | features: 7 | 8 | 1. Write image data from `stdin` or a file directly to an offset on 9 | the device (this is similar to `dd` except that offsets are 10 | specified in bytes instead of blocks) 11 | 12 | 2. Batch up writes into 1 MiB blocks by default to improve transfer 13 | rate. (`dd` defaults to 512 byte blocks without the `bs` argument) 14 | 15 | 3. Automatically unmount partitions that are using the device. This 16 | prevents data corruption either due to latent writes from the 17 | mounted file systems or due to image writes being cached. 18 | 19 | 4. Provide human and machine readable progress similar to using `pv` 20 | with `dd`, except with the improvement that the percentages track 21 | completed writes rather than initiated writes. 22 | 23 | 5. Automatic detection of MMC and SDCards. This option queries the 24 | user before writing anything by default to avoid accidental 25 | overwrites. 26 | 27 | 6. Optionally run a TRIM command on the MMC or SDCard before writing 28 | to it. This lets you quickly reset the entire memory contents even 29 | if you're only going to write a fraction of it. This is only available 30 | on devices and SDCard readers that support the TRIM command. 31 | 32 | Here's an example run: 33 | 34 | $ sudo mmccopy -p sdcard.img 35 | Use memory card found at /dev/sdc? [y/N] y 36 | 100% 37 | $ 38 | 39 | # Building from source 40 | 41 | Clone or download the source code and run the following: 42 | 43 | ./autogen.sh 44 | ./configure 45 | make 46 | make install 47 | 48 | # Invoking 49 | 50 | ``` 51 | Usage: mmccopy [options] [path] 52 | -d 53 | -n Report numeric progress 54 | -o 55 | -p Report progress (default) 56 | -q Quiet 57 | -r Read from the memory card 58 | -s 59 | -t Run the TRIM command on the memory card before copying 60 | -v Print out the version and exit 61 | -w Write to the memory card (default) 62 | -y Accept automatically found memory card 63 | 64 | The [path] specifies the location of the image to copy to or from 65 | the memory card. If it is unspecified or '-', the image will either 66 | be read from stdin (-w) or written to stdout (-r). 67 | 68 | Examples: 69 | 70 | Write the file sdcard.img to an automatically detected SD Card: 71 | mmccopy sdcard.img 72 | 73 | Read the master boot record (512 bytes @ offset 0) from /dev/sdc: 74 | mmccopy -r -s 512 -o 0 -d /dev/sdc mbr.img 75 | 76 | Offset and size may be specified with the following suffixes: 77 | b 512 78 | kB 1000 79 | K 1024 80 | KiB 1024 81 | MB 1000000 82 | M 1048576 83 | MiB 1048576 84 | GB 1000000000 85 | G 1073741824 86 | GiB 1073741824 87 | ``` 88 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf --install || exit 1 4 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([mmccopy], [1.0.0], [fhunleth@troodon-software.com]) 2 | AC_CONFIG_SRCDIR([mmccopy.c]) 3 | AC_CONFIG_HEADERS([config.h]) 4 | 5 | AM_INIT_AUTOMAKE([-Wall -Werror foreign]) 6 | 7 | # Checks for programs. 8 | AC_PROG_INSTALL 9 | 10 | # Checks for header files. 11 | AC_CHECK_HEADERS([fcntl.h stdlib.h string.h sys/mount.h unistd.h]) 12 | 13 | # Checks for typedefs, structures, and compiler characteristics. 14 | AC_TYPE_SIZE_T 15 | AC_TYPE_SSIZE_T 16 | AC_SYS_LARGEFILE 17 | 18 | # Checks for library functions. 19 | AC_FUNC_MALLOC 20 | AC_CHECK_FUNCS([strdup strstr strtoul]) 21 | 22 | AC_CONFIG_FILES([Makefile]) 23 | AC_OUTPUT 24 | -------------------------------------------------------------------------------- /mmccopy.c: -------------------------------------------------------------------------------- 1 | /* The MIT License (MIT) 2 | * 3 | * Copyright (c) 2013 Frank Hunleth 4 | * 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | * this software and associated documentation files (the "Software"), to deal in 7 | * the Software without restriction, including without limitation the rights to 8 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | * the Software, and to permit persons to whom the Software is furnished to do so, 10 | * subject to the following conditions: 11 | * 12 | * The above copyright notice and this permission notice shall be included in all 13 | * copies or substantial portions of the Software. 14 | * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #include "config.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | 40 | #ifndef BLKDISCARD 41 | #define BLKDISCARD _IO(0x12,119) 42 | #endif 43 | 44 | #define NUM_ELEMENTS(X) (sizeof(X) / sizeof(X[0])) 45 | 46 | #define ONE_KiB (1024ULL) 47 | #define ONE_MiB (1024 * ONE_KiB) 48 | #define ONE_GiB (1024 * ONE_MiB) 49 | 50 | #define COPY_BUFFER_SIZE ONE_MiB 51 | 52 | struct suffix_multiplier 53 | { 54 | const char *suffix; 55 | size_t multiple; 56 | }; 57 | 58 | struct suffix_multiplier suffix_multipliers[] = { 59 | {"b", 512}, 60 | {"kB", 1000}, 61 | {"K", ONE_KiB}, 62 | {"KiB", ONE_KiB}, 63 | {"MB", 1000 * 1000}, 64 | {"M", ONE_MiB}, 65 | {"MiB", ONE_MiB}, 66 | {"GB", 1000 * 1000 * 1000}, 67 | {"G", ONE_GiB}, 68 | {"GiB", ONE_GiB} 69 | }; 70 | 71 | // Global options 72 | static bool numeric_progress = false; 73 | static bool quiet = false; 74 | 75 | void print_version() 76 | { 77 | fprintf(stderr, "%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION); 78 | } 79 | 80 | void print_usage(const char *argv0) 81 | { 82 | fprintf(stderr, "Usage: %s [options] [path]\n", argv0); 83 | fprintf(stderr, " -d \n"); 84 | fprintf(stderr, " -f Run SDCard auto-detection and print the device path\n"); 85 | fprintf(stderr, " -n Report numeric progress\n"); 86 | fprintf(stderr, " -o \n"); 87 | fprintf(stderr, " -p Report progress (default)\n"); 88 | fprintf(stderr, " -q Quiet\n"); 89 | fprintf(stderr, " -r Read from the memory card\n"); 90 | fprintf(stderr, " -s \n"); 91 | fprintf(stderr, " -t Run the TRIM command on the memory card before copying\n"); 92 | fprintf(stderr, " -v Print out the version and exit\n"); 93 | fprintf(stderr, " -w Write to the memory card (default)\n"); 94 | fprintf(stderr, " -y Accept automatically found memory card\n"); 95 | fprintf(stderr, "\n"); 96 | fprintf(stderr, "The [path] specifies the location of the image to copy to or from\n"); 97 | fprintf(stderr, "the memory card. If it is unspecified or '-', the image will either\n"); 98 | fprintf(stderr, "be read from stdin (-w) or written to stdout (-r).\n"); 99 | fprintf(stderr, "\n"); 100 | fprintf(stderr, "The -d argument does not need to be a device file. It can also be a regular file.\n"); 101 | fprintf(stderr, "\n"); 102 | fprintf(stderr, "Examples:\n"); 103 | fprintf(stderr, "\n"); 104 | fprintf(stderr, "Write the file sdcard.img to an automatically detected SD Card:\n"); 105 | fprintf(stderr, " %s sdcard.img\n", argv0); 106 | fprintf(stderr, "\n"); 107 | fprintf(stderr, "Read the master boot record (512 bytes @ offset 0) from /dev/sdc:\n"); 108 | fprintf(stderr, " %s -r -s 512 -o 0 -d /dev/sdc mbr.img\n", argv0); 109 | fprintf(stderr, "\n"); 110 | fprintf(stderr, "Offset and size may be specified with the following suffixes:\n"); 111 | size_t i; 112 | for (i = 0; i < NUM_ELEMENTS(suffix_multipliers); i++) 113 | fprintf(stderr, " %3s %d\n", suffix_multipliers[i].suffix, (int) suffix_multipliers[i].multiple); 114 | } 115 | 116 | size_t parse_size(const char *str) 117 | { 118 | char *suffix; 119 | size_t value = strtoul(str, &suffix, 10); 120 | size_t i; 121 | 122 | if (suffix == str) 123 | errx(EXIT_FAILURE, "Expecting number but got '%s'", str); 124 | 125 | if (*suffix == '\0') 126 | return value; 127 | 128 | for (i = 0; i < NUM_ELEMENTS(suffix_multipliers); i++) { 129 | if (strcmp(suffix_multipliers[i].suffix, suffix) == 0) 130 | return value * suffix_multipliers[i].multiple; 131 | } 132 | 133 | errx(EXIT_FAILURE, "Unknown size multiplier '%s'", suffix); 134 | return 0; 135 | } 136 | 137 | char *unescape_string(const char *input) 138 | { 139 | char *result = (char *) malloc(strlen(input) + 1); 140 | char *p = result; 141 | while (*input) { 142 | if (*input != '\\') { 143 | *p = *input; 144 | p++; 145 | input++; 146 | } else { 147 | input++; 148 | switch (*input) { 149 | case '\"': 150 | case '\\': 151 | default: 152 | *p = *input++; 153 | break; 154 | case 'a': 155 | *p = '\a'; input++; 156 | break; 157 | case 'b': 158 | *p = '\b'; input++; 159 | break; 160 | case 'f': 161 | *p = '\f'; input++; 162 | break; 163 | case 'n': 164 | *p = '\n'; input++; 165 | break; 166 | case 'r': 167 | *p = '\r'; input++; 168 | break; 169 | case 't': 170 | *p = '\t'; input++; 171 | break; 172 | case 'v': 173 | *p = '\v'; input++; 174 | break; 175 | case '0': 176 | case '1': 177 | case '2': 178 | case '3': 179 | case '4': 180 | case '5': 181 | case '6': 182 | case '7': // octal 183 | { 184 | int digits = (input[1] && input[1] >= '0' && input[1] <= '7' ? 1 : 0) 185 | + (input[1] && input[2] && input[2] >= '0' && input[2] <= '7' ? 1 : 0); 186 | int result = *input++ - '0'; 187 | while (digits--) 188 | result = result * 8 + *input++ - '0'; 189 | *p = (char) result; 190 | break; 191 | } 192 | } 193 | p++; 194 | } 195 | } 196 | *p = 0; 197 | return result; 198 | } 199 | 200 | void umount_all_on_dev(const char *mmc_device) 201 | { 202 | FILE *fp = fopen("/proc/mounts", "r"); 203 | if (!fp) 204 | err(EXIT_FAILURE, "/proc/mounts"); 205 | 206 | char *todo[64] = {0}; 207 | int todo_ix = 0; 208 | int i; 209 | 210 | char line[512] = {0}; 211 | while (!feof(fp) && 212 | fgets(line, sizeof(line), fp)) { 213 | char devname[64]; 214 | char mountpoint[256]; 215 | if (sscanf(line, "%63s %255s", devname, mountpoint) != 2) 216 | continue; 217 | 218 | if (strstr(devname, mmc_device) == devname) { 219 | // mmc_device is a prefix of this device, i.e. mmc_device is /dev/sdc 220 | // and /dev/sdc1 is mounted. 221 | 222 | if (todo_ix == NUM_ELEMENTS(todo)) 223 | errx(EXIT_FAILURE, "Device mounted too many times"); 224 | 225 | // strings from /proc/mounts are escaped, so unescape them 226 | todo[todo_ix++] = unescape_string(mountpoint); 227 | } 228 | } 229 | fclose(fp); 230 | 231 | int mtab_exists = (access("/etc/mtab", F_OK) != -1); 232 | for (i = 0; i < todo_ix; i++) { 233 | if (mtab_exists) { 234 | // If /etc/mtab, then call umount(8) so that 235 | // gets updated correctly. 236 | char cmdline[384]; 237 | sprintf(cmdline, "/bin/umount %s", todo[i]); 238 | int rc = system(cmdline); 239 | if (rc != 0) 240 | err(EXIT_FAILURE, "%s", cmdline); 241 | } else { 242 | // No /etc/mtab, so call the kernel directly. 243 | if (umount(todo[i]) < 0) 244 | err(EXIT_FAILURE, "umount %s", todo[i]); 245 | } 246 | } 247 | 248 | for (i = 0; i < todo_ix; i++) 249 | free(todo[i]); 250 | } 251 | 252 | size_t device_size(const char *devpath) 253 | { 254 | int fd = open(devpath, O_RDONLY); 255 | if (fd < 0) 256 | return 0; 257 | 258 | off_t len = lseek(fd, 0, SEEK_END); 259 | close(fd); 260 | 261 | return len < 0 ? 0 : len; 262 | } 263 | 264 | bool is_mmc_device(const char *devpath) 265 | { 266 | // Check 1: Path exists and can read length 267 | size_t len = device_size(devpath); 268 | if (len == 0) 269 | return false; 270 | 271 | // Check 2: Capacity larger than 32 GiB -> false 272 | if (len > (32 * ONE_GiB)) 273 | return false; 274 | 275 | // Certainly there are more checks that we can do 276 | // to avoid false memory card detects... 277 | 278 | return true; 279 | } 280 | 281 | char *find_mmc_device() 282 | { 283 | char *possible[64] = {0}; 284 | size_t possible_ix = 0; 285 | char c; 286 | size_t i; 287 | 288 | // Scan memory cards connected via USB. These are /dev/sd_ devices. 289 | // NOTE: Don't scan /dev/sda, since I don't think this is ever right 290 | // for any use case. 291 | for (c = 'b'; c != 'z'; c++) { 292 | char devpath[64]; 293 | sprintf(devpath, "/dev/sd%c", c); 294 | 295 | if (is_mmc_device(devpath) && possible_ix < NUM_ELEMENTS(possible)) 296 | possible[possible_ix++] = strdup(devpath); 297 | } 298 | 299 | // Scan the mmcblk devices 300 | for (i = 0; i < 16; i++) { 301 | char devpath[64]; 302 | sprintf(devpath, "/dev/mmcblk%d", (int) i); 303 | 304 | if (is_mmc_device(devpath) && possible_ix < NUM_ELEMENTS(possible)) 305 | possible[possible_ix++] = strdup(devpath); 306 | } 307 | 308 | if (possible_ix == 1) { 309 | // Success. 310 | return possible[0]; 311 | } else if (possible_ix == 0) { 312 | if (getuid() != 0) 313 | errx(EXIT_FAILURE, "Memory card couldn't be found automatically.\nTry running as root or specify -? for help"); 314 | else 315 | errx(EXIT_FAILURE, "No memory cards found."); 316 | } else { 317 | fprintf(stderr, "Too many possible memory cards found: \n"); 318 | for (i = 0; i < possible_ix; i++) 319 | fprintf(stderr, " %s\n", possible[i]); 320 | fprintf(stderr, "Pick one and specify it explicitly on the commandline.\n"); 321 | exit(EXIT_FAILURE); 322 | } 323 | } 324 | 325 | void trim_mmc(int fd) 326 | { 327 | // Run the TRIM command on the MMC file handle to free all currently 328 | // allocated blocks back to the MMC firmware. 329 | uint64_t range[2]; 330 | 331 | range[0] = 0; 332 | if (ioctl(fd, BLKGETSIZE64, &range[1])) 333 | err(EXIT_FAILURE, "Can't get size of device. Check that device is block device"); 334 | 335 | if (ioctl(fd, BLKDISCARD, &range)) 336 | err(EXIT_FAILURE, "BLKDISCARD (TRIM command) failed"); 337 | 338 | } 339 | 340 | double calculate_progress(size_t written, size_t total) 341 | { 342 | if (total > 0) 343 | return 100.0 * written / total; 344 | else 345 | return 0; 346 | } 347 | 348 | void pretty_size(size_t amount, char *out) 349 | { 350 | if (amount >= ONE_GiB) 351 | sprintf(out, "%.2f GiB", ((double) amount) / ONE_GiB); 352 | else if (amount >= ONE_MiB) 353 | sprintf(out, "%.2f MiB", ((double) amount) / ONE_MiB); 354 | else if (amount >= ONE_KiB) 355 | sprintf(out, "%d KiB", (int) (amount / ONE_KiB)); 356 | else 357 | sprintf(out, "%d bytes", (int) amount); 358 | } 359 | 360 | void report_progress(size_t written, size_t total) 361 | { 362 | if (quiet) 363 | return; 364 | 365 | if (numeric_progress) { 366 | // If numeric, write the percentage if we can figure it out. 367 | printf("%.0f\n", calculate_progress(written, total)); 368 | } else { 369 | // If this is for a human, then print the percent complete 370 | // if we can calculate it or the bytes written. 371 | if (total > 0) 372 | printf("\r%.0f%%", calculate_progress(written, total)); 373 | else { 374 | char sizestr[32]; 375 | pretty_size(written, sizestr); 376 | printf("\r%s ", sizestr); 377 | } 378 | fflush(stdout); 379 | } 380 | } 381 | 382 | void copy(int from_fd, int to_fd, size_t total_to_copy) 383 | { 384 | char *buffer = malloc(COPY_BUFFER_SIZE); 385 | off_t total_written = 0; 386 | while (total_to_copy == 0 || total_written < total_to_copy) { 387 | size_t amount_to_read = COPY_BUFFER_SIZE; 388 | if (total_to_copy != 0 && total_to_copy < amount_to_read) 389 | amount_to_read = total_to_copy; 390 | 391 | ssize_t amount_read = read(from_fd, buffer, amount_to_read); 392 | if (amount_read < 0) 393 | err(EXIT_FAILURE, "read"); 394 | 395 | if (amount_read == 0) 396 | break; 397 | 398 | char *ptr = buffer; 399 | do { 400 | ssize_t amount_written = write(to_fd, ptr, amount_read); 401 | if (amount_written < 0) { 402 | if (errno == EINTR) 403 | continue; 404 | else 405 | err(EXIT_FAILURE, "write"); 406 | } 407 | 408 | amount_read -= amount_written; 409 | ptr += amount_written; 410 | total_written += amount_written; 411 | } while (amount_read > 0); 412 | 413 | report_progress(total_written, total_to_copy); 414 | } 415 | free(buffer); 416 | 417 | // Print a linefeed at the end so that the final progress report has 418 | // a new line after it. Numeric progress already prints linefeeds, so 419 | // don't add another on those. 420 | if (!quiet && !numeric_progress) 421 | printf("\n"); 422 | } 423 | 424 | int main(int argc, char *argv[]) 425 | { 426 | const char *mmc_device = 0; 427 | const char *data_pathname = "-"; 428 | size_t total_to_copy = 0; 429 | off_t seek_offset = 0; 430 | bool accept_found_device = false; 431 | bool read_from_mmc = false; 432 | bool trim_mmc_device = false; 433 | 434 | // Memory cards are too big to bother with systems 435 | // that don't support large file sizes any more. 436 | if (sizeof(off_t) != 8) 437 | errx(EXIT_FAILURE, "recompile with largefile support"); 438 | 439 | int opt; 440 | while ((opt = getopt(argc, argv, "d:fno:pqrs:tvwy")) != -1) { 441 | switch (opt) { 442 | case 'd': 443 | mmc_device = optarg; 444 | break; 445 | case 'f': 446 | mmc_device = find_mmc_device(); 447 | printf("%s", mmc_device); 448 | exit(EXIT_SUCCESS); 449 | break; 450 | case 's': 451 | total_to_copy = parse_size(optarg); 452 | break; 453 | case 'o': 454 | seek_offset = parse_size(optarg); 455 | break; 456 | case 'n': 457 | numeric_progress = true; 458 | break; 459 | case 'p': 460 | // This is now the default. Keep parameter around since I wrote 461 | // some docs that include it. 462 | break; 463 | case 'q': 464 | quiet = true; 465 | break; 466 | case 'r': 467 | read_from_mmc = true; 468 | break; 469 | case 't': 470 | trim_mmc_device = true; 471 | break; 472 | case 'w': 473 | read_from_mmc = false; 474 | break; 475 | case 'y': 476 | accept_found_device = true; 477 | break; 478 | case 'v': 479 | print_version(); 480 | exit(EXIT_SUCCESS); 481 | break; 482 | default: /* '?' */ 483 | print_usage(argv[0]); 484 | exit(EXIT_FAILURE); 485 | } 486 | } 487 | 488 | if (quiet && numeric_progress) 489 | errx(EXIT_FAILURE, "pick either -n or -q, but not both."); 490 | 491 | if (optind < argc) 492 | data_pathname = argv[optind]; 493 | 494 | if (read_from_mmc && total_to_copy == 0) 495 | errx(EXIT_FAILURE, "Specify the amount to copy (-s) when reading from memory card."); 496 | 497 | if (read_from_mmc && trim_mmc_device) 498 | errx(EXIT_FAILURE, "You probably don't want to TRIM the device if you're going to read from it."); 499 | 500 | if (!mmc_device) { 501 | mmc_device = find_mmc_device(); 502 | 503 | if (!accept_found_device) { 504 | if (strcmp(data_pathname, "-") == 0) 505 | errx(EXIT_FAILURE, "Cannot confirm use of %s when using stdin/stdout.\nRerun with -y if location is correct.", mmc_device); 506 | 507 | char sizestr[16]; 508 | pretty_size(device_size(mmc_device), sizestr); 509 | fprintf(stderr, "Use %s memory card found at %s? [y/N] ", sizestr, mmc_device); 510 | int response = fgetc(stdin); 511 | if (response != 'y' && response != 'Y') 512 | errx(EXIT_FAILURE, "aborted"); 513 | } 514 | } 515 | 516 | int data_fd; 517 | if (strcmp(data_pathname, "-") != 0) { 518 | if (read_from_mmc) 519 | data_fd = open(data_pathname, O_WRONLY | O_CREAT | O_TRUNC, 0644); 520 | else 521 | data_fd = open(data_pathname, O_RDONLY); 522 | if (data_fd < 0) 523 | err(EXIT_FAILURE, "%s", data_pathname); 524 | 525 | // If writing to the MMC, cap the number of bytes to write to the file size. 526 | if (!read_from_mmc) { 527 | struct stat st; 528 | if (fstat(data_fd, &st)) 529 | err(EXIT_FAILURE, "fstat"); 530 | 531 | if (total_to_copy == 0 || 532 | st.st_size < total_to_copy) 533 | total_to_copy = st.st_size; 534 | } 535 | } else { 536 | // Reading from stdin or stdout. 537 | if (read_from_mmc) { 538 | data_fd = STDOUT_FILENO; 539 | 540 | // Force quiet to true so that progress reports don't stomp on 541 | // the data. 542 | quiet = true; 543 | } else 544 | data_fd = STDIN_FILENO; 545 | } 546 | 547 | if (numeric_progress && 548 | total_to_copy == 0) 549 | errx(EXIT_FAILURE, "Specify input size to report numeric progress"); 550 | 551 | // Unmount everything so that our read and writes to the device are 552 | // unaffected by file system caches or other concurrent activity. 553 | umount_all_on_dev(mmc_device); 554 | 555 | int mmc_fd = open(mmc_device, read_from_mmc ? O_RDONLY : (O_WRONLY | O_SYNC)); 556 | if (mmc_fd < 0) { 557 | if (errno == EROFS) 558 | errx(EXIT_FAILURE, "%s isn't writable. Check permissions or write-protect switch", mmc_device); 559 | else 560 | err(EXIT_FAILURE, "%s", mmc_device); 561 | } 562 | 563 | // Update the progress to 0% to give the user quick feedback 564 | report_progress(0, total_to_copy); 565 | 566 | if (trim_mmc_device) 567 | trim_mmc(mmc_fd); 568 | 569 | if (lseek(mmc_fd, seek_offset, SEEK_SET) == (off_t) -1) 570 | err(EXIT_FAILURE, "lseek"); 571 | 572 | if (read_from_mmc) 573 | copy(mmc_fd, data_fd, total_to_copy); 574 | else 575 | copy(data_fd, mmc_fd, total_to_copy); 576 | 577 | close(mmc_fd); 578 | if (data_fd != STDOUT_FILENO && data_fd != STDIN_FILENO) 579 | close(data_fd); 580 | 581 | exit(EXIT_SUCCESS); 582 | } 583 | --------------------------------------------------------------------------------