LCOV - code coverage report
Current view: top level - cmdline - snapraid.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 497 544 91.4 %
Date: 2017-11-06 22:14:04 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2011 Andrea Mazzoleni
       3             :  *
       4             :  * This program is free software: you can redistribute it and/or modify
       5             :  * it under the terms of the GNU General Public License as published by
       6             :  * the Free Software Foundation, either version 3 of the License, or
       7             :  * (at your option) any later version.
       8             :  *
       9             :  * This program is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :  * GNU General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public License
      15             :  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
      16             :  */
      17             : 
      18             : #include "portable.h"
      19             : 
      20             : #include "snapraid.h"
      21             : #include "support.h"
      22             : #include "elem.h"
      23             : #include "import.h"
      24             : #include "search.h"
      25             : #include "state.h"
      26             : #include "io.h"
      27             : #include "raid/raid.h"
      28             : 
      29             : /****************************************************************************/
      30             : /* main */
      31             : 
      32           2 : void version(void)
      33             : {
      34           2 :         msg_status(PACKAGE " v" VERSION " by Andrea Mazzoleni, " PACKAGE_URL "\n");
      35           2 : }
      36             : 
      37           1 : void usage(void)
      38             : {
      39           1 :         version();
      40             : 
      41           1 :         printf("Usage: " PACKAGE " status|diff|sync|scrub|list|dup|up|down|smart|pool|check|fix [options]\n");
      42           1 :         printf("\n");
      43           1 :         printf("Commands:\n");
      44           1 :         printf("  status Print the status of the array\n");
      45           1 :         printf("  diff   Show the changes that needs to be synchronized\n");
      46           1 :         printf("  sync   Synchronize the state of the array\n");
      47           1 :         printf("  scrub  Scrub the array\n");
      48           1 :         printf("  list   List the array content\n");
      49           1 :         printf("  dup    Find duplicate files\n");
      50           1 :         printf("  up     Spin-up the array\n");
      51           1 :         printf("  down   Spin-down the array\n");
      52           1 :         printf("  smart  SMART attributes of the array\n");
      53           1 :         printf("  pool   Create or update the virtual view of the array\n");
      54           1 :         printf("  check  Check the array\n");
      55           1 :         printf("  fix    Fix the array\n");
      56           1 :         printf("\n");
      57           1 :         printf("Options:\n");
      58           1 :         printf("  " SWITCH_GETOPT_LONG("-c, --conf FILE       ", "-c") "  Configuration file\n");
      59           1 :         printf("  " SWITCH_GETOPT_LONG("-f, --filter PATTERN  ", "-f") "  Process only files matching the pattern\n");
      60           1 :         printf("  " SWITCH_GETOPT_LONG("-d, --filter-disk NAME", "-f") "  Process only files in the specified disk\n");
      61           1 :         printf("  " SWITCH_GETOPT_LONG("-m, --filter-missing  ", "-m") "  Process only missing/deleted files\n");
      62           1 :         printf("  " SWITCH_GETOPT_LONG("-e, --filter-error    ", "-e") "  Process only files with errors\n");
      63           1 :         printf("  " SWITCH_GETOPT_LONG("-p, --plan PLAN       ", "-p") "  Define a scrub plan or percentage\n");
      64           1 :         printf("  " SWITCH_GETOPT_LONG("-o, --older-than DAYS ", "-o") "  Process only the older part of the array\n");
      65           1 :         printf("  " SWITCH_GETOPT_LONG("-i, --import DIR      ", "-i") "  Import deleted files\n");
      66           1 :         printf("  " SWITCH_GETOPT_LONG("-l, --log FILE        ", "-l") "  Log file. Default none\n");
      67           1 :         printf("  " SWITCH_GETOPT_LONG("-a, --audit-only      ", "-a") "  Check only file data and not parity\n");
      68           1 :         printf("  " SWITCH_GETOPT_LONG("-h, --pre-hash        ", "-h") "  Pre-hash all the new data\n");
      69           1 :         printf("  " SWITCH_GETOPT_LONG("-Z, --force-zero      ", "-Z") "  Force syncing of files that get zero size\n");
      70           1 :         printf("  " SWITCH_GETOPT_LONG("-E, --force-empty     ", "-E") "  Force syncing of disks that get empty\n");
      71           1 :         printf("  " SWITCH_GETOPT_LONG("-U, --force-uuid      ", "-U") "  Force commands on disks with uuid changed\n");
      72           1 :         printf("  " SWITCH_GETOPT_LONG("-D, --force-device    ", "-D") "  Force commands with inaccessible/shared disks\n");
      73           1 :         printf("  " SWITCH_GETOPT_LONG("-N, --force-nocopy    ", "-N") "  Force commands disabling the copy detection\n");
      74           1 :         printf("  " SWITCH_GETOPT_LONG("-F, --force-full      ", "-F") "  Force a full parity computation in sync\n");
      75           1 :         printf("  " SWITCH_GETOPT_LONG("-R, --force-realloc   ", "-R") "  Force a full parity reallocation in sync\n");
      76           1 :         printf("  " SWITCH_GETOPT_LONG("-v, --verbose         ", "-v") "  Verbose\n");
      77           1 : }
      78             : 
      79         238 : void memory(void)
      80             : {
      81         238 :         log_tag("memory:used:%" PRIu64 "\n", (uint64_t)malloc_counter_get());
      82             : 
      83             :         /* size of the block */
      84         238 :         log_tag("memory:block:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_block)));
      85         238 :         log_tag("memory:extent:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_extent)));
      86         238 :         log_tag("memory:file:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_file)));
      87         238 :         log_tag("memory:link:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_link)));
      88         238 :         log_tag("memory:dir:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_dir)));
      89             : 
      90         238 :         msg_progress("Using %u MiB of memory for the FileSystem.\n", (unsigned)(malloc_counter_get() / MEBI));
      91         238 : }
      92             : 
      93         277 : void test(int argc, char* argv[])
      94             : {
      95             :         int i;
      96             :         char buffer[ESC_MAX];
      97             : 
      98             :         /* special testing code for quoting */
      99         277 :         if (argc < 2 || strcmp(argv[1], "test") != 0)
     100         552 :                 return;
     101             : 
     102           1 :         for (i = 2; i < argc; ++i) {
     103           0 :                 printf("argv[%d]\n", i);
     104           0 :                 printf("\t#%s#\n", argv[i]);
     105           0 :                 printf("\t#%s#\n", esc_shell(argv[i], buffer));
     106             :         }
     107             : 
     108             : #ifdef _WIN32
     109             :         assert(strcmp(esc_shell(" ", buffer), "\" \"") == 0);
     110             :         assert(strcmp(esc_shell(" \" ", buffer), "\" \"\\\"\" \"") == 0);
     111             :         assert(strcmp(esc_shell("&|()<>^", buffer), "^&^|^(^)^<^>^^") == 0);
     112             :         assert(strcmp(esc_shell("&|()<>^ ", buffer), "\"&|()<>^ \"") == 0);
     113             : #else
     114           1 :         assert(strcmp(esc_shell(",._+:@%%/-", buffer), ",._+:@%%/-") == 0);
     115           1 :         assert(strcmp(esc_shell(" ", buffer), "\\ ") == 0);
     116             : #endif
     117             : 
     118           1 :         printf("Everything OK\n");
     119             : 
     120           1 :         exit(EXIT_SUCCESS);
     121             : }
     122             : 
     123             : /****************************************************************************/
     124             : /* log */
     125             : 
     126         266 : void log_open(const char* file)
     127             : {
     128             :         char path[PATH_MAX];
     129             :         const char* mode;
     130             :         char text_T[32];
     131             :         char text_D[32];
     132             :         time_t t;
     133             :         struct tm* tm;
     134             : 
     135             :         /* leave stdlog at 0 if not specified */
     136         266 :         if (file == 0)
     137         378 :                 return;
     138             : 
     139          80 :         t = time(0);
     140          80 :         tm = localtime(&t);
     141          80 :         if (tm) {
     142          80 :                 strftime(text_T, sizeof(text_T), "%H%M%S", tm);
     143          80 :                 strftime(text_D, sizeof(text_T), "%Y%m%d", tm);
     144             :         } else {
     145             :                 /* LCOV_EXCL_START */
     146             :                 strcpy(text_T, "invalid");
     147             :                 strcpy(text_D, "invalid");
     148             :                 /* LCOV_EXCL_STOP */
     149             :         }
     150             : 
     151             :         /* file mode */
     152          80 :         mode = "wt";
     153          80 :         if (*file == '>') {
     154           7 :                 ++file;
     155             : 
     156           7 :                 if (*file == '>') {
     157           1 :                         mode = "at";
     158           1 :                         ++file;
     159             :                 }
     160             : 
     161           7 :                 if (file[0] == '&' && file[1] == '1') {
     162           5 :                         stdlog = stdout;
     163           5 :                         return;
     164             :                 }
     165             : 
     166           2 :                 if (file[0] == '&' && file[1] == '2') {
     167           1 :                         stdlog = stderr;
     168           1 :                         return;
     169             :                 }
     170             :         }
     171             : 
     172             :         /* process the path */
     173         938 :         for (*path = 0; *file != 0; ) {
     174         790 :                 switch (*file) {
     175             :                 case '%' :
     176           2 :                         ++file;
     177           2 :                         switch (*file) {
     178             :                         case '%' :
     179           0 :                                 pathcatc(path, sizeof(path), '%');
     180           0 :                                 break;
     181             :                         case 'T' :
     182           1 :                                 pathcat(path, sizeof(path), text_T);
     183           1 :                                 break;
     184             :                         case 'D' :
     185           1 :                                 pathcat(path, sizeof(path), text_D);
     186           1 :                                 break;
     187             :                         default :
     188             :                                 /* LCOV_EXCL_START */
     189             :                                 log_fatal("Invalid type specifier '%c' in the log file.\n", *file);
     190             :                                 exit(EXIT_FAILURE);
     191             :                                 /* LCOV_EXCL_STOP */
     192             :                         }
     193           2 :                         break;
     194             :                 default :
     195         788 :                         pathcatc(path, sizeof(path), *file);
     196         788 :                         break;
     197             :                 }
     198         790 :                 ++file;
     199             :         }
     200             : 
     201          74 :         stdlog = fopen(path, mode);
     202          74 :         if (!stdlog) {
     203             :                 /* LCOV_EXCL_START */
     204             :                 log_fatal("Error opening the log file '%s'. %s.\n", path, strerror(errno));
     205             :                 exit(EXIT_FAILURE);
     206             :                 /* LCOV_EXCL_STOP */
     207             :         }
     208             : }
     209             : 
     210         243 : void log_close(const char* file)
     211             : {
     212         243 :         if (stdlog != stdout && stdlog != stderr && stdlog != 0) {
     213          74 :                 if (fclose(stdlog) != 0) {
     214             :                         /* LCOV_EXCL_START */
     215             :                         log_fatal("Error closing the log file '%s'. %s.\n", file, strerror(errno));
     216             :                         exit(EXIT_FAILURE);
     217             :                         /* LCOV_EXCL_STOP */
     218             :                 }
     219             :         }
     220             : 
     221         243 :         stdlog = 0;
     222         243 : }
     223             : 
     224             : /****************************************************************************/
     225             : /* config */
     226             : 
     227         276 : void config(char* conf, size_t conf_size, const char* argv0)
     228             : {
     229             : #ifdef _WIN32
     230             :         char* slash;
     231             : 
     232             :         pathimport(conf, conf_size, argv0);
     233             : 
     234             :         slash = strrchr(conf, '/');
     235             :         if (slash) {
     236             :                 slash[1] = 0;
     237             :                 pathcat(conf, conf_size, PACKAGE ".conf");
     238             :         } else {
     239             :                 pathcpy(conf, conf_size, PACKAGE ".conf");
     240             :         }
     241             : #else
     242             :         (void)argv0;
     243             : 
     244             : #ifdef SYSCONFDIR
     245             :         /* if it exists, give precedence at sysconfdir, usually /usr/local/etc */
     246         276 :         if (access(SYSCONFDIR "/" PACKAGE ".conf", F_OK) == 0)
     247           0 :                 pathcpy(conf, conf_size, SYSCONFDIR "/" PACKAGE ".conf");
     248             :         else /* otherwise fallback to plain /etc */
     249             : #endif
     250         276 :                 pathcpy(conf, conf_size, "/etc/" PACKAGE ".conf");
     251             : #endif
     252         276 : }
     253             : 
     254             : /****************************************************************************/
     255             : /* main */
     256             : 
     257             : #define OPT_TEST_SKIP_SELF 256
     258             : #define OPT_TEST_KILL_AFTER_SYNC 257
     259             : #define OPT_TEST_EXPECT_UNRECOVERABLE 258
     260             : #define OPT_TEST_EXPECT_RECOVERABLE 259
     261             : #define OPT_TEST_SKIP_SIGN 260
     262             : #define OPT_TEST_SKIP_FALLOCATE 261
     263             : #define OPT_TEST_SKIP_DEVICE 262
     264             : #define OPT_TEST_FORCE_MURMUR3 264
     265             : #define OPT_TEST_FORCE_SPOOKY2 265
     266             : #define OPT_TEST_SKIP_LOCK 266
     267             : #define OPT_TEST_FORCE_ORDER_PHYSICAL 267
     268             : #define OPT_TEST_FORCE_ORDER_INODE 268
     269             : #define OPT_TEST_FORCE_ORDER_ALPHA 269
     270             : #define OPT_TEST_FORCE_ORDER_DIR 270
     271             : #define OPT_TEST_FORCE_SCRUB_AT 271
     272             : #define OPT_TEST_FORCE_SCRUB_EVEN 272
     273             : #define OPT_TEST_FORCE_CONTENT_WRITE 273
     274             : #define OPT_TEST_SKIP_CONTENT_CHECK 275
     275             : #define OPT_TEST_SKIP_PARITY_ACCESS 276
     276             : #define OPT_TEST_EXPECT_FAILURE 277
     277             : #define OPT_TEST_RUN 278
     278             : #define OPT_TEST_FORCE_SCAN_WINFIND 279
     279             : #define OPT_TEST_IMPORT_CONTENT 280
     280             : #define OPT_TEST_FORCE_PROGRESS 281
     281             : #define OPT_TEST_SKIP_DISK_ACCESS 282
     282             : #define OPT_TEST_FORCE_AUTOSAVE_AT 283
     283             : #define OPT_TEST_FAKE_DEVICE 284
     284             : #define OPT_TEST_EXPECT_NEED_SYNC 285
     285             : #define OPT_NO_WARNINGS 286
     286             : #define OPT_TEST_FAKE_UUID 287
     287             : #define OPT_TEST_MATCH_FIRST_UUID 288
     288             : #define OPT_TEST_FORCE_PARITY_UPDATE 289
     289             : #define OPT_TEST_IO_CACHE 290
     290             : #define OPT_TEST_IO_STATS 291
     291             : #define OPT_TEST_COND_SIGNAL_OUTSIDE 292
     292             : #define OPT_TEST_IO_ADVISE_NONE 293
     293             : #define OPT_TEST_IO_ADVISE_SEQUENTIAL 294
     294             : #define OPT_TEST_IO_ADVISE_FLUSH 295
     295             : #define OPT_TEST_IO_ADVISE_FLUSH_WINDOW 296
     296             : #define OPT_TEST_IO_ADVISE_DISCARD 297
     297             : #define OPT_TEST_IO_ADVISE_DISCARD_WINDOW 298
     298             : #define OPT_TEST_IO_ADVISE_DIRECT 299
     299             : #define OPT_TEST_PARITY_LIMIT 301
     300             : #define OPT_TEST_SKIP_CONTENT_WRITE 302
     301             : #define OPT_TEST_SKIP_SPACE_HOLDER 303
     302             : #define OPT_TEST_FORMAT 304
     303             : 
     304             : #if HAVE_GETOPT_LONG
     305             : struct option long_options[] = {
     306             :         { "conf", 1, 0, 'c' },
     307             :         { "filter", 1, 0, 'f' },
     308             :         { "filter-disk", 1, 0, 'd' },
     309             :         { "filter-missing", 0, 0, 'm' },
     310             :         { "filter-error", 0, 0, 'e' },
     311             :         { "percentage", 1, 0, 'p' }, /* legacy name for --plan */
     312             :         { "plan", 1, 0, 'p' },
     313             :         { "older-than", 1, 0, 'o' },
     314             :         { "start", 1, 0, 'S' },
     315             :         { "count", 1, 0, 'B' },
     316             :         { "error-limit", 1, 0, 'L' },
     317             :         { "import", 1, 0, 'i' },
     318             :         { "log", 1, 0, 'l' },
     319             :         { "force-zero", 0, 0, 'Z' },
     320             :         { "force-empty", 0, 0, 'E' },
     321             :         { "force-uuid", 0, 0, 'U' },
     322             :         { "force-device", 0, 0, 'D' },
     323             :         { "force-nocopy", 0, 0, 'N' },
     324             :         { "force-full", 0, 0, 'F' },
     325             :         { "force-realloc", 0, 0, 'R' },
     326             :         { "audit-only", 0, 0, 'a' },
     327             :         { "pre-hash", 0, 0, 'h' },
     328             :         { "speed-test", 0, 0, 'T' }, /* undocumented speed test command */
     329             :         { "gen-conf", 1, 0, 'C' },
     330             :         { "verbose", 0, 0, 'v' },
     331             :         { "quiet", 0, 0, 'q' }, /* undocumented quiet option */
     332             :         { "gui", 0, 0, 'G' }, /* undocumented GUI interface option */
     333             :         { "help", 0, 0, 'H' },
     334             :         { "version", 0, 0, 'V' },
     335             : 
     336             :         /* The following are test specific options, DO NOT USE! */
     337             : 
     338             :         /* After syncing, do not write the new content file */
     339             :         { "test-kill-after-sync", 0, 0, OPT_TEST_KILL_AFTER_SYNC },
     340             : 
     341             :         /* Exit with failure if after check/fix there ARE NOT unrecoverable errors. */
     342             :         { "test-expect-unrecoverable", 0, 0, OPT_TEST_EXPECT_UNRECOVERABLE },
     343             : 
     344             :         /* Exit with failure if after check/fix there ARE NOT recoverable errors. */
     345             :         { "test-expect-recoverable", 0, 0, OPT_TEST_EXPECT_RECOVERABLE },
     346             : 
     347             :         /* Skip the initial self test */
     348             :         { "test-skip-self", 0, 0, OPT_TEST_SKIP_SELF },
     349             : 
     350             :         /* Skip the initial sign check when reading the content file */
     351             :         { "test-skip-sign", 0, 0, OPT_TEST_SKIP_SIGN },
     352             : 
     353             :         /* Skip the fallocate() when growing the parity files */
     354             :         { "test-skip-fallocate", 0, 0, OPT_TEST_SKIP_FALLOCATE },
     355             : 
     356             :         /* Skip the device check */
     357             :         { "test-skip-device", 0, 0, OPT_TEST_SKIP_DEVICE },
     358             : 
     359             :         /* Force Murmur3 hash */
     360             :         { "test-force-murmur3", 0, 0, OPT_TEST_FORCE_MURMUR3 },
     361             : 
     362             :         /* Force Spooky2 hash */
     363             :         { "test-force-spooky2", 0, 0, OPT_TEST_FORCE_SPOOKY2 },
     364             : 
     365             :         /* Skip the use of lock file */
     366             :         { "test-skip-lock", 0, 0, OPT_TEST_SKIP_LOCK },
     367             : 
     368             :         /* Force a sort order for files */
     369             :         { "test-force-order-physical", 0, 0, OPT_TEST_FORCE_ORDER_PHYSICAL },
     370             :         { "test-force-order-inode", 0, 0, OPT_TEST_FORCE_ORDER_INODE },
     371             :         { "test-force-order-alpha", 0, 0, OPT_TEST_FORCE_ORDER_ALPHA },
     372             :         { "test-force-order-dir", 0, 0, OPT_TEST_FORCE_ORDER_DIR },
     373             : 
     374             :         /* Force scrub of the specified number of blocks */
     375             :         { "test-force-scrub-at", 1, 0, OPT_TEST_FORCE_SCRUB_AT },
     376             : 
     377             :         /* Force scrub of all the even blocks. This is really for testing, don't try it */
     378             :         { "test-force-scrub-even", 0, 0, OPT_TEST_FORCE_SCRUB_EVEN },
     379             : 
     380             :         /* Force write of the content file even if no modification is done */
     381             :         { "test-force-content-write", 0, 0, OPT_TEST_FORCE_CONTENT_WRITE },
     382             : 
     383             :         /* Relax the checks done at the content file */
     384             :         { "test-skip-content-check", 0, 0, OPT_TEST_SKIP_CONTENT_CHECK },
     385             : 
     386             :         /* Skip the parity access */
     387             :         { "test-skip-parity-access", 0, 0, OPT_TEST_SKIP_PARITY_ACCESS },
     388             : 
     389             :         /* Exit generic failure */
     390             :         { "test-expect-failure", 0, 0, OPT_TEST_EXPECT_FAILURE },
     391             : 
     392             :         /* Exit generic need sync */
     393             :         { "test-expect-need-sync", 0, 0, OPT_TEST_EXPECT_NEED_SYNC },
     394             : 
     395             :         /* Run some command after loading the state and before the command */
     396             :         { "test-run", 1, 0, OPT_TEST_RUN },
     397             : 
     398             :         /* Use the FindFirst/Next approach in Windows to list files */
     399             :         { "test-force-scan-winfind", 0, 0, OPT_TEST_FORCE_SCAN_WINFIND },
     400             : 
     401             :         /* Alternative import working by data */
     402             :         { "test-import-content", 1, 0, OPT_TEST_IMPORT_CONTENT },
     403             : 
     404             :         /* Force immediate progress state update */
     405             :         { "test-force-progress", 0, 0, OPT_TEST_FORCE_PROGRESS },
     406             : 
     407             :         /* Skip the disk access */
     408             :         { "test-skip-disk-access", 0, 0, OPT_TEST_SKIP_DISK_ACCESS },
     409             : 
     410             :         /* Force autosave at the specified block */
     411             :         { "test-force-autosave-at", 1, 0, OPT_TEST_FORCE_AUTOSAVE_AT },
     412             : 
     413             :         /* Fake device data */
     414             :         { "test-fake-device", 0, 0, OPT_TEST_FAKE_DEVICE },
     415             : 
     416             :         /* Disable annoying warnings */
     417             :         { "no-warnings", 0, 0, OPT_NO_WARNINGS },
     418             : 
     419             :         /* Fake UUID */
     420             :         { "test-fake-uuid", 0, 0, OPT_TEST_FAKE_UUID },
     421             : 
     422             :         /* Match first UUID */
     423             :         { "test-match-first-uuid", 0, 0, OPT_TEST_MATCH_FIRST_UUID },
     424             : 
     425             :         /* Force parity update even if all the data hash is already matching */
     426             :         { "test-force-parity-update", 0, 0, OPT_TEST_FORCE_PARITY_UPDATE },
     427             : 
     428             :         /* Number of IO buffers */
     429             :         { "test-io-cache", 1, 0, OPT_TEST_IO_CACHE },
     430             : 
     431             :         /* Print IO stats */
     432             :         { "test-io-stats", 0, 0, OPT_TEST_IO_STATS },
     433             : 
     434             :         /* Signal condition variable outside the mutex */
     435             :         { "test-cond-signal-outside", 0, 0, OPT_TEST_COND_SIGNAL_OUTSIDE },
     436             : 
     437             :         /* Set the io advise to none */
     438             :         { "test-io-advise-none", 0, 0, OPT_TEST_IO_ADVISE_NONE },
     439             : 
     440             :         /* Set the io advise to sequential */
     441             :         { "test-io-advise-sequential", 0, 0, OPT_TEST_IO_ADVISE_SEQUENTIAL },
     442             : 
     443             :         /* Set the io advise to flush */
     444             :         { "test-io-advise-flush", 0, 0, OPT_TEST_IO_ADVISE_FLUSH },
     445             : 
     446             :         /* Set the io advise to flush window */
     447             :         { "test-io-advise-flush-window", 0, 0, OPT_TEST_IO_ADVISE_FLUSH_WINDOW },
     448             : 
     449             :         /* Set the io advise to discard */
     450             :         { "test-io-advise-discard", 0, 0, OPT_TEST_IO_ADVISE_DISCARD },
     451             : 
     452             :         /* Set the io advise to discard window */
     453             :         { "test-io-advise-discard-window", 0, 0, OPT_TEST_IO_ADVISE_DISCARD_WINDOW },
     454             : 
     455             :         /* Set the io advise to direct */
     456             :         { "test-io-advise-direct", 0, 0, OPT_TEST_IO_ADVISE_DIRECT },
     457             : 
     458             :         /* Set an artificial parity limit */
     459             :         { "test-parity-limit", 1, 0, OPT_TEST_PARITY_LIMIT },
     460             : 
     461             :         /* Skip content write */
     462             :         { "test-skip-content-write", 0, 0, OPT_TEST_SKIP_CONTENT_WRITE },
     463             : 
     464             :         /* Skip space holder file in parity disks */
     465             :         { "test-skip-space-holder", 0, 0, OPT_TEST_SKIP_SPACE_HOLDER },
     466             : 
     467             :         /* Set the output format */
     468             :         { "test-fmt", 1, 0, OPT_TEST_FORMAT },
     469             : 
     470             :         { 0, 0, 0, 0 }
     471             : };
     472             : #endif
     473             : 
     474             : #define OPTIONS "c:f:d:mep:o:S:B:L:i:l:ZEUDNFRahTC:vqHVG"
     475             : 
     476             : volatile int global_interrupt = 0;
     477             : 
     478             : /* LCOV_EXCL_START */
     479             : void signal_handler(int signum)
     480             : {
     481             :         (void)signum;
     482             : 
     483             :         /* report the request of interruption */
     484             :         global_interrupt = 1;
     485             : }
     486             : /* LCOV_EXCL_STOP */
     487             : 
     488         223 : void signal_init(void)
     489             : {
     490             : #if HAVE_SIGACTION
     491             :         struct sigaction sa;
     492             : 
     493         223 :         sa.sa_handler = signal_handler;
     494         223 :         sigemptyset(&sa.sa_mask);
     495             : 
     496             :         /* use the SA_RESTART to automatically restart interrupted system calls */
     497         223 :         sa.sa_flags = SA_RESTART;
     498             : 
     499         223 :         sigaction(SIGHUP, &sa, 0);
     500         223 :         sigaction(SIGTERM, &sa, 0);
     501         223 :         sigaction(SIGINT, &sa, 0);
     502         223 :         sigaction(SIGQUIT, &sa, 0);
     503             : #else
     504             :         signal(SIGINT, signal_handler);
     505             : #endif
     506         223 : }
     507             : 
     508             : #define OPERATION_DIFF 0
     509             : #define OPERATION_SYNC 1
     510             : #define OPERATION_CHECK 2
     511             : #define OPERATION_FIX 3
     512             : #define OPERATION_DRY 4
     513             : #define OPERATION_DUP 5
     514             : #define OPERATION_LIST 6
     515             : #define OPERATION_POOL 7
     516             : #define OPERATION_REHASH 8
     517             : #define OPERATION_SCRUB 9
     518             : #define OPERATION_STATUS 10
     519             : #define OPERATION_REWRITE 11
     520             : #define OPERATION_READ 12
     521             : #define OPERATION_TOUCH 13
     522             : #define OPERATION_SPINUP 14
     523             : #define OPERATION_SPINDOWN 15
     524             : #define OPERATION_DEVICES 16
     525             : #define OPERATION_SMART 17
     526             : 
     527         277 : int main(int argc, char* argv[])
     528             : {
     529             :         int c;
     530             :         struct snapraid_option opt;
     531             :         char conf[PATH_MAX];
     532             :         struct snapraid_state state;
     533             :         int operation;
     534             :         block_off_t blockstart;
     535             :         block_off_t blockcount;
     536             :         int ret;
     537             :         tommy_list filterlist_file;
     538             :         tommy_list filterlist_disk;
     539             :         int filter_missing;
     540             :         int filter_error;
     541             :         int plan;
     542             :         int olderthan;
     543             :         char* e;
     544             :         const char* command;
     545             :         const char* import_timestamp;
     546             :         const char* import_content;
     547             :         const char* log_file;
     548             :         int lock;
     549             :         const char* gen_conf;
     550             :         const char* run;
     551             :         int speedtest;
     552             :         int period;
     553             :         time_t t;
     554             :         struct tm* tm;
     555             :         int i;
     556             : 
     557         277 :         test(argc, argv);
     558             : 
     559         276 :         lock_init();
     560             : 
     561             :         /* defaults */
     562         276 :         config(conf, sizeof(conf), argv[0]);
     563         276 :         memset(&opt, 0, sizeof(opt));
     564         276 :         opt.io_error_limit = 100;
     565         276 :         blockstart = 0;
     566         276 :         blockcount = 0;
     567         276 :         tommy_list_init(&filterlist_file);
     568         276 :         tommy_list_init(&filterlist_disk);
     569         276 :         period = 1000;
     570         276 :         filter_missing = 0;
     571         276 :         filter_error = 0;
     572         276 :         plan = SCRUB_AUTO;
     573         276 :         olderthan = SCRUB_AUTO;
     574         276 :         import_timestamp = 0;
     575         276 :         import_content = 0;
     576         276 :         log_file = 0;
     577         276 :         lock = 0;
     578         276 :         gen_conf = 0;
     579         276 :         speedtest = 0;
     580         276 :         run = 0;
     581             : 
     582         276 :         opterr = 0;
     583        3475 :         while ((c =
     584             : #if HAVE_GETOPT_LONG
     585             :                 getopt_long(argc, argv, OPTIONS, long_options, 0))
     586             : #else
     587             :                 getopt(argc, argv, OPTIONS))
     588             : #endif
     589             :                 != EOF) {
     590        2928 :                 switch (c) {
     591             :                 case 'c' :
     592         269 :                         pathimport(conf, sizeof(conf), optarg);
     593         269 :                         break;
     594             :                 case 'f' : {
     595           6 :                         struct snapraid_filter* filter = filter_alloc_file(1, optarg);
     596           6 :                         if (!filter) {
     597             :                                 /* LCOV_EXCL_START */
     598             :                                 log_fatal("Invalid filter specification '%s'\n", optarg);
     599             :                                 log_fatal("Filters using relative paths are not supported. Ensure to add an initial slash\n");
     600             :                                 exit(EXIT_FAILURE);
     601             :                                 /* LCOV_EXCL_STOP */
     602             :                         }
     603           3 :                         tommy_list_insert_tail(&filterlist_file, &filter->node, filter);
     604           3 :                 } break;
     605             :                 case 'd' : {
     606          10 :                         struct snapraid_filter* filter = filter_alloc_disk(1, optarg);
     607          10 :                         if (!filter) {
     608             :                                 /* LCOV_EXCL_START */
     609             :                                 log_fatal("Invalid filter specification '%s'\n", optarg);
     610             :                                 exit(EXIT_FAILURE);
     611             :                                 /* LCOV_EXCL_STOP */
     612             :                         }
     613          10 :                         tommy_list_insert_tail(&filterlist_disk, &filter->node, filter);
     614          10 :                 } break;
     615             :                 case 'm' :
     616           2 :                         filter_missing = 1;
     617           2 :                         opt.expected_missing = 1;
     618           2 :                         break;
     619             :                 case 'e' :
     620             :                         /* when processing only error, we filter both files and blocks */
     621             :                         /* and we apply fixes only to synced ones */
     622           2 :                         filter_error = 1;
     623           2 :                         opt.badonly = 1;
     624           2 :                         opt.syncedonly = 1;
     625           2 :                         break;
     626             :                 case 'p' :
     627           5 :                         if (strcmp(optarg, "bad") == 0) {
     628           1 :                                 plan = SCRUB_BAD;
     629           4 :                         } else if (strcmp(optarg, "new") == 0) {
     630           1 :                                 plan = SCRUB_NEW;
     631           3 :                         } else if (strcmp(optarg, "full") == 0) {
     632           2 :                                 plan = SCRUB_FULL;
     633             :                         } else {
     634           1 :                                 plan = strtoul(optarg, &e, 10);
     635           1 :                                 if (!e || *e || plan > 100) {
     636             :                                         /* LCOV_EXCL_START */
     637             :                                         log_fatal("Invalid plan/percentage '%s'\n", optarg);
     638             :                                         exit(EXIT_FAILURE);
     639             :                                         /* LCOV_EXCL_STOP */
     640             :                                 }
     641             :                         }
     642           5 :                         break;
     643             :                 case 'o' :
     644           1 :                         olderthan = strtoul(optarg, &e, 10);
     645           1 :                         if (!e || *e || olderthan > 1000) {
     646             :                                 /* LCOV_EXCL_START */
     647             :                                 log_fatal("Invalid number of days '%s'\n", optarg);
     648             :                                 exit(EXIT_FAILURE);
     649             :                                 /* LCOV_EXCL_STOP */
     650             :                         }
     651           1 :                         break;
     652             :                 case 'S' :
     653           2 :                         blockstart = strtoul(optarg, &e, 0);
     654           2 :                         if (!e || *e) {
     655             :                                 /* LCOV_EXCL_START */
     656             :                                 log_fatal("Invalid start position '%s'\n", optarg);
     657             :                                 exit(EXIT_FAILURE);
     658             :                                 /* LCOV_EXCL_STOP */
     659             :                         }
     660           2 :                         break;
     661             :                 case 'B' :
     662           7 :                         blockcount = strtoul(optarg, &e, 0);
     663           7 :                         if (!e || *e) {
     664             :                                 /* LCOV_EXCL_START */
     665             :                                 log_fatal("Invalid count number '%s'\n", optarg);
     666             :                                 exit(EXIT_FAILURE);
     667             :                                 /* LCOV_EXCL_STOP */
     668             :                         }
     669           7 :                         break;
     670             :                 case 'L' :
     671           0 :                         opt.io_error_limit = strtoul(optarg, &e, 0);
     672           0 :                         if (!e || *e) {
     673             :                                 /* LCOV_EXCL_START */
     674             :                                 log_fatal("Invalid error limit number '%s'\n", optarg);
     675             :                                 exit(EXIT_FAILURE);
     676             :                                 /* LCOV_EXCL_STOP */
     677             :                         }
     678           0 :                         break;
     679             :                 case 'i' :
     680           1 :                         if (import_timestamp) {
     681             :                                 /* LCOV_EXCL_START */
     682             :                                 log_fatal("Import directory '%s' already specified as '%s'\n", optarg, import_timestamp);
     683             :                                 exit(EXIT_FAILURE);
     684             :                                 /* LCOV_EXCL_STOP */
     685             :                         }
     686           1 :                         import_timestamp = optarg;
     687           1 :                         break;
     688             :                 case OPT_TEST_IMPORT_CONTENT :
     689           1 :                         if (import_content) {
     690             :                                 /* LCOV_EXCL_START */
     691             :                                 log_fatal("Import directory '%s' already specified as '%s'\n", optarg, import_content);
     692             :                                 exit(EXIT_FAILURE);
     693             :                                 /* LCOV_EXCL_STOP */
     694             :                         }
     695           1 :                         import_content = optarg;
     696           1 :                         break;
     697             :                 case 'l' :
     698          80 :                         if (log_file) {
     699             :                                 /* LCOV_EXCL_START */
     700             :                                 log_fatal("Log file '%s' already specified as '%s'\n", optarg, log_file);
     701             :                                 exit(EXIT_FAILURE);
     702             :                                 /* LCOV_EXCL_STOP */
     703             :                         }
     704          80 :                         log_file = optarg;
     705          80 :                         break;
     706             :                 case 'Z' :
     707           0 :                         opt.force_zero = 1;
     708           0 :                         break;
     709             :                 case 'E' :
     710           6 :                         opt.force_empty = 1;
     711           6 :                         break;
     712             :                 case 'U' :
     713           0 :                         opt.force_uuid = 1;
     714           0 :                         break;
     715             :                 case 'D' :
     716           1 :                         opt.force_device = 1;
     717           1 :                         break;
     718             :                 case 'N' :
     719           1 :                         opt.force_nocopy = 1;
     720           1 :                         break;
     721             :                 case 'F' :
     722           6 :                         opt.force_full = 1;
     723           6 :                         break;
     724             :                 case 'R' :
     725           1 :                         opt.force_realloc = 1;
     726           1 :                         break;
     727             :                 case 'a' :
     728           6 :                         opt.auditonly = 1;
     729           6 :                         break;
     730             :                 case 'h' :
     731           7 :                         opt.prehash = 1;
     732           7 :                         break;
     733             :                 case 'v' :
     734          26 :                         ++msg_level;
     735          26 :                         break;
     736             :                 case 'q' :
     737         729 :                         --msg_level;
     738         729 :                         break;
     739             :                 case 'G' :
     740          26 :                         opt.gui = 1;
     741          26 :                         break;
     742             :                 case 'H' :
     743           1 :                         usage();
     744           1 :                         exit(EXIT_SUCCESS);
     745             :                 case 'V' :
     746           1 :                         version();
     747           1 :                         exit(EXIT_SUCCESS);
     748             :                 case 'T' :
     749           4 :                         speedtest = 1;
     750           4 :                         break;
     751             :                 case 'C' :
     752           1 :                         gen_conf = optarg;
     753           1 :                         break;
     754             :                 case OPT_TEST_KILL_AFTER_SYNC :
     755          10 :                         opt.kill_after_sync = 1;
     756          10 :                         break;
     757             :                 case OPT_TEST_EXPECT_UNRECOVERABLE :
     758          13 :                         opt.expect_unrecoverable = 1;
     759          13 :                         break;
     760             :                 case OPT_TEST_EXPECT_RECOVERABLE :
     761          23 :                         opt.expect_recoverable = 1;
     762          23 :                         break;
     763             :                 case OPT_TEST_SKIP_SELF :
     764         269 :                         opt.skip_self = 1;
     765         269 :                         break;
     766             :                 case OPT_TEST_SKIP_SIGN :
     767           0 :                         opt.skip_sign = 1;
     768           0 :                         break;
     769             :                 case OPT_TEST_SKIP_FALLOCATE :
     770           1 :                         opt.skip_fallocate = 1;
     771           1 :                         break;
     772             :                 case OPT_TEST_SKIP_DEVICE :
     773         276 :                         opt.skip_device = 1;
     774         276 :                         period = 50; /* reduce period of the speed test */
     775         276 :                         break;
     776             :                 case OPT_TEST_SKIP_CONTENT_CHECK :
     777           0 :                         opt.skip_content_check = 1;
     778           0 :                         break;
     779             :                 case OPT_TEST_SKIP_PARITY_ACCESS :
     780           0 :                         opt.skip_parity_access = 1;
     781           0 :                         break;
     782             :                 case OPT_TEST_SKIP_DISK_ACCESS :
     783           0 :                         opt.skip_disk_access = 1;
     784           0 :                         break;
     785             :                 case OPT_TEST_FORCE_MURMUR3 :
     786           1 :                         opt.force_murmur3 = 1;
     787           1 :                         break;
     788             :                 case OPT_TEST_FORCE_SPOOKY2 :
     789           1 :                         opt.force_spooky2 = 1;
     790           1 :                         break;
     791             :                 case OPT_TEST_SKIP_LOCK :
     792           0 :                         opt.skip_lock = 1;
     793           0 :                         break;
     794             :                 case OPT_TEST_FORCE_ORDER_PHYSICAL :
     795           2 :                         opt.force_order = SORT_PHYSICAL;
     796           2 :                         break;
     797             :                 case OPT_TEST_FORCE_ORDER_INODE :
     798           0 :                         opt.force_order = SORT_INODE;
     799           0 :                         break;
     800             :                 case OPT_TEST_FORCE_ORDER_ALPHA :
     801         267 :                         opt.force_order = SORT_ALPHA;
     802         267 :                         break;
     803             :                 case OPT_TEST_FORCE_ORDER_DIR :
     804           0 :                         opt.force_order = SORT_DIR;
     805           0 :                         break;
     806             :                 case OPT_TEST_FORCE_SCRUB_AT :
     807           4 :                         opt.force_scrub_at = atoi(optarg);
     808           4 :                         break;
     809             :                 case OPT_TEST_FORCE_SCRUB_EVEN :
     810           1 :                         opt.force_scrub_even = 1;
     811           1 :                         break;
     812             :                 case OPT_TEST_FORCE_CONTENT_WRITE :
     813           0 :                         opt.force_content_write = 1;
     814           0 :                         break;
     815             :                 case OPT_TEST_EXPECT_FAILURE :
     816             :                         /* invert the exit codes */
     817          19 :                         exit_success = 1;
     818          19 :                         exit_failure = 0;
     819          19 :                         break;
     820             :                 case OPT_TEST_EXPECT_NEED_SYNC :
     821             :                         /* invert the exit codes */
     822           7 :                         exit_success = 1;
     823           7 :                         exit_sync_needed = 0;
     824           7 :                         break;
     825             :                 case OPT_TEST_RUN :
     826          10 :                         run = optarg;
     827          10 :                         break;
     828             :                 case OPT_TEST_FORCE_SCAN_WINFIND :
     829           0 :                         opt.force_scan_winfind = 1;
     830           0 :                         break;
     831             :                 case OPT_TEST_FORCE_PROGRESS :
     832         269 :                         opt.force_progress = 1;
     833         269 :                         break;
     834             :                 case OPT_TEST_FORCE_AUTOSAVE_AT :
     835           1 :                         opt.force_autosave_at = atoi(optarg);
     836           1 :                         break;
     837             :                 case OPT_TEST_FAKE_DEVICE :
     838           1 :                         opt.fake_device = 1;
     839           1 :                         break;
     840             :                 case OPT_NO_WARNINGS :
     841         270 :                         opt.no_warnings = 1;
     842         270 :                         break;
     843             :                 case OPT_TEST_FAKE_UUID :
     844           1 :                         opt.fake_uuid = 2;
     845           1 :                         break;
     846             :                 case OPT_TEST_MATCH_FIRST_UUID :
     847           2 :                         opt.match_first_uuid = 1;
     848           2 :                         break;
     849             :                 case OPT_TEST_FORCE_PARITY_UPDATE :
     850           0 :                         opt.force_parity_update = 1;
     851           0 :                         break;
     852             :                 case OPT_TEST_IO_CACHE :
     853           1 :                         opt.io_cache = atoi(optarg);
     854           1 :                         if (opt.io_cache != 1 && (opt.io_cache < IO_MIN || opt.io_cache > IO_MAX)) {
     855             :                                 /* LCOV_EXCL_START */
     856             :                                 log_fatal("The IO cache should be between %u and %u.\n", IO_MIN, IO_MAX);
     857             :                                 exit(EXIT_FAILURE);
     858             :                                 /* LCOV_EXCL_STOP */
     859             :                         }
     860           1 :                         break;
     861             :                 case OPT_TEST_IO_STATS :
     862           1 :                         opt.force_stats = 1;
     863           1 :                         break;
     864             :                 case OPT_TEST_COND_SIGNAL_OUTSIDE :
     865             : #if HAVE_PTHREAD
     866           0 :                         thread_cond_signal_outside = 1;
     867             : #endif
     868           0 :                         break;
     869             :                 case OPT_TEST_IO_ADVISE_NONE :
     870           1 :                         opt.file_mode = ADVISE_NONE;
     871           1 :                         break;
     872             :                 case OPT_TEST_IO_ADVISE_SEQUENTIAL :
     873           1 :                         opt.file_mode = ADVISE_SEQUENTIAL;
     874           1 :                         break;
     875             :                 case OPT_TEST_IO_ADVISE_FLUSH :
     876           0 :                         opt.file_mode = ADVISE_FLUSH;
     877           0 :                         break;
     878             :                 case OPT_TEST_IO_ADVISE_FLUSH_WINDOW :
     879           1 :                         opt.file_mode = ADVISE_FLUSH_WINDOW;
     880           1 :                         break;
     881             :                 case OPT_TEST_IO_ADVISE_DISCARD :
     882           0 :                         opt.file_mode = ADVISE_DISCARD;
     883           0 :                         break;
     884             :                 case OPT_TEST_IO_ADVISE_DISCARD_WINDOW :
     885           1 :                         opt.file_mode = ADVISE_DISCARD_WINDOW;
     886           1 :                         break;
     887             :                 case OPT_TEST_IO_ADVISE_DIRECT :
     888           0 :                         opt.file_mode = ADVISE_DIRECT;
     889           0 :                         break;
     890             :                 case OPT_TEST_PARITY_LIMIT :
     891         269 :                         opt.parity_limit_size = atoll(optarg);
     892         269 :                         break;
     893             :                 case OPT_TEST_SKIP_CONTENT_WRITE :
     894           0 :                         opt.skip_content_write = 1;
     895           0 :                         break;
     896             :                 case OPT_TEST_SKIP_SPACE_HOLDER :
     897           0 :                         opt.skip_space_holder = 1;
     898           0 :                         break;
     899             :                 case OPT_TEST_FORMAT :
     900           3 :                         if (strcmp(optarg, "file") == 0)
     901           1 :                                 FMT_MODE = FMT_FILE;
     902           2 :                         else if (strcmp(optarg, "disk") == 0)
     903           1 :                                 FMT_MODE = FMT_DISK;
     904           1 :                         else if (strcmp(optarg, "path") == 0)
     905           1 :                                 FMT_MODE = FMT_PATH;
     906             :                         else {
     907             :                                 /* LCOV_EXCL_START */
     908             :                                 log_fatal("Unknown format '%s'\n", optarg);
     909             :                                 exit(EXIT_FAILURE);
     910             :                                 /* LCOV_EXCL_STOP */
     911             :                         }
     912           3 :                         break;
     913             :                 default :
     914             :                         /* LCOV_EXCL_START */
     915             :                         log_fatal("Unknown option '%c'\n", (char)c);
     916             :                         exit(EXIT_FAILURE);
     917             :                         /* LCOV_EXCL_STOP */
     918             :                 }
     919             :         }
     920             : 
     921         271 :         os_init(opt.force_scan_winfind);
     922         271 :         raid_init();
     923         271 :         crc32c_init();
     924             : 
     925         271 :         if (speedtest != 0) {
     926           4 :                 speed(period);
     927           4 :                 os_done();
     928           4 :                 exit(EXIT_SUCCESS);
     929             :         }
     930             : 
     931         267 :         if (gen_conf != 0) {
     932           1 :                 generate_configuration(gen_conf);
     933           1 :                 os_done();
     934           1 :                 exit(EXIT_SUCCESS);
     935             :         }
     936             : 
     937         266 :         if (optind + 1 != argc) {
     938             :                 /* LCOV_EXCL_START */
     939             :                 usage();
     940             :                 exit(EXIT_FAILURE);
     941             :                 /* LCOV_EXCL_STOP */
     942             :         }
     943             : 
     944         266 :         command = argv[optind];
     945         266 :         if (strcmp(command, "diff") == 0) {
     946           8 :                 operation = OPERATION_DIFF;
     947         258 :         } else if (strcmp(argv[optind], "sync") == 0) {
     948          89 :                 operation = OPERATION_SYNC;
     949         169 :         } else if (strcmp(argv[optind], "check") == 0) {
     950          75 :                 operation = OPERATION_CHECK;
     951          94 :         } else if (strcmp(argv[optind], "fix") == 0) {
     952          46 :                 operation = OPERATION_FIX;
     953          48 :         } else if (strcmp(argv[optind], "test-dry") == 0) {
     954           2 :                 operation = OPERATION_DRY;
     955          46 :         } else if (strcmp(argv[optind], "dup") == 0) {
     956           3 :                 operation = OPERATION_DUP;
     957          43 :         } else if (strcmp(argv[optind], "list") == 0) {
     958           5 :                 operation = OPERATION_LIST;
     959          38 :         } else if (strcmp(argv[optind], "pool") == 0) {
     960           3 :                 operation = OPERATION_POOL;
     961          35 :         } else if (strcmp(argv[optind], "rehash") == 0) {
     962           1 :                 operation = OPERATION_REHASH;
     963          34 :         } else if (strcmp(argv[optind], "scrub") == 0) {
     964          11 :                 operation = OPERATION_SCRUB;
     965          23 :         } else if (strcmp(argv[optind], "status") == 0) {
     966          15 :                 operation = OPERATION_STATUS;
     967           8 :         } else if (strcmp(argv[optind], "test-rewrite") == 0) {
     968           1 :                 operation = OPERATION_REWRITE;
     969           7 :         } else if (strcmp(argv[optind], "test-read") == 0) {
     970           1 :                 operation = OPERATION_READ;
     971           6 :         } else if (strcmp(argv[optind], "touch") == 0) {
     972           1 :                 operation = OPERATION_TOUCH;
     973           5 :         } else if (strcmp(argv[optind], "up") == 0) {
     974           1 :                 operation = OPERATION_SPINUP;
     975           4 :         } else if (strcmp(argv[optind], "down") == 0) {
     976           1 :                 operation = OPERATION_SPINDOWN;
     977           3 :         } else if (strcmp(argv[optind], "devices") == 0) {
     978           1 :                 operation = OPERATION_DEVICES;
     979           2 :         } else if (strcmp(argv[optind], "smart") == 0) {
     980           2 :                 operation = OPERATION_SMART;
     981             :         } else {
     982             :                 /* LCOV_EXCL_START */
     983             :                 log_fatal("Unknown command '%s'\n", argv[optind]);
     984             :                 exit(EXIT_FAILURE);
     985             :                 /* LCOV_EXCL_STOP */
     986             :         }
     987             : 
     988             :         /* check options compatibility */
     989         266 :         switch (operation) {
     990             :         case OPERATION_CHECK :
     991          75 :                 break;
     992             :         default :
     993         191 :                 if (opt.auditonly) {
     994             :                         /* LCOV_EXCL_START */
     995             :                         log_fatal("You cannot use -a, --audit-only with the '%s' command\n", command);
     996             :                         exit(EXIT_FAILURE);
     997             :                         /* LCOV_EXCL_STOP */
     998             :                 }
     999             :         }
    1000             : 
    1001         266 :         switch (operation) {
    1002             :         case OPERATION_FIX :
    1003             :         case OPERATION_CHECK :
    1004         121 :                 break;
    1005             :         default :
    1006         145 :                 if (opt.force_device) {
    1007             :                         /* LCOV_EXCL_START */
    1008             :                         log_fatal("You cannot use -D, --force-device with the '%s' command\n", command);
    1009             :                         exit(EXIT_FAILURE);
    1010             :                         /* LCOV_EXCL_STOP */
    1011             :                 }
    1012             :         }
    1013             : 
    1014         266 :         switch (operation) {
    1015             :         case OPERATION_SYNC :
    1016             :         case OPERATION_CHECK :
    1017             :         case OPERATION_FIX :
    1018         210 :                 break;
    1019             :         default :
    1020          56 :                 if (opt.force_nocopy) {
    1021             :                         /* LCOV_EXCL_START */
    1022             :                         log_fatal("You cannot use -N, --force-nocopy with the '%s' command\n", command);
    1023             :                         exit(EXIT_FAILURE);
    1024             :                         /* LCOV_EXCL_STOP */
    1025             :                 }
    1026             :         }
    1027             : 
    1028         266 :         switch (operation) {
    1029             :         case OPERATION_SYNC :
    1030          89 :                 break;
    1031             :         default :
    1032         177 :                 if (opt.prehash) {
    1033             :                         /* LCOV_EXCL_START */
    1034             :                         log_fatal("You cannot use -h, --pre-hash with the '%s' command\n", command);
    1035             :                         exit(EXIT_FAILURE);
    1036             :                         /* LCOV_EXCL_STOP */
    1037             :                 }
    1038             : 
    1039         177 :                 if (opt.force_full) {
    1040             :                         /* LCOV_EXCL_START */
    1041             :                         log_fatal("You cannot use -F, --force-full with the '%s' command\n", command);
    1042             :                         exit(EXIT_FAILURE);
    1043             :                         /* LCOV_EXCL_STOP */
    1044             :                 }
    1045             : 
    1046         177 :                 if (opt.force_realloc) {
    1047             :                         /* LCOV_EXCL_START */
    1048             :                         log_fatal("You cannot use -R, --force-realloc with the '%s' command\n", command);
    1049             :                         exit(EXIT_FAILURE);
    1050             :                         /* LCOV_EXCL_STOP */
    1051             :                 }
    1052             :         }
    1053             : 
    1054         266 :         if (opt.force_full && opt.force_nocopy) {
    1055             :                 /* LCOV_EXCL_START */
    1056             :                 log_fatal("You cannot use the -F, --force-full and -N, --force-nocopy options at the same time\n");
    1057             :                 exit(EXIT_FAILURE);
    1058             :                 /* LCOV_EXCL_STOP */
    1059             :         }
    1060             : 
    1061         266 :         if (opt.force_realloc && opt.force_nocopy) {
    1062             :                 /* LCOV_EXCL_START */
    1063             :                 log_fatal("You cannot use the -R, --force-realloc and -N, --force-nocopy options at the same time\n");
    1064             :                 exit(EXIT_FAILURE);
    1065             :                 /* LCOV_EXCL_STOP */
    1066             :         }
    1067             : 
    1068         266 :         if (opt.force_realloc && opt.force_full) {
    1069             :                 /* LCOV_EXCL_START */
    1070             :                 log_fatal("You cannot use the -R, --force-realloc and -F, --force-full options at the same time\n");
    1071             :                 exit(EXIT_FAILURE);
    1072             :                 /* LCOV_EXCL_STOP */
    1073             :         }
    1074             : 
    1075         266 :         if (opt.prehash && opt.force_nocopy) {
    1076             :                 /* LCOV_EXCL_START */
    1077             :                 log_fatal("You cannot use the -h, --pre-hash and -N, --force-nocopy options at the same time\n");
    1078             :                 exit(EXIT_FAILURE);
    1079             :                 /* LCOV_EXCL_STOP */
    1080             :         }
    1081             : 
    1082         266 :         switch (operation) {
    1083             :         case OPERATION_CHECK :
    1084             :         case OPERATION_FIX :
    1085             :         case OPERATION_DRY :
    1086         123 :                 break;
    1087             :         default :
    1088         141 :                 if (!tommy_list_empty(&filterlist_disk)) {
    1089             :                         /* LCOV_EXCL_START */
    1090             :                         log_fatal("You cannot use -d, --filter-disk with the '%s' command\n", command);
    1091             :                         exit(EXIT_FAILURE);
    1092             :                         /* LCOV_EXCL_STOP */
    1093             :                 }
    1094             :                 /* follow */
    1095             :         case OPERATION_SPINUP :
    1096             :         case OPERATION_SPINDOWN :
    1097         143 :                 if (!tommy_list_empty(&filterlist_file)) {
    1098             :                         /* LCOV_EXCL_START */
    1099             :                         log_fatal("You cannot use -f, --filter with the '%s' command\n", command);
    1100             :                         exit(EXIT_FAILURE);
    1101             :                         /* LCOV_EXCL_STOP */
    1102             :                 }
    1103         143 :                 if (filter_missing != 0) {
    1104             :                         /* LCOV_EXCL_START */
    1105             :                         log_fatal("You cannot use -m, --filter-missing with the '%s' command\n", command);
    1106             :                         exit(EXIT_FAILURE);
    1107             :                         /* LCOV_EXCL_STOP */
    1108             :                 }
    1109         143 :                 if (filter_error != 0) {
    1110             :                         /* LCOV_EXCL_START */
    1111             :                         log_fatal("You cannot use -e, --filter-error with the '%s' command\n", command);
    1112             :                         exit(EXIT_FAILURE);
    1113             :                         /* LCOV_EXCL_STOP */
    1114             :                 }
    1115             :         }
    1116             : 
    1117             :         /* errors must be always fixed on all disks */
    1118             :         /* because we don't keep the information on what disk is the error */
    1119         266 :         if (filter_error != 0 && !tommy_list_empty(&filterlist_disk)) {
    1120             :                 /* LCOV_EXCL_START */
    1121             :                 log_fatal("You cannot use -e, --filter-error and -d, --filter-disk at the same time\n");
    1122             :                 exit(EXIT_FAILURE);
    1123             :                 /* LCOV_EXCL_STOP */
    1124             :         }
    1125             : 
    1126         266 :         switch (operation) {
    1127             :         case OPERATION_CHECK :
    1128             :         case OPERATION_FIX :
    1129         121 :                 break;
    1130             :         default :
    1131         145 :                 if (import_timestamp != 0 || import_content != 0) {
    1132             :                         /* LCOV_EXCL_START */
    1133             :                         log_fatal("You cannot import with the '%s' command\n", command);
    1134             :                         exit(EXIT_FAILURE);
    1135             :                         /* LCOV_EXCL_STOP */
    1136             :                 }
    1137             :         }
    1138             : 
    1139         266 :         switch (operation) {
    1140             :         case OPERATION_LIST :
    1141             :         case OPERATION_DUP :
    1142             :         case OPERATION_STATUS :
    1143             :         case OPERATION_REWRITE :
    1144             :         case OPERATION_READ :
    1145             :         case OPERATION_REHASH :
    1146             :         case OPERATION_SPINUP : /* we want to do it in different threads to avoid blocking */
    1147             :                 /* avoid to check and access data disks if not needed */
    1148          27 :                 opt.skip_disk_access = 1;
    1149          27 :                 break;
    1150             :         }
    1151             : 
    1152         266 :         switch (operation) {
    1153             :         case OPERATION_DIFF :
    1154             :         case OPERATION_LIST :
    1155             :         case OPERATION_DUP :
    1156             :         case OPERATION_POOL :
    1157             :         case OPERATION_STATUS :
    1158             :         case OPERATION_REWRITE :
    1159             :         case OPERATION_READ :
    1160             :         case OPERATION_REHASH :
    1161             :         case OPERATION_TOUCH :
    1162             :         case OPERATION_SPINUP : /* we want to do it in different threads to avoid blocking */
    1163             :                 /* avoid to check and access parity disks if not needed */
    1164          39 :                 opt.skip_parity_access = 1;
    1165          39 :                 break;
    1166             :         }
    1167             : 
    1168         266 :         switch (operation) {
    1169             :         case OPERATION_FIX :
    1170             :         case OPERATION_CHECK :
    1171             :                 /* avoid to stop processing if a content file is not accessible */
    1172         121 :                 opt.skip_content_access = 1;
    1173         121 :                 break;
    1174             :         }
    1175             : 
    1176         266 :         switch (operation) {
    1177             :         case OPERATION_DIFF :
    1178             :         case OPERATION_LIST :
    1179             :         case OPERATION_DUP :
    1180             :         case OPERATION_POOL :
    1181             :         case OPERATION_TOUCH :
    1182             :         case OPERATION_SPINUP :
    1183             :         case OPERATION_SPINDOWN :
    1184             :         case OPERATION_DEVICES :
    1185             :         case OPERATION_SMART :
    1186          25 :                 opt.skip_self = 1;
    1187          25 :                 break;
    1188             :         }
    1189             : 
    1190         266 :         switch (operation) {
    1191             : #if HAVE_DIRECT_IO
    1192             :         case OPERATION_SYNC :
    1193             :         case OPERATION_SCRUB :
    1194             :         case OPERATION_DRY :
    1195         102 :                 break;
    1196             : #endif
    1197             :         default:
    1198             :                 /* we allow direct IO only on some commands */
    1199         164 :                 if (opt.file_mode == ADVISE_DIRECT)
    1200           0 :                         opt.file_mode = ADVISE_SEQUENTIAL;
    1201         164 :                 break;
    1202             :         }
    1203             : 
    1204         266 :         switch (operation) {
    1205             :         case OPERATION_DEVICES :
    1206             :         case OPERATION_SMART :
    1207             :                 /* we may need to use these commands during operations */
    1208           3 :                 opt.skip_lock = 1;
    1209           3 :                 break;
    1210             :         }
    1211             : 
    1212         266 :         switch (operation) {
    1213             :         case OPERATION_SMART :
    1214             :                 /* allow to run without configuration file */
    1215           2 :                 opt.auto_conf = 1;
    1216           2 :                 break;
    1217             :         }
    1218             : 
    1219             :         /* open the log file */
    1220         266 :         log_open(log_file);
    1221             : 
    1222             :         /* print generic info into the log */
    1223         266 :         t = time(0);
    1224         266 :         tm = localtime(&t);
    1225         266 :         log_tag("version:%s\n", PACKAGE_VERSION);
    1226         266 :         log_tag("unixtime:%" PRIi64 "\n", (int64_t)t);
    1227         266 :         if (tm) {
    1228             :                 char datetime[64];
    1229         266 :                 strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S", tm);
    1230         266 :                 log_tag("time:%s\n", datetime);
    1231             :         }
    1232         266 :         log_tag("command:%s\n", command);
    1233        4066 :         for (i = 0; i < argc; ++i)
    1234        3800 :                 log_tag("argv:%u:%s\n", i, argv[i]);
    1235         266 :         log_flush();
    1236             : 
    1237         266 :         if (!opt.skip_self)
    1238           1 :                 selftest();
    1239             : 
    1240         266 :         state_init(&state);
    1241             : 
    1242             :         /* read the configuration file */
    1243         266 :         state_config(&state, conf, command, &opt, &filterlist_disk);
    1244             : 
    1245             :         /* set the raid mode */
    1246         265 :         raid_mode(state.raid_mode);
    1247             : 
    1248             : #if HAVE_LOCKFILE
    1249             :         /* create the lock file */
    1250         265 :         if (!opt.skip_lock && state.lockfile[0]) {
    1251         262 :                 lock = lock_lock(state.lockfile);
    1252         262 :                 if (lock == -1) {
    1253             :                         /* LCOV_EXCL_START */
    1254             :                         if (errno != EWOULDBLOCK) {
    1255             :                                 log_fatal("Error creating the lock file '%s'. %s.\n", state.lockfile, strerror(errno));
    1256             :                         } else {
    1257             :                                 log_fatal("The lock file '%s' is already locked!\n", state.lockfile);
    1258             :                                 log_fatal("SnapRAID is already in use!\n");
    1259             :                         }
    1260             :                         exit(EXIT_FAILURE);
    1261             :                         /* LCOV_EXCL_STOP */
    1262             :                 }
    1263             :         }
    1264             : #else
    1265             :         (void)lock;
    1266             : #endif
    1267             : 
    1268         265 :         if (operation == OPERATION_DIFF) {
    1269           8 :                 state_read(&state);
    1270             : 
    1271           8 :                 ret = state_diff(&state);
    1272             : 
    1273             :                 /* abort if sync needed */
    1274           8 :                 if (ret > 0)
    1275           7 :                         exit(EXIT_SYNC_NEEDED);
    1276         257 :         } else if (operation == OPERATION_SYNC) {
    1277             : 
    1278             :                 /* in the next state read ensures to clear all the past hashes in case */
    1279             :                 /* we are reading from an incomplete sync */
    1280             :                 /* The indeterminate hash are only for CHG/DELETED blocks for which we don't */
    1281             :                 /* know if the previous interrupted sync was able to update or not the parity. */
    1282             :                 /* The sync process instead needs to trust this information because it's used */
    1283             :                 /* to avoid to recompute the parity if all the input are equals as before. */
    1284             : 
    1285             :                 /* In these cases we don't know if the old state is still the one */
    1286             :                 /* stored inside the parity, because after an aborted sync, the parity */
    1287             :                 /* may be or may be not have been updated with the data that may be now */
    1288             :                 /* deleted. Then we reset the hash to a bogus value. */
    1289             : 
    1290             :                 /* An example for CHG blocks is: */
    1291             :                 /* - One file is added creating a CHG block with ZERO state */
    1292             :                 /* - Sync aborted after updating the parity to the new state, */
    1293             :                 /*   but without saving the content file representing this new BLK state. */
    1294             :                 /* - File is now deleted after the aborted sync */
    1295             :                 /* - Sync again, deleting the blocks over the CHG ones */
    1296             :                 /*   with the hash of CHG blocks not representing the real parity state */
    1297             : 
    1298             :                 /* An example for DELETED blocks is: */
    1299             :                 /* - One file is deleted creating DELETED blocks */
    1300             :                 /* - Sync aborted after, updating the parity to the new state, */
    1301             :                 /*   but without saving the content file representing this new EMPTY state. */
    1302             :                 /* - Another file is added again over the DELETE ones */
    1303             :                 /*   with the hash of DELETED blocks not representing the real parity state */
    1304          89 :                 state.clear_past_hash = 1;
    1305             : 
    1306          89 :                 state_read(&state);
    1307             : 
    1308          88 :                 state_scan(&state);
    1309             : 
    1310             :                 /* refresh the size info before the content write */
    1311          87 :                 state_refresh(&state);
    1312             : 
    1313          87 :                 memory();
    1314             : 
    1315             :                 /* intercept signals while operating */
    1316          87 :                 signal_init();
    1317             : 
    1318             :                 /* run a test command if required */
    1319          87 :                 if (run != 0) {
    1320          10 :                         ret = system(run); /* ignore error */
    1321          10 :                         if (ret != 0) {
    1322             :                                 /* LCOV_EXCL_START */
    1323             :                                 log_fatal("Error in running command '%s'.\n", run);
    1324             :                                 exit(EXIT_FAILURE);
    1325             :                                 /* LCOV_EXCL_STOP */
    1326             :                         }
    1327             :                 }
    1328             : 
    1329             :                 /* waits some time to ensure that any concurrent modification done at the files, */
    1330             :                 /* using the same mtime read by the scan process, will be read by sync. */
    1331             :                 /* Note that any later modification done, potentially not read by sync, will have */
    1332             :                 /* a different mtime, and it will be synchronized at the next sync. */
    1333             :                 /* The worst case is the FAT file-system with a two seconds resolution for mtime. */
    1334             :                 /* If you don't use FAT, the wait is not needed, because most file-systems have now */
    1335             :                 /* at least microseconds resolution, but better to be safe. */
    1336          87 :                 if (!opt.skip_self)
    1337           0 :                         sleep(2);
    1338             : 
    1339          87 :                 ret = state_sync(&state, blockstart, blockcount);
    1340             : 
    1341             :                 /* save the new state if required */
    1342          87 :                 if (!opt.kill_after_sync) {
    1343          77 :                         if ((state.need_write || state.opt.force_content_write))
    1344          58 :                                 state_write(&state);
    1345             :                 } else {
    1346          10 :                         log_fatal("WARNING! Skipped state write for --test-kill-after-sync option.\n");
    1347             :                 }
    1348             : 
    1349             :                 /* abort if required */
    1350          87 :                 if (ret != 0) {
    1351             :                         /* LCOV_EXCL_START */
    1352             :                         exit(EXIT_FAILURE);
    1353             :                         /* LCOV_EXCL_STOP */
    1354             :                 }
    1355         168 :         } else if (operation == OPERATION_DRY) {
    1356           2 :                 state_read(&state);
    1357             : 
    1358             :                 /* filter */
    1359           2 :                 state_skip(&state);
    1360           2 :                 state_filter(&state, &filterlist_file, &filterlist_disk, filter_missing, filter_error);
    1361             : 
    1362           2 :                 memory();
    1363             : 
    1364             :                 /* intercept signals while operating */
    1365           2 :                 signal_init();
    1366             : 
    1367           2 :                 state_dry(&state, blockstart, blockcount);
    1368         166 :         } else if (operation == OPERATION_REHASH) {
    1369           1 :                 state_read(&state);
    1370             : 
    1371             :                 /* intercept signals while operating */
    1372           1 :                 signal_init();
    1373             : 
    1374           1 :                 state_rehash(&state);
    1375             : 
    1376             :                 /* save the new state if required */
    1377           1 :                 if (state.need_write)
    1378           1 :                         state_write(&state);
    1379         165 :         } else if (operation == OPERATION_SCRUB) {
    1380          11 :                 state_read(&state);
    1381             : 
    1382          11 :                 memory();
    1383             : 
    1384             :                 /* intercept signals while operating */
    1385          11 :                 signal_init();
    1386             : 
    1387          11 :                 ret = state_scrub(&state, plan, olderthan);
    1388             : 
    1389             :                 /* save the new state if required */
    1390          11 :                 if (state.need_write || state.opt.force_content_write)
    1391          10 :                         state_write(&state);
    1392             : 
    1393             :                 /* abort if required */
    1394          11 :                 if (ret != 0) {
    1395             :                         /* LCOV_EXCL_START */
    1396             :                         exit(EXIT_FAILURE);
    1397             :                         /* LCOV_EXCL_STOP */
    1398             :                 }
    1399         154 :         } else if (operation == OPERATION_REWRITE) {
    1400           1 :                 state_read(&state);
    1401             : 
    1402             :                 /* intercept signals while operating */
    1403           1 :                 signal_init();
    1404             : 
    1405           1 :                 state_write(&state);
    1406             : 
    1407           1 :                 memory();
    1408         153 :         } else if (operation == OPERATION_READ) {
    1409           1 :                 state_read(&state);
    1410             : 
    1411           1 :                 memory();
    1412         152 :         } else if (operation == OPERATION_TOUCH) {
    1413           1 :                 state_read(&state);
    1414             : 
    1415           1 :                 state_touch(&state);
    1416             : 
    1417             :                 /* intercept signals while operating */
    1418           1 :                 signal_init();
    1419             : 
    1420           1 :                 state_write(&state);
    1421             : 
    1422           1 :                 memory();
    1423         151 :         } else if (operation == OPERATION_SPINUP) {
    1424           1 :                 state_device(&state, DEVICE_UP, &filterlist_disk);
    1425         150 :         } else if (operation == OPERATION_SPINDOWN) {
    1426           1 :                 state_device(&state, DEVICE_DOWN, &filterlist_disk);
    1427         149 :         } else if (operation == OPERATION_DEVICES) {
    1428           1 :                 state_device(&state, DEVICE_LIST, 0);
    1429         148 :         } else if (operation == OPERATION_SMART) {
    1430           2 :                 state_device(&state, DEVICE_SMART, 0);
    1431         146 :         } else if (operation == OPERATION_STATUS) {
    1432          15 :                 state_read(&state);
    1433             : 
    1434          15 :                 memory();
    1435             : 
    1436          15 :                 state_status(&state);
    1437         131 :         } else if (operation == OPERATION_DUP) {
    1438           3 :                 state_read(&state);
    1439             : 
    1440           3 :                 state_dup(&state);
    1441         128 :         } else if (operation == OPERATION_LIST) {
    1442           5 :                 state_read(&state);
    1443             : 
    1444           5 :                 state_list(&state);
    1445         123 :         } else if (operation == OPERATION_POOL) {
    1446           3 :                 state_read(&state);
    1447             : 
    1448           3 :                 state_pool(&state);
    1449             :         } else {
    1450         120 :                 state_read(&state);
    1451             : 
    1452             :                 /* if we are also trying to recover */
    1453         120 :                 if (!state.opt.auditonly) {
    1454             :                         /* import the user specified dirs */
    1455         114 :                         if (import_timestamp != 0)
    1456           1 :                                 state_search(&state, import_timestamp);
    1457         114 :                         if (import_content != 0)
    1458           1 :                                 state_import(&state, import_content);
    1459             : 
    1460             :                         /* import from all the array */
    1461         114 :                         if (!state.opt.force_nocopy)
    1462         114 :                                 state_search_array(&state);
    1463             :                 }
    1464             : 
    1465             :                 /* filter */
    1466         120 :                 state_skip(&state);
    1467         120 :                 state_filter(&state, &filterlist_file, &filterlist_disk, filter_missing, filter_error);
    1468             : 
    1469         120 :                 memory();
    1470             : 
    1471             :                 /* intercept signals while operating */
    1472         120 :                 signal_init();
    1473             : 
    1474         120 :                 if (operation == OPERATION_CHECK) {
    1475          75 :                         ret = state_check(&state, 0, blockstart, blockcount);
    1476             :                 } else { /* it's fix */
    1477          45 :                         ret = state_check(&state, 1, blockstart, blockcount);
    1478             :                 }
    1479             : 
    1480             :                 /* abort if required */
    1481         120 :                 if (ret != 0) {
    1482             :                         /* LCOV_EXCL_START */
    1483             :                         exit(EXIT_FAILURE);
    1484             :                         /* LCOV_EXCL_STOP */
    1485             :                 }
    1486             :         }
    1487             : 
    1488             :         /* close log file */
    1489         243 :         log_close(log_file);
    1490             : 
    1491             : #if HAVE_LOCKFILE
    1492         243 :         if (!opt.skip_lock && state.lockfile[0]) {
    1493         241 :                 if (lock_unlock(lock) == -1) {
    1494             :                         /* LCOV_EXCL_START */
    1495             :                         log_fatal("Error closing the lock file '%s'. %s.\n", state.lockfile, strerror(errno));
    1496             :                         exit(EXIT_FAILURE);
    1497             :                         /* LCOV_EXCL_STOP */
    1498             :                 }
    1499             :         }
    1500             : #endif
    1501             : 
    1502         243 :         state_done(&state);
    1503         243 :         tommy_list_foreach(&filterlist_file, (tommy_foreach_func*)filter_free);
    1504         243 :         tommy_list_foreach(&filterlist_disk, (tommy_foreach_func*)filter_free);
    1505             : 
    1506         243 :         os_done();
    1507         243 :         lock_done();
    1508             : 
    1509         243 :         return EXIT_SUCCESS;
    1510             : }
    1511             : 

Generated by: LCOV version 1.13