LCOV - code coverage report
Current view: top level - cmdline - snapraid.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 805 921 87.4 %
Date: 2026-04-29 15:04:44 Functions: 10 10 100.0 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-3.0-or-later
       2             : // Copyright (C) 2011 Andrea Mazzoleni
       3             : 
       4             : #include "portable.h"
       5             : 
       6             : #include "snapraid.h"
       7             : #include "support.h"
       8             : #include "elem.h"
       9             : #include "import.h"
      10             : #include "search.h"
      11             : #include "state.h"
      12             : #include "io.h"
      13             : #include "raid/raid.h"
      14             : #include "locate.h"
      15             : 
      16             : /****************************************************************************/
      17             : /* misc */
      18             : 
      19           2 : void version(void)
      20             : {
      21           2 :         msg_status(PACKAGE_NAME " v" VERSION " by Andrea Mazzoleni, " PACKAGE_URL "\n");
      22           2 : }
      23             : 
      24           1 : void usage(const char* conf)
      25             : {
      26           1 :         version();
      27             : 
      28           1 :         printf("Usage: " PACKAGE " status|diff|sync|scrub|list|dup|up|down|probe|touch|smart|pool|check|fix [options]\n");
      29           1 :         printf("\n");
      30           1 :         printf("Commands:\n");
      31           1 :         printf("  status Print the status of the array\n");
      32           1 :         printf("  diff   Show the changes that needs to be synchronized\n");
      33           1 :         printf("  sync   Synchronize the state of the array\n");
      34           1 :         printf("  scrub  Scrub the array\n");
      35           1 :         printf("  list   List the array content\n");
      36           1 :         printf("  dup    Find duplicate files\n");
      37           1 :         printf("  up     Spin-up the array\n");
      38           1 :         printf("  down   Spin-down the array\n");
      39           1 :         printf("  probe  Show the spinning status of all disks\n");
      40           1 :         printf("  touch  Add non-zero ns timestamps to files\n");
      41           1 :         printf("  smart  SMART attributes of the array\n");
      42           1 :         printf("  pool   Create or update the virtual view of the array\n");
      43           1 :         printf("  check  Check the array\n");
      44           1 :         printf("  fix    Fix the array\n");
      45           1 :         printf("\n");
      46           1 :         printf("Options:\n");
      47           1 :         printf("  " SWITCH_GETOPT_LONG("-c, --conf FILE       ", "-c") "  Configuration file\n");
      48           1 :         printf("  " SWITCH_GETOPT_LONG("-f, --filter PATTERN  ", "-f") "  Process only files matching the pattern\n");
      49           1 :         printf("  " SWITCH_GETOPT_LONG("-d, --filter-disk NAME", "-d") "  Process only files in the specified disk\n");
      50           1 :         printf("  " SWITCH_GETOPT_LONG("-m, --filter-missing  ", "-m") "  Process only missing/deleted files\n");
      51           1 :         printf("  " SWITCH_GETOPT_LONG("-e, --filter-error    ", "-e") "  Process only files with errors\n");
      52           1 :         printf("  " SWITCH_GETOPT_LONG("-p, --plan PLAN       ", "-p") "  Define a scrub plan or percentage\n");
      53           1 :         printf("  " SWITCH_GETOPT_LONG("-o, --older-than DAYS ", "-o") "  Process only the older part of the array\n");
      54           1 :         printf("  " SWITCH_GETOPT_LONG("-i, --import DIR      ", "-i") "  Import deleted files\n");
      55           1 :         printf("  " SWITCH_GETOPT_LONG("-l, --log FILE        ", "-l") "  Log file. Default none\n");
      56           1 :         printf("  " SWITCH_GETOPT_LONG("-a, --audit-only      ", "-a") "  Check only file data and not parity\n");
      57           1 :         printf("  " SWITCH_GETOPT_LONG("-h, --pre-hash        ", "-h") "  Pre-hash all the new data\n");
      58           1 :         printf("  " SWITCH_GETOPT_LONG("-Z, --force-zero      ", "-Z") "  Force syncing of files that get zero size\n");
      59           1 :         printf("  " SWITCH_GETOPT_LONG("-E, --force-empty     ", "-E") "  Force syncing of disks that get empty\n");
      60           1 :         printf("  " SWITCH_GETOPT_LONG("-U, --force-uuid      ", "-U") "  Force commands on disks with uuid changed\n");
      61           1 :         printf("  " SWITCH_GETOPT_LONG("-D, --force-device    ", "-D") "  Force commands with inaccessible/shared disks\n");
      62           1 :         printf("  " SWITCH_GETOPT_LONG("-N, --force-nocopy    ", "-N") "  Force commands disabling the copy detection\n");
      63             :         /* --force-full, --force-realloc and --force-realloc-tail are not listed as they are dangerous */
      64           1 :         printf("  " SWITCH_GETOPT_LONG("-w, --bw-limit RATE   ", "-w") "  Limit IO bandwidth (M|G)\n");
      65           1 :         printf("  " SWITCH_GETOPT_LONG("-v, --verbose         ", "-v") "  Verbose\n");
      66           1 :         printf("\n");
      67           1 :         printf("Configuration file: %s\n", conf);
      68           1 :         printf("\n");
      69           1 : }
      70             : 
      71         275 : void memory(void)
      72             : {
      73         275 :         log_tag("memory:used:%" PRIu64 "\n", (uint64_t)malloc_counter_get());
      74             : 
      75             :         /* size of the block */
      76         275 :         log_tag("memory:block:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_block)));
      77         275 :         log_tag("memory:extent:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_extent)));
      78         275 :         log_tag("memory:file:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_file)));
      79         275 :         log_tag("memory:link:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_link)));
      80         275 :         log_tag("memory:dir:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_dir)));
      81             : 
      82         275 :         msg_progress("Using %u MiB of memory for the file-system.\n", (unsigned)(malloc_counter_get() / MEBI));
      83         275 : }
      84             : 
      85         327 : void test(int argc, char* argv[])
      86             : {
      87             :         int i;
      88             :         char buffer[ESC_MAX];
      89             : 
      90             :         /* special testing code */
      91         327 :         if (argc < 2 || strcmp(argv[1], "test") != 0)
      92         326 :                 return;
      93             : 
      94           1 :         lock_init();
      95             : 
      96           1 :         assert(strcmp(strpolish(strcpy(buffer, "\r \n\xFF")), "    ") == 0);
      97           1 :         assert(strcmp(strtrim(strcpy(buffer, " trim trim \n\r")), "trim trim") == 0);
      98           1 :         assert(strcmp(strlwr(strcpy(buffer, " LoWer\n\r")), " lower\n\r") == 0);
      99             : 
     100           1 :         assert(worddigitstr("longneedlestring", "needle") == 0);
     101           1 :         assert(worddigitstr("longneedlestring", "") == 0);
     102           1 :         assert(worddigitstr("long needle string", "needle") != 0);
     103           1 :         assert(worddigitstr("long1needle2string", "needle") != 0);
     104           1 :         assert(worddigitstr("long\rneedle3string", "needle") != 0);
     105           1 :         assert(worddigitstr("long1needle", "needle") != 0);
     106           1 :         assert(worddigitstr("needle2string", "needle") != 0);
     107           1 :         assert(worddigitstr("needle", "needle") != 0);
     108             : 
     109           1 :         assert(strcmp(esc_tag("simple", buffer), "simple") == 0);
     110           1 :         assert(strcmp(esc_tag("line1\nline2", buffer), "line1\\nline2") == 0);
     111           1 :         assert(strcmp(esc_tag("line1\rline2", buffer), "line1\\rline2") == 0);
     112           1 :         assert(strcmp(esc_tag("key:value", buffer), "key\\dvalue") == 0);
     113           1 :         assert(strcmp(esc_tag("C:\\path\\file", buffer), "C\\d\\\\path\\\\file") == 0);
     114           1 :         assert(strcmp(esc_tag("A\nB\rC:D\\E", buffer), "A\\nB\\rC\\dD\\\\E") == 0);
     115           1 :         assert(strcmp(esc_tag("endwith\\", buffer), "endwith\\\\") == 0);
     116           1 :         assert(strcmp(esc_tag("", buffer), "") == 0);
     117           1 :         assert(strcmp(esc_tag("\n\r:\\\\", buffer), "\\n\\r\\d\\\\\\\\") == 0);
     118             : 
     119           1 :         for (i = 2; i < argc; ++i) {
     120           0 :                 printf("argv[%d]\n", i);
     121           0 :                 printf("\t#%s#\n", argv[i]);
     122           0 :                 printf("\t#%s#\n", esc_shell(argv[i], buffer));
     123             :         }
     124             : 
     125             : #ifdef _WIN32
     126             :         /* basic cases - no special characters, no quotes needed */
     127             :         assert(strcmp(esc_shell("simple", buffer), "simple") == 0);
     128             :         assert(strcmp(esc_shell("file.txt", buffer), "file.txt") == 0);
     129             :         assert(strcmp(esc_shell("file123", buffer), "file123") == 0);
     130             :         assert(strcmp(esc_shell("file_name-test.doc", buffer), "file_name-test.doc") == 0);
     131             :         assert(strcmp(esc_shell(",._+:@/-", buffer), ",._+:@/-") == 0);
     132             :         assert(strcmp(esc_shell("C:\\Users\\test", buffer), "C:\\Users\\test") == 0);
     133             : 
     134             :         /* space - requires quoting */
     135             :         assert(strcmp(esc_shell(" ", buffer), "\" \"") == 0);
     136             :         assert(strcmp(esc_shell("file name.txt", buffer), "\"file name.txt\"") == 0);
     137             :         assert(strcmp(esc_shell("my document.doc", buffer), "\"my document.doc\"") == 0);
     138             :         assert(strcmp(esc_shell("  multiple  spaces  ", buffer), "\"  multiple  spaces  \"") == 0);
     139             : 
     140             :         /* tab - requires quoting */
     141             :         assert(strcmp(esc_shell("\t", buffer), "\"\t\"") == 0);
     142             :         assert(strcmp(esc_shell("file\tname", buffer), "\"file\tname\"") == 0);
     143             : 
     144             :         /* newline - requires quoting */
     145             :         assert(strcmp(esc_shell("\n", buffer), "\"\n\"") == 0);
     146             :         assert(strcmp(esc_shell("line1\nline2", buffer), "\"line1\nline2\"") == 0);
     147             : 
     148             :         /* carriage return - requires quoting */
     149             :         assert(strcmp(esc_shell("\r", buffer), "\"\r\"") == 0);
     150             :         assert(strcmp(esc_shell("text\r\n", buffer), "\"text\r\n\"") == 0);
     151             : 
     152             :         /* double quote - requires quoting and escaping with backslash */
     153             :         assert(strcmp(esc_shell("\"", buffer), "\"\\\"\"") == 0);
     154             :         assert(strcmp(esc_shell("file\"name", buffer), "\"file\\\"name\"") == 0);
     155             :         assert(strcmp(esc_shell("\"quoted\"", buffer), "\"\\\"quoted\\\"\"") == 0);
     156             :         assert(strcmp(esc_shell("say \"hello\"", buffer), "\"say \\\"hello\\\"\"") == 0);
     157             : 
     158             :         /* ampersand - requires quoting */
     159             :         assert(strcmp(esc_shell("&", buffer), "\"&\"") == 0);
     160             :         assert(strcmp(esc_shell("file&name", buffer), "\"file&name\"") == 0);
     161             :         assert(strcmp(esc_shell("a&b&c", buffer), "\"a&b&c\"") == 0);
     162             :         assert(strcmp(esc_shell("file & name", buffer), "\"file & name\"") == 0);
     163             : 
     164             :         /* pipe - requires quoting */
     165             :         assert(strcmp(esc_shell("|", buffer), "\"|\"") == 0);
     166             :         assert(strcmp(esc_shell("file|name", buffer), "\"file|name\"") == 0);
     167             :         assert(strcmp(esc_shell("a | b", buffer), "\"a | b\"") == 0);
     168             : 
     169             :         /* parentheses - requires quoting */
     170             :         assert(strcmp(esc_shell("(", buffer), "\"(\"") == 0);
     171             :         assert(strcmp(esc_shell(")", buffer), "\")\"") == 0);
     172             :         assert(strcmp(esc_shell("(test)", buffer), "\"(test)\"") == 0);
     173             :         assert(strcmp(esc_shell("file (1)", buffer), "\"file (1)\"") == 0);
     174             :         assert(strcmp(esc_shell("file(copy)", buffer), "\"file(copy)\"") == 0);
     175             : 
     176             :         /* angle brackets - requires quoting */
     177             :         assert(strcmp(esc_shell("<", buffer), "\"<\"") == 0);
     178             :         assert(strcmp(esc_shell(">", buffer), "\">\"") == 0);
     179             :         assert(strcmp(esc_shell("a<b>c", buffer), "\"a<b>c\"") == 0);
     180             :         assert(strcmp(esc_shell("file > output", buffer), "\"file > output\"") == 0);
     181             : 
     182             :         /* caret - requires quoting */
     183             :         assert(strcmp(esc_shell("^", buffer), "\"^\"") == 0);
     184             :         assert(strcmp(esc_shell("test^test", buffer), "\"test^test\"") == 0);
     185             :         assert(strcmp(esc_shell("a ^ b", buffer), "\"a ^ b\"") == 0);
     186             : 
     187             :         /* multiple special chars - requires quoting */
     188             :         assert(strcmp(esc_shell("&|()<>^", buffer), "\"&|()<>^\"") == 0);
     189             :         assert(strcmp(esc_shell("test&|test", buffer), "\"test&|test\"") == 0);
     190             :         assert(strcmp(esc_shell("a & b | c", buffer), "\"a & b | c\"") == 0);
     191             : 
     192             :         /* percent sign - requires quoting */
     193             :         assert(strcmp(esc_shell("%", buffer), "\"%\"") == 0);
     194             :         assert(strcmp(esc_shell("%%", buffer), "\"%%\"") == 0);
     195             :         assert(strcmp(esc_shell("%PATH%", buffer), "\"%PATH%\"") == 0);
     196             :         assert(strcmp(esc_shell("test%var%test", buffer), "\"test%var%test\"") == 0);
     197             :         assert(strcmp(esc_shell("%PATH% file", buffer), "\"%PATH% file\"") == 0);
     198             : 
     199             :         /* exclamation mark - requires quoting */
     200             :         assert(strcmp(esc_shell("!", buffer), "\"!\"") == 0);
     201             :         assert(strcmp(esc_shell("!VAR!", buffer), "\"!VAR!\"") == 0);
     202             :         assert(strcmp(esc_shell("test!test", buffer), "\"test!test\"") == 0);
     203             :         assert(strcmp(esc_shell("hello !world!", buffer), "\"hello !world!\"") == 0);
     204             : 
     205             :         /* equals sign - requires quoting */
     206             :         assert(strcmp(esc_shell("=", buffer), "\"=\"") == 0);
     207             :         assert(strcmp(esc_shell("VAR=value", buffer), "\"VAR=value\"") == 0);
     208             :         assert(strcmp(esc_shell("a=b", buffer), "\"a=b\"") == 0);
     209             : 
     210             :         /* semicolon - requires quoting */
     211             :         assert(strcmp(esc_shell(";", buffer), "\";\"") == 0);
     212             :         assert(strcmp(esc_shell("cmd1;cmd2", buffer), "\"cmd1;cmd2\"") == 0);
     213             : 
     214             :         /* backslash - no quotes needed when alone or in path */
     215             :         assert(strcmp(esc_shell("\\", buffer), "\\") == 0);
     216             :         assert(strcmp(esc_shell("C:\\", buffer), "C:\\") == 0);
     217             :         assert(strcmp(esc_shell("C:\\Users", buffer), "C:\\Users") == 0);
     218             :         assert(strcmp(esc_shell("path\\to\\file", buffer), "path\\to\\file") == 0);
     219             :         assert(strcmp(esc_shell("C:\\folder\\", buffer), "C:\\folder\\") == 0);
     220             : 
     221             :         /* backslash with space - requires quoting, normal backslash inside */
     222             :         assert(strcmp(esc_shell("\\ ", buffer), "\"\\ \"") == 0);
     223             :         assert(strcmp(esc_shell("C:\\ ", buffer), "\"C:\\ \"") == 0);
     224             :         assert(strcmp(esc_shell("C:\\Program Files", buffer), "\"C:\\Program Files\"") == 0);
     225             : 
     226             :         /* trailing backslash with quotes - backslashes before closing quote must be doubled */
     227             :         assert(strcmp(esc_shell("C:\\folder\\ ", buffer), "\"C:\\folder\\ \"") == 0);
     228             :         assert(strcmp(esc_shell("path\\ ", buffer), "\"path\\ \"") == 0);
     229             :         assert(strcmp(esc_shell("C:\\My Documents\\", buffer), "\"C:\\My Documents\\\\\"") == 0);
     230             : 
     231             :         /* backslash before embedded quote - backslash before quote must be doubled */
     232             :         assert(strcmp(esc_shell("C:\\\"test\"", buffer), "\"C:\\\\\\\"test\\\"\"") == 0);
     233             :         assert(strcmp(esc_shell("path\\\"file\"", buffer), "\"path\\\\\\\"file\\\"\"") == 0);
     234             : 
     235             :         /* multiple trailing backslashes before end with quotes */
     236             :         assert(strcmp(esc_shell("test\\\\ ", buffer), "\"test\\\\ \"") == 0);
     237             :         assert(strcmp(esc_shell("path\\\\\\\\ ", buffer), "\"path\\\\\\\\ \"") == 0);
     238             : 
     239             :         /* backslash NOT before quote - normal backslash */
     240             :         assert(strcmp(esc_shell("test\\file ", buffer), "\"test\\file \"") == 0);
     241             :         assert(strcmp(esc_shell("a\\b c", buffer), "\"a\\b c\"") == 0);
     242             : 
     243             :         /* control characters - require quoting */
     244             :         assert(strcmp(esc_shell("\x01", buffer), "\"\x01\"") == 0);
     245             :         assert(strcmp(esc_shell("\x1F", buffer), "\"\x1F\"") == 0);
     246             :         assert(strcmp(esc_shell("\x7F", buffer), "\"\x7F\"") == 0); /* DEL character */
     247             :         assert(strcmp(esc_shell("test\x01test", buffer), "\"test\x01test\"") == 0);
     248             : 
     249             :         /* complex real-world examples */
     250             :         assert(strcmp(esc_shell("C:\\Program Files\\App", buffer), "\"C:\\Program Files\\App\"") == 0);
     251             :         assert(strcmp(esc_shell("C:\\Program Files (x86)\\", buffer), "\"C:\\Program Files (x86)\\\\\"") == 0);
     252             :         assert(strcmp(esc_shell("file (copy).txt", buffer), "\"file (copy).txt\"") == 0);
     253             :         assert(strcmp(esc_shell("setup-v1.0.exe", buffer), "setup-v1.0.exe") == 0);
     254             :         assert(strcmp(esc_shell("setup v1.0.exe", buffer), "\"setup v1.0.exe\"") == 0);
     255             : 
     256             :         /* mixed quotes and special chars */
     257             :         assert(strcmp(esc_shell("say \"hi\" & exit", buffer), "\"say \\\"hi\\\" & exit\"") == 0);
     258             :         assert(strcmp(esc_shell("test \"a|b\"", buffer), "\"test \\\"a|b\\\"\"") == 0);
     259             : 
     260             :         /* empty string */
     261             :         assert(strcmp(esc_shell("", buffer), "") == 0);
     262             : 
     263             :         /* all safe characters that don't need escaping */
     264             :         assert(strcmp(esc_shell("abcdefghijklmnopqrstuvwxyz", buffer), "abcdefghijklmnopqrstuvwxyz") == 0);
     265             :         assert(strcmp(esc_shell("ABCDEFGHIJKLMNOPQRSTUVWXYZ", buffer), "ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 0);
     266             :         assert(strcmp(esc_shell("0123456789", buffer), "0123456789") == 0);
     267             :         assert(strcmp(esc_shell("._-+,@:", buffer), "._-+,@:") == 0);
     268             : #else
     269             :         /* basic cases - no special characters */
     270           1 :         assert(strcmp(esc_shell("simple", buffer), "simple") == 0);
     271           1 :         assert(strcmp(esc_shell("file.txt", buffer), "file.txt") == 0);
     272           1 :         assert(strcmp(esc_shell("file123", buffer), "file123") == 0);
     273           1 :         assert(strcmp(esc_shell("file_name-test.doc", buffer), "file_name-test.doc") == 0);
     274           1 :         assert(strcmp(esc_shell(",._+:@/-", buffer), ",._+:@/-") == 0);
     275           1 :         assert(strcmp(esc_shell("/usr/local/bin", buffer), "/usr/local/bin") == 0);
     276             : 
     277             :         /* empty string */
     278           1 :         assert(strcmp(esc_shell("", buffer), "") == 0);
     279             : 
     280             :         /* space - escape with backslash */
     281           1 :         assert(strcmp(esc_shell(" ", buffer), "\\ ") == 0);
     282           1 :         assert(strcmp(esc_shell("file name.txt", buffer), "file\\ name.txt") == 0);
     283           1 :         assert(strcmp(esc_shell("my document.doc", buffer), "my\\ document.doc") == 0);
     284           1 :         assert(strcmp(esc_shell("  spaces  ", buffer), "\\ \\ spaces\\ \\ ") == 0);
     285           1 :         assert(strcmp(esc_shell("a b c", buffer), "a\\ b\\ c") == 0);
     286             : 
     287             :         /* tab - escape with backslash */
     288           1 :         assert(strcmp(esc_shell("\t", buffer), "\\\t") == 0);
     289           1 :         assert(strcmp(esc_shell("file\tname", buffer), "file\\\tname") == 0);
     290           1 :         assert(strcmp(esc_shell("\t\t", buffer), "\\\t\\\t") == 0);
     291             : 
     292             :         /* newline - escape with backslash */
     293           1 :         assert(strcmp(esc_shell("\n", buffer), "\\\n") == 0);
     294           1 :         assert(strcmp(esc_shell("line1\nline2", buffer), "line1\\\nline2") == 0);
     295           1 :         assert(strcmp(esc_shell("\n\n", buffer), "\\\n\\\n") == 0);
     296             : 
     297             :         /* carriage return - escape with backslash */
     298           1 :         assert(strcmp(esc_shell("\r", buffer), "\\\r") == 0);
     299           1 :         assert(strcmp(esc_shell("text\r\n", buffer), "text\\\r\\\n") == 0);
     300             : 
     301             :         /* tilde (home directory expansion) */
     302           1 :         assert(strcmp(esc_shell("~", buffer), "\\~") == 0);
     303           1 :         assert(strcmp(esc_shell("~/file", buffer), "\\~/file") == 0);
     304           1 :         assert(strcmp(esc_shell("file~name", buffer), "file\\~name") == 0);
     305           1 :         assert(strcmp(esc_shell("~user", buffer), "\\~user") == 0);
     306             : 
     307             :         /* backtick (command substitution) */
     308           1 :         assert(strcmp(esc_shell("`", buffer), "\\`") == 0);
     309           1 :         assert(strcmp(esc_shell("`command`", buffer), "\\`command\\`") == 0);
     310           1 :         assert(strcmp(esc_shell("test`test", buffer), "test\\`test") == 0);
     311           1 :         assert(strcmp(esc_shell("``", buffer), "\\`\\`") == 0);
     312             : 
     313             :         /* hash (comment) */
     314           1 :         assert(strcmp(esc_shell("#", buffer), "\\#") == 0);
     315           1 :         assert(strcmp(esc_shell("#comment", buffer), "\\#comment") == 0);
     316           1 :         assert(strcmp(esc_shell("file#name", buffer), "file\\#name") == 0);
     317           1 :         assert(strcmp(esc_shell("test#123", buffer), "test\\#123") == 0);
     318             : 
     319             :         /* dollar sign (variable expansion) */
     320           1 :         assert(strcmp(esc_shell("$", buffer), "\\$") == 0);
     321           1 :         assert(strcmp(esc_shell("$$", buffer), "\\$\\$") == 0);
     322           1 :         assert(strcmp(esc_shell("$VAR", buffer), "\\$VAR") == 0);
     323           1 :         assert(strcmp(esc_shell("${VAR}", buffer), "\\$\\{VAR\\}") == 0);
     324           1 :         assert(strcmp(esc_shell("test$test", buffer), "test\\$test") == 0);
     325           1 :         assert(strcmp(esc_shell("$1", buffer), "\\$1") == 0);
     326           1 :         assert(strcmp(esc_shell("$PATH", buffer), "\\$PATH") == 0);
     327             : 
     328             :         /* ampersand (background job) */
     329           1 :         assert(strcmp(esc_shell("&", buffer), "\\&") == 0);
     330           1 :         assert(strcmp(esc_shell("&&", buffer), "\\&\\&") == 0);
     331           1 :         assert(strcmp(esc_shell("file&name", buffer), "file\\&name") == 0);
     332           1 :         assert(strcmp(esc_shell("a&b&c", buffer), "a\\&b\\&c") == 0);
     333           1 :         assert(strcmp(esc_shell("cmd1 & cmd2", buffer), "cmd1\\ \\&\\ cmd2") == 0);
     334             : 
     335             :         /* asterisk (wildcard) */
     336           1 :         assert(strcmp(esc_shell("*", buffer), "\\*") == 0);
     337           1 :         assert(strcmp(esc_shell("**", buffer), "\\*\\*") == 0);
     338           1 :         assert(strcmp(esc_shell("*.txt", buffer), "\\*.txt") == 0);
     339           1 :         assert(strcmp(esc_shell("file*name", buffer), "file\\*name") == 0);
     340           1 :         assert(strcmp(esc_shell("test*", buffer), "test\\*") == 0);
     341             : 
     342             :         /* parentheses (subshell) */
     343           1 :         assert(strcmp(esc_shell("(", buffer), "\\(") == 0);
     344           1 :         assert(strcmp(esc_shell(")", buffer), "\\)") == 0);
     345           1 :         assert(strcmp(esc_shell("()", buffer), "\\(\\)") == 0);
     346           1 :         assert(strcmp(esc_shell("(test)", buffer), "\\(test\\)") == 0);
     347           1 :         assert(strcmp(esc_shell("file(1)", buffer), "file\\(1\\)") == 0);
     348           1 :         assert(strcmp(esc_shell("(a)(b)", buffer), "\\(a\\)\\(b\\)") == 0);
     349             : 
     350             :         /* backslash (escape character) */
     351           1 :         assert(strcmp(esc_shell("\\", buffer), "\\\\") == 0);
     352           1 :         assert(strcmp(esc_shell("\\\\", buffer), "\\\\\\\\") == 0);
     353           1 :         assert(strcmp(esc_shell("path\\to\\file", buffer), "path\\\\to\\\\file") == 0);
     354           1 :         assert(strcmp(esc_shell("test\\test", buffer), "test\\\\test") == 0);
     355           1 :         assert(strcmp(esc_shell("a\\b\\c", buffer), "a\\\\b\\\\c") == 0);
     356             : 
     357             :         /* pipe (pipeline) */
     358           1 :         assert(strcmp(esc_shell("|", buffer), "\\|") == 0);
     359           1 :         assert(strcmp(esc_shell("||", buffer), "\\|\\|") == 0);
     360           1 :         assert(strcmp(esc_shell("file|name", buffer), "file\\|name") == 0);
     361           1 :         assert(strcmp(esc_shell("a|b|c", buffer), "a\\|b\\|c") == 0);
     362           1 :         assert(strcmp(esc_shell("cmd1 | cmd2", buffer), "cmd1\\ \\|\\ cmd2") == 0);
     363             : 
     364             :         /* square brackets (wildcard) */
     365           1 :         assert(strcmp(esc_shell("[", buffer), "\\[") == 0);
     366           1 :         assert(strcmp(esc_shell("]", buffer), "\\]") == 0);
     367           1 :         assert(strcmp(esc_shell("[]", buffer), "\\[\\]") == 0);
     368           1 :         assert(strcmp(esc_shell("[abc]", buffer), "\\[abc\\]") == 0);
     369           1 :         assert(strcmp(esc_shell("file[1]", buffer), "file\\[1\\]") == 0);
     370           1 :         assert(strcmp(esc_shell("[0-9]", buffer), "\\[0-9\\]") == 0);
     371             : 
     372             :         /* curly braces (brace expansion) */
     373           1 :         assert(strcmp(esc_shell("{", buffer), "\\{") == 0);
     374           1 :         assert(strcmp(esc_shell("}", buffer), "\\}") == 0);
     375           1 :         assert(strcmp(esc_shell("{}", buffer), "\\{\\}") == 0);
     376           1 :         assert(strcmp(esc_shell("{a,b,c}", buffer), "\\{a,b,c\\}") == 0);
     377           1 :         assert(strcmp(esc_shell("file{1,2}", buffer), "file\\{1,2\\}") == 0);
     378           1 :         assert(strcmp(esc_shell("{1..10}", buffer), "\\{1..10\\}") == 0);
     379             : 
     380             :         /* semicolon (command separator) */
     381           1 :         assert(strcmp(esc_shell(";", buffer), "\\;") == 0);
     382           1 :         assert(strcmp(esc_shell(";;", buffer), "\\;\\;") == 0);
     383           1 :         assert(strcmp(esc_shell("cmd1;cmd2", buffer), "cmd1\\;cmd2") == 0);
     384           1 :         assert(strcmp(esc_shell("test;test", buffer), "test\\;test") == 0);
     385           1 :         assert(strcmp(esc_shell("a; b", buffer), "a\\;\\ b") == 0);
     386             : 
     387             :         /* single quote */
     388           1 :         assert(strcmp(esc_shell("'", buffer), "\\'") == 0);
     389           1 :         assert(strcmp(esc_shell("''", buffer), "\\'\\'") == 0);
     390           1 :         assert(strcmp(esc_shell("'test'", buffer), "\\'test\\'") == 0);
     391           1 :         assert(strcmp(esc_shell("file'name", buffer), "file\\'name") == 0);
     392           1 :         assert(strcmp(esc_shell("it's", buffer), "it\\'s") == 0);
     393             : 
     394             :         /* double quote */
     395           1 :         assert(strcmp(esc_shell("\"", buffer), "\\\"") == 0);
     396           1 :         assert(strcmp(esc_shell("\"\"", buffer), "\\\"\\\"") == 0);
     397           1 :         assert(strcmp(esc_shell("\"test\"", buffer), "\\\"test\\\"") == 0);
     398           1 :         assert(strcmp(esc_shell("file\"name", buffer), "file\\\"name") == 0);
     399           1 :         assert(strcmp(esc_shell("say \"hi\"", buffer), "say\\ \\\"hi\\\"") == 0);
     400             : 
     401             :         /* angle brackets (redirection) */
     402           1 :         assert(strcmp(esc_shell("<", buffer), "\\<") == 0);
     403           1 :         assert(strcmp(esc_shell(">", buffer), "\\>") == 0);
     404           1 :         assert(strcmp(esc_shell("<<", buffer), "\\<\\<") == 0);
     405           1 :         assert(strcmp(esc_shell(">>", buffer), "\\>\\>") == 0);
     406           1 :         assert(strcmp(esc_shell("a<b>c", buffer), "a\\<b\\>c") == 0);
     407           1 :         assert(strcmp(esc_shell("file>output", buffer), "file\\>output") == 0);
     408           1 :         assert(strcmp(esc_shell("cmd < in > out", buffer), "cmd\\ \\<\\ in\\ \\>\\ out") == 0);
     409             : 
     410             :         /* question mark (wildcard) */
     411           1 :         assert(strcmp(esc_shell("?", buffer), "\\?") == 0);
     412           1 :         assert(strcmp(esc_shell("??", buffer), "\\?\\?") == 0);
     413           1 :         assert(strcmp(esc_shell("file?.txt", buffer), "file\\?.txt") == 0);
     414           1 :         assert(strcmp(esc_shell("test?test", buffer), "test\\?test") == 0);
     415           1 :         assert(strcmp(esc_shell("file??", buffer), "file\\?\\?") == 0);
     416             : 
     417             :         /* equals sign (assignment in some contexts) */
     418           1 :         assert(strcmp(esc_shell("=", buffer), "\\=") == 0);
     419           1 :         assert(strcmp(esc_shell("==", buffer), "\\=\\=") == 0);
     420           1 :         assert(strcmp(esc_shell("VAR=value", buffer), "VAR\\=value") == 0);
     421           1 :         assert(strcmp(esc_shell("a=b", buffer), "a\\=b") == 0);
     422           1 :         assert(strcmp(esc_shell("PATH=/usr/bin", buffer), "PATH\\=/usr/bin") == 0);
     423             : 
     424             :         /* exclamation mark (history expansion) */
     425           1 :         assert(strcmp(esc_shell("!", buffer), "\\!") == 0);
     426           1 :         assert(strcmp(esc_shell("!!", buffer), "\\!\\!") == 0);
     427           1 :         assert(strcmp(esc_shell("test!test", buffer), "test\\!test") == 0);
     428           1 :         assert(strcmp(esc_shell("!$", buffer), "\\!\\$") == 0);
     429           1 :         assert(strcmp(esc_shell("!123", buffer), "\\!123") == 0);
     430             : 
     431             :         /* control characters (0x01-0x1F) - escape with backslash */
     432           1 :         assert(strcmp(esc_shell("\x01", buffer), "\\\x01") == 0);
     433           1 :         assert(strcmp(esc_shell("\x02", buffer), "\\\x02") == 0);
     434           1 :         assert(strcmp(esc_shell("\x1F", buffer), "\\\x1F") == 0);
     435           1 :         assert(strcmp(esc_shell("test\x01test", buffer), "test\\\x01test") == 0);
     436             : 
     437             :         /* DEL character (0x7F) */
     438           1 :         assert(strcmp(esc_shell("\x7F", buffer), "\\\x7F") == 0);
     439           1 :         assert(strcmp(esc_shell("test\x7Ftest", buffer), "test\\\x7Ftest") == 0);
     440             : 
     441             :         /* multiple special characters combined */
     442           1 :         assert(strcmp(esc_shell("$VAR & $OTHER", buffer), "\\$VAR\\ \\&\\ \\$OTHER") == 0);
     443           1 :         assert(strcmp(esc_shell("*.txt | grep test", buffer), "\\*.txt\\ \\|\\ grep\\ test") == 0);
     444           1 :         assert(strcmp(esc_shell("file (1) [copy].txt", buffer), "file\\ \\(1\\)\\ \\[copy\\].txt") == 0);
     445           1 :         assert(strcmp(esc_shell("a & b | c", buffer), "a\\ \\&\\ b\\ \\|\\ c") == 0);
     446           1 :         assert(strcmp(esc_shell("cmd1; cmd2 && cmd3", buffer), "cmd1\\;\\ cmd2\\ \\&\\&\\ cmd3") == 0);
     447             : 
     448             :         /* complex real-world examples */
     449           1 :         assert(strcmp(esc_shell("/home/user/My Documents", buffer), "/home/user/My\\ Documents") == 0);
     450           1 :         assert(strcmp(esc_shell("/path/to/file (copy).txt", buffer), "/path/to/file\\ \\(copy\\).txt") == 0);
     451           1 :         assert(strcmp(esc_shell("~/project/file-v1.0.tar.gz", buffer), "\\~/project/file-v1.0.tar.gz") == 0);
     452           1 :         assert(strcmp(esc_shell("$(whoami)@$(hostname)", buffer), "\\$\\(whoami\\)@\\$\\(hostname\\)") == 0);
     453           1 :         assert(strcmp(esc_shell("test && echo 'done'", buffer), "test\\ \\&\\&\\ echo\\ \\'done\\'") == 0);
     454           1 :         assert(strcmp(esc_shell("file #1 [important].txt", buffer), "file\\ \\#1\\ \\[important\\].txt") == 0);
     455           1 :         assert(strcmp(esc_shell("/tmp/test (1).txt", buffer), "/tmp/test\\ \\(1\\).txt") == 0);
     456           1 :         assert(strcmp(esc_shell("var=$HOME/bin:$PATH", buffer), "var\\=\\$HOME/bin:\\$PATH") == 0);
     457             : 
     458             :         /* edge cases with multiple escapes */
     459           1 :         assert(strcmp(esc_shell("a\\ b", buffer), "a\\\\\\ b") == 0);
     460           1 :         assert(strcmp(esc_shell("'\"test\"'", buffer), "\\'\\\"test\\\"\\'") == 0);
     461           1 :         assert(strcmp(esc_shell("$(echo \"test\")", buffer), "\\$\\(echo\\ \\\"test\\\"\\)") == 0);
     462             : 
     463             :         /* all safe characters that don't need escaping */
     464           1 :         assert(strcmp(esc_shell("abcdefghijklmnopqrstuvwxyz", buffer), "abcdefghijklmnopqrstuvwxyz") == 0);
     465           1 :         assert(strcmp(esc_shell("ABCDEFGHIJKLMNOPQRSTUVWXYZ", buffer), "ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 0);
     466           1 :         assert(strcmp(esc_shell("0123456789", buffer), "0123456789") == 0);
     467           1 :         assert(strcmp(esc_shell("._-+,@:", buffer), "._-+,@:") == 0);
     468           1 :         assert(strcmp(esc_shell("/path/to/file", buffer), "/path/to/file") == 0);
     469           1 :         assert(strcmp(esc_shell("simple_file-name.txt", buffer), "simple_file-name.txt") == 0);
     470             : 
     471             :         /* file extensions and versions */
     472           1 :         assert(strcmp(esc_shell("file.tar.gz", buffer), "file.tar.gz") == 0);
     473           1 :         assert(strcmp(esc_shell("app-v1.2.3.deb", buffer), "app-v1.2.3.deb") == 0);
     474           1 :         assert(strcmp(esc_shell("test_2024-01-01.log", buffer), "test_2024-01-01.log") == 0);
     475             : #endif
     476             : 
     477           1 :         random_seed(0);
     478           1 :         assert(random_u8() == 0xAF);
     479           1 :         assert(random_u64() == 0x6E789E6AA1B965F4ULL);
     480             : 
     481           1 :         printf("Everything OK\n");
     482             : 
     483           1 :         lock_done();
     484             : 
     485           1 :         exit(EXIT_SUCCESS);
     486             : }
     487             : 
     488             : /****************************************************************************/
     489             : /* log */
     490             : 
     491         316 : void log_open(const char* file)
     492             : {
     493             :         char path[PATH_MAX];
     494             :         const char* mode;
     495             :         char text_T[32];
     496             :         char text_D[32];
     497             :         time_t t;
     498             :         struct tm* tm;
     499             : #if HAVE_LOCALTIME_R
     500             :         struct tm tm_res;
     501             : #endif
     502             : 
     503             :         /* leave stdlog at 0 if not specified */
     504         316 :         if (file == 0)
     505         235 :                 return;
     506             : 
     507          87 :         t = time(0);
     508             : #if HAVE_LOCALTIME_R
     509          87 :         tm = localtime_r(&t, &tm_res);
     510             : #else
     511             :         tm = localtime(&t);
     512             : #endif
     513          87 :         if (tm) {
     514          87 :                 strftime(text_T, sizeof(text_T), "%H%M%S", tm);
     515          87 :                 strftime(text_D, sizeof(text_T), "%Y%m%d", tm);
     516             :         } else {
     517             :                 /* LCOV_EXCL_START */
     518             :                 strcpy(text_T, "invalid");
     519             :                 strcpy(text_D, "invalid");
     520             :                 /* LCOV_EXCL_STOP */
     521             :         }
     522             : 
     523             :         /* file mode */
     524          87 :         mode = "wt";
     525          87 :         if (*file == '>') {
     526           7 :                 ++file;
     527             : 
     528           7 :                 if (*file == '>') {
     529           1 :                         mode = "at";
     530           1 :                         ++file;
     531             :                 }
     532             : 
     533           7 :                 if (file[0] == '&' && file[1] == '1') {
     534           5 :                         stdlog = stdout;
     535           5 :                         return;
     536             :                 }
     537             : 
     538           2 :                 if (file[0] == '&' && file[1] == '2') {
     539           1 :                         stdlog = stderr;
     540           1 :                         return;
     541             :                 }
     542             :         }
     543             : 
     544             :         /* process the path */
     545         927 :         for (*path = 0; *file != 0; ) {
     546         846 :                 switch (*file) {
     547           2 :                 case '%' :
     548           2 :                         ++file;
     549           2 :                         switch (*file) {
     550           0 :                         case '%' :
     551           0 :                                 pathcatc(path, sizeof(path), '%');
     552           0 :                                 break;
     553           1 :                         case 'T' :
     554           1 :                                 pathcat(path, sizeof(path), text_T);
     555           1 :                                 break;
     556           1 :                         case 'D' :
     557           1 :                                 pathcat(path, sizeof(path), text_D);
     558           1 :                                 break;
     559           0 :                         default :
     560             :                                 /* LCOV_EXCL_START */
     561             :                                 log_fatal(EUSER, "Invalid type specifier '%c' in the log file.\n", *file);
     562             :                                 exit(EXIT_FAILURE);
     563             :                                 /* LCOV_EXCL_STOP */
     564             :                         }
     565           2 :                         break;
     566         844 :                 default :
     567         844 :                         pathcatc(path, sizeof(path), *file);
     568         844 :                         break;
     569             :                 }
     570         846 :                 ++file;
     571             :         }
     572             : 
     573          81 :         stdlog = fopen(path, mode);
     574          81 :         if (!stdlog) {
     575             :                 /* LCOV_EXCL_START */
     576             :                 log_fatal(errno, "Error opening the log file '%s'. %s.\n", path, strerror(errno));
     577             :                 exit(EXIT_FAILURE);
     578             :                 /* LCOV_EXCL_STOP */
     579             :         }
     580             : }
     581             : 
     582         291 : void log_close(const char* file)
     583             : {
     584         291 :         if (stdlog != stdout && stdlog != stderr && stdlog != 0) {
     585          81 :                 if (fclose(stdlog) != 0) {
     586             :                         /* LCOV_EXCL_START */
     587             :                         log_fatal(errno, "Error closing the log file '%s'. %s.\n", file, strerror(errno));
     588             :                         exit(EXIT_FAILURE);
     589             :                         /* LCOV_EXCL_STOP */
     590             :                 }
     591             :         }
     592             : 
     593         291 :         stdlog = 0;
     594         291 : }
     595             : 
     596             : /****************************************************************************/
     597             : /* config */
     598             : 
     599         326 : void config(char* conf, size_t conf_size, const char* argv0)
     600             : {
     601             : #ifdef _WIN32
     602             :         char* slash;
     603             : 
     604             :         pathimport(conf, conf_size, argv0);
     605             : 
     606             :         slash = strrchr(conf, '/');
     607             :         if (slash) {
     608             :                 slash[1] = 0;
     609             :                 pathcat(conf, conf_size, PACKAGE ".conf");
     610             :         } else {
     611             :                 pathcpy(conf, conf_size, PACKAGE ".conf");
     612             :         }
     613             : #else
     614             :         (void)argv0;
     615             : 
     616             : #ifdef SYSCONFDIR
     617             :         /* if it exists, give precedence at sysconfdir, usually /usr/local/etc */
     618         326 :         if (access(SYSCONFDIR "/" PACKAGE ".conf", F_OK) == 0) {
     619           0 :                 pathcpy(conf, conf_size, SYSCONFDIR "/" PACKAGE ".conf");
     620             :         } else /* otherwise fallback to plain /etc */
     621             : #endif
     622             :         {
     623         326 :                 pathcpy(conf, conf_size, "/etc/" PACKAGE ".conf");
     624             :         }
     625             : #endif
     626         326 : }
     627             : 
     628             : /****************************************************************************/
     629             : /* main */
     630             : 
     631             : /**
     632             :  * Only long options
     633             :  */
     634             : #define OPT_NO_WARNINGS 500
     635             : #define OPT_GUI 501
     636             : #define OPT_GUI_VERBOSE 502
     637             : #define OPT_GUI_RESCAN_AFTER 503
     638             : #define OPT_GUI_TOUCH_BEFORE 504
     639             : #define OPT_GUI_THRESHOLD_REMOVES 505
     640             : #define OPT_GUI_THRESHOLD_UPDATES 506
     641             : 
     642             : /**
     643             :  * Test options
     644             :  */
     645             : #define OPT_TEST_SKIP_SELF 256
     646             : #define OPT_TEST_KILL_AFTER_SYNC 257
     647             : #define OPT_TEST_EXPECT_UNRECOVERABLE 258
     648             : #define OPT_TEST_EXPECT_RECOVERABLE 259
     649             : #define OPT_TEST_SKIP_SIGN 260
     650             : #define OPT_TEST_SKIP_FALLOCATE 261
     651             : #define OPT_TEST_SKIP_DEVICE 262
     652             : #define OPT_TEST_FORCE_MURMUR3 264
     653             : #define OPT_TEST_FORCE_SPOOKY2 265
     654             : #define OPT_TEST_SKIP_LOCK 266
     655             : #define OPT_TEST_FORCE_ORDER_PHYSICAL 267
     656             : #define OPT_TEST_FORCE_ORDER_INODE 268
     657             : #define OPT_TEST_FORCE_ORDER_ALPHA 269
     658             : #define OPT_TEST_FORCE_ORDER_DIR 270
     659             : #define OPT_TEST_FORCE_SCRUB_AT 271
     660             : #define OPT_TEST_FORCE_SCRUB_EVEN 272
     661             : #define OPT_TEST_FORCE_CONTENT_WRITE 273
     662             : #define OPT_TEST_SKIP_CONTENT_CHECK 275
     663             : #define OPT_TEST_SKIP_PARITY_ACCESS 276
     664             : #define OPT_TEST_EXPECT_FAILURE 277
     665             : #define OPT_TEST_RUN 278
     666             : #define OPT_TEST_FORCE_SCAN_WINFIND 279
     667             : #define OPT_TEST_IMPORT_CONTENT 280
     668             : #define OPT_TEST_FORCE_PROGRESS 281
     669             : #define OPT_TEST_SKIP_DISK_ACCESS 282
     670             : #define OPT_TEST_FORCE_AUTOSAVE_AT 283
     671             : #define OPT_TEST_FAKE_DEVICE 284
     672             : #define OPT_TEST_EXPECT_NEED_SYNC 285
     673             : #define OPT_TEST_FAKE_UUID 287
     674             : #define OPT_TEST_MATCH_FIRST_UUID 288
     675             : #define OPT_TEST_FORCE_PARITY_UPDATE 289
     676             : #define OPT_TEST_IO_CACHE 290
     677             : #define OPT_TEST_IO_STATS 291
     678             : #define OPT_TEST_COND_SIGNAL_OUTSIDE 292
     679             : #define OPT_TEST_IO_ADVISE_NONE 293
     680             : #define OPT_TEST_IO_ADVISE_SEQUENTIAL 294
     681             : #define OPT_TEST_IO_ADVISE_FLUSH 295
     682             : #define OPT_TEST_IO_ADVISE_FLUSH_WINDOW 296
     683             : #define OPT_TEST_IO_ADVISE_DISCARD 297
     684             : #define OPT_TEST_IO_ADVISE_DISCARD_WINDOW 298
     685             : #define OPT_TEST_IO_ADVISE_DIRECT 299
     686             : #define OPT_TEST_PARITY_LIMIT 301
     687             : #define OPT_TEST_SKIP_CONTENT_WRITE 302
     688             : #define OPT_TEST_SKIP_SPACE_HOLDER 303
     689             : #define OPT_TEST_FORMAT 304
     690             : #define OPT_TEST_SKIP_MULTI_SCAN 305
     691             : #define OPT_TEST_SPEED_PERIOD 307
     692             : #define OPT_TEST_SPEED_DISKS_NUMBER 308
     693             : #define OPT_TEST_SPEED_BLOCKS_SIZE 309
     694             : #define OPT_TEST_KILL_BEFORE_SYNC 310
     695             : 
     696             : 
     697             : #if HAVE_GETOPT_LONG
     698             : static struct option long_options[] = {
     699             :         { "conf", 1, 0, 'c' },
     700             :         { "filter", 1, 0, 'f' },
     701             :         { "filter-disk", 1, 0, 'd' },
     702             :         { "filter-missing", 0, 0, 'm' },
     703             :         { "filter-error", 0, 0, 'e' },
     704             :         { "filter-block-error", 0, 0, 'b' },
     705             :         { "percentage", 1, 0, 'p' }, /* legacy name for --plan */
     706             :         { "plan", 1, 0, 'p' },
     707             :         { "older-than", 1, 0, 'o' },
     708             :         { "start", 1, 0, 'S' },
     709             :         { "count", 1, 0, 'B' },
     710             :         { "error-limit", 1, 0, 'L' },
     711             :         { "import", 1, 0, 'i' },
     712             :         { "log", 1, 0, 'l' },
     713             :         { "stats", 0, 0, 'A' },
     714             :         { "force-zero", 0, 0, 'Z' },
     715             :         { "force-empty", 0, 0, 'E' },
     716             :         { "force-uuid", 0, 0, 'U' },
     717             :         { "force-device", 0, 0, 'D' },
     718             :         { "force-nocopy", 0, 0, 'N' },
     719             :         { "force-full", 0, 0, 'F' },
     720             :         { "force-realloc", 0, 0, 'R' },
     721             :         { "force-realloc-tail", 1, 0, 'W' },
     722             :         { "bw-limit", 1, 0, 'w' },
     723             :         { "audit-only", 0, 0, 'a' },
     724             :         { "pre-hash", 0, 0, 'h' },
     725             :         { "tail", 1, 0, 't' },
     726             :         { "speed-test", 0, 0, 'T' }, /* undocumented speed test command */
     727             :         { "speed-test-period", 1, 0, OPT_TEST_SPEED_PERIOD }, /* for how many milliseconds test each feature. Default 1000. */
     728             :         { "speed-test-disks-number", 1, 0, OPT_TEST_SPEED_DISKS_NUMBER }, /* how many disk number uses in the speed test. Default 8. */
     729             :         { "speed-test-blocks-size", 1, 0, OPT_TEST_SPEED_BLOCKS_SIZE }, /* how big in kBytes should be the blocks in the speed test. Default 256. */
     730             :         { "gen-conf", 1, 0, 'C' },
     731             :         { "verbose", 0, 0, 'v' },
     732             :         { "quiet", 0, 0, 'q' },
     733             :         { "help", 0, 0, 'H' },
     734             :         { "version", 0, 0, 'V' },
     735             : 
     736             : 
     737             :         { "no-warnings", 0, 0, OPT_NO_WARNINGS }, /* disable annoying warnings */
     738             :         { "gui", 0, 0, OPT_GUI }, /* undocumented GUI interface option (it was also 'G' in the past) */
     739             :         { "gui-verbose", 0, 0, OPT_GUI_VERBOSE }, /* undocumented GUI interface option */
     740             :         { "gui-rescan-after", 0, 0, OPT_GUI_RESCAN_AFTER }, /* undocumented GUI, force a rescan after the command to log differences */
     741             :         { "gui-touch-before", 0, 0, OPT_GUI_TOUCH_BEFORE }, /* undocumented GUI, force a touch before the command */
     742             :         { "gui-threshold-removes", 1, 0, OPT_GUI_THRESHOLD_REMOVES }, /* undocumented GUI, abort sync if too many removed files */
     743             :         { "gui-threshold-updates", 1, 0, OPT_GUI_THRESHOLD_UPDATES }, /* undocumented GUI, abort sync if too many updated files */
     744             : 
     745             :         /* The following are test specific options, DO NOT USE! */
     746             : 
     747             :         /* After syncing, do not write the new content file */
     748             :         { "test-kill-after-sync", 0, 0, OPT_TEST_KILL_AFTER_SYNC },
     749             : 
     750             :         /* Do not execure syncing after writing the new initial content file */
     751             :         { "test-kill-before-sync", 0, 0, OPT_TEST_KILL_BEFORE_SYNC },
     752             : 
     753             :         /* Exit with failure if after check/fix there ARE NOT unrecoverable errors. */
     754             :         { "test-expect-unrecoverable", 0, 0, OPT_TEST_EXPECT_UNRECOVERABLE },
     755             : 
     756             :         /* Exit with failure if after check/fix there ARE NOT recoverable errors. */
     757             :         { "test-expect-recoverable", 0, 0, OPT_TEST_EXPECT_RECOVERABLE },
     758             : 
     759             :         /* Skip the initial self test */
     760             :         { "test-skip-self", 0, 0, OPT_TEST_SKIP_SELF },
     761             : 
     762             :         /* Skip the initial sign check when reading the content file */
     763             :         { "test-skip-sign", 0, 0, OPT_TEST_SKIP_SIGN },
     764             : 
     765             :         /* Skip the fallocate() when growing the parity files */
     766             :         { "test-skip-fallocate", 0, 0, OPT_TEST_SKIP_FALLOCATE },
     767             : 
     768             :         /* Skip the device check */
     769             :         { "test-skip-device", 0, 0, OPT_TEST_SKIP_DEVICE },
     770             : 
     771             :         /* Force Murmur3 hash */
     772             :         { "test-force-murmur3", 0, 0, OPT_TEST_FORCE_MURMUR3 },
     773             : 
     774             :         /* Force Spooky2 hash */
     775             :         { "test-force-spooky2", 0, 0, OPT_TEST_FORCE_SPOOKY2 },
     776             : 
     777             :         /* Skip the use of lock file */
     778             :         { "test-skip-lock", 0, 0, OPT_TEST_SKIP_LOCK },
     779             : 
     780             :         /* Force a sort order for files */
     781             :         { "test-force-order-physical", 0, 0, OPT_TEST_FORCE_ORDER_PHYSICAL },
     782             :         { "test-force-order-inode", 0, 0, OPT_TEST_FORCE_ORDER_INODE },
     783             :         { "test-force-order-alpha", 0, 0, OPT_TEST_FORCE_ORDER_ALPHA },
     784             :         { "test-force-order-dir", 0, 0, OPT_TEST_FORCE_ORDER_DIR },
     785             : 
     786             :         /* Force scrub of the specified number of blocks */
     787             :         { "test-force-scrub-at", 1, 0, OPT_TEST_FORCE_SCRUB_AT },
     788             : 
     789             :         /* Force scrub of all the even blocks. This is really for testing, don't try it */
     790             :         { "test-force-scrub-even", 0, 0, OPT_TEST_FORCE_SCRUB_EVEN },
     791             : 
     792             :         /* Force write of the content file even if no modification is done */
     793             :         { "test-force-content-write", 0, 0, OPT_TEST_FORCE_CONTENT_WRITE },
     794             : 
     795             :         /* Relax the checks done at the content file */
     796             :         { "test-skip-content-check", 0, 0, OPT_TEST_SKIP_CONTENT_CHECK },
     797             : 
     798             :         /* Skip the parity access */
     799             :         { "test-skip-parity-access", 0, 0, OPT_TEST_SKIP_PARITY_ACCESS },
     800             : 
     801             :         /* Exit generic failure */
     802             :         { "test-expect-failure", 0, 0, OPT_TEST_EXPECT_FAILURE },
     803             : 
     804             :         /* Exit generic need sync */
     805             :         { "test-expect-need-sync", 0, 0, OPT_TEST_EXPECT_NEED_SYNC },
     806             : 
     807             :         /* Run some command after loading the state and before the command */
     808             :         { "test-run", 1, 0, OPT_TEST_RUN },
     809             : 
     810             :         /* Use the FindFirst/Next approach in Windows to list files */
     811             :         { "test-force-scan-winfind", 0, 0, OPT_TEST_FORCE_SCAN_WINFIND },
     812             : 
     813             :         /* Alternative import working by data */
     814             :         { "test-import-content", 1, 0, OPT_TEST_IMPORT_CONTENT },
     815             : 
     816             :         /* Force immediate progress state update */
     817             :         { "test-force-progress", 0, 0, OPT_TEST_FORCE_PROGRESS },
     818             : 
     819             :         /* Skip the disk access */
     820             :         { "test-skip-disk-access", 0, 0, OPT_TEST_SKIP_DISK_ACCESS },
     821             : 
     822             :         /* Force autosave at the specified block */
     823             :         { "test-force-autosave-at", 1, 0, OPT_TEST_FORCE_AUTOSAVE_AT },
     824             : 
     825             :         /* Fake device data */
     826             :         { "test-fake-device", 0, 0, OPT_TEST_FAKE_DEVICE },
     827             : 
     828             :         /* Fake UUID */
     829             :         { "test-fake-uuid", 0, 0, OPT_TEST_FAKE_UUID },
     830             : 
     831             :         /* Match first UUID */
     832             :         { "test-match-first-uuid", 0, 0, OPT_TEST_MATCH_FIRST_UUID },
     833             : 
     834             :         /* Force parity update even if all the data hash is already matching */
     835             :         { "test-force-parity-update", 0, 0, OPT_TEST_FORCE_PARITY_UPDATE },
     836             : 
     837             :         /* Number of IO buffers */
     838             :         { "test-io-cache", 1, 0, OPT_TEST_IO_CACHE },
     839             : 
     840             :         /* Print IO stats */
     841             :         { "test-io-stats", 0, 0, OPT_TEST_IO_STATS }, /* now replaced by -A, --stats */
     842             : 
     843             :         /* Signal condition variable outside the mutex */
     844             :         { "test-cond-signal-outside", 0, 0, OPT_TEST_COND_SIGNAL_OUTSIDE },
     845             : 
     846             :         /* Set the io advise to none */
     847             :         { "test-io-advise-none", 0, 0, OPT_TEST_IO_ADVISE_NONE },
     848             : 
     849             :         /* Set the io advise to sequential */
     850             :         { "test-io-advise-sequential", 0, 0, OPT_TEST_IO_ADVISE_SEQUENTIAL },
     851             : 
     852             :         /* Set the io advise to flush */
     853             :         { "test-io-advise-flush", 0, 0, OPT_TEST_IO_ADVISE_FLUSH },
     854             : 
     855             :         /* Set the io advise to flush window */
     856             :         { "test-io-advise-flush-window", 0, 0, OPT_TEST_IO_ADVISE_FLUSH_WINDOW },
     857             : 
     858             :         /* Set the io advise to discard */
     859             :         { "test-io-advise-discard", 0, 0, OPT_TEST_IO_ADVISE_DISCARD },
     860             : 
     861             :         /* Set the io advise to discard window */
     862             :         { "test-io-advise-discard-window", 0, 0, OPT_TEST_IO_ADVISE_DISCARD_WINDOW },
     863             : 
     864             :         /* Set the io advise to direct */
     865             :         { "test-io-advise-direct", 0, 0, OPT_TEST_IO_ADVISE_DIRECT },
     866             : 
     867             :         /* Set an artificial parity limit */
     868             :         { "test-parity-limit", 1, 0, OPT_TEST_PARITY_LIMIT },
     869             : 
     870             :         /* Skip content write */
     871             :         { "test-skip-content-write", 0, 0, OPT_TEST_SKIP_CONTENT_WRITE },
     872             : 
     873             :         /* Skip space holder file in parity disks */
     874             :         { "test-skip-space-holder", 0, 0, OPT_TEST_SKIP_SPACE_HOLDER },
     875             : 
     876             :         /* Set the output format */
     877             :         { "test-fmt", 1, 0, OPT_TEST_FORMAT },
     878             : 
     879             :         /* Skip thread in disk scan */
     880             :         { "test-skip-multi-scan", 0, 0, OPT_TEST_SKIP_MULTI_SCAN },
     881             : 
     882             :         { 0, 0, 0, 0 }
     883             : };
     884             : #endif
     885             : 
     886             : /*
     887             :  * Free letters: gIjJkKMnPQruxXz
     888             :  *
     889             :  * The 's' letter is used in main.c
     890             :  * The 'G' letter is free but only from 14.0
     891             :  */
     892             : #define OPTIONS "t:c:f:d:mebp:o:S:B:L:i:l:AZEUDNFRW:ahTC:vqHVw:"
     893             : 
     894           6 : int parse_option_size(const char* arg, uint64_t* out_size)
     895             : {
     896             :         char* e;
     897             : 
     898             :         /* parse the number part */
     899           6 :         data_off_t size = strtoul(arg, &e, 10);
     900           6 :         if (e == arg)
     901           0 :                 return -1;
     902             : 
     903             :         /* Handle suffixes */
     904           6 :         if ((e[0] == 'k' || e[0] == 'K') && e[1] == 0) {
     905           5 :                 size *= KILO;
     906           1 :         } else if ((e[0] == 'm' || e[0] == 'M') && e[1] == 0) {
     907           1 :                 size *= MEGA;
     908           0 :         } else if ((e[0] == 'g' || e[0] == 'G') && e[1] == 0) {
     909           0 :                 size *= GIGA;
     910           0 :         } else if ((e[0] == 't' || e[0] == 'T') && e[1] == 0) {
     911           0 :                 size *= TERA;
     912           0 :         } else if (e[0] != 0) {
     913           0 :                 return -1;
     914             :         }
     915             : 
     916           6 :         *out_size = size;
     917           6 :         return 0;
     918             : }
     919             : 
     920             : volatile int global_interrupt = 0;
     921             : 
     922             : /* LCOV_EXCL_START */
     923             : void signal_handler(int signum)
     924             : {
     925             :         /* report the request of interruption with the signal received */
     926             :         global_interrupt = signum;
     927             : }
     928             : /* LCOV_EXCL_STOP */
     929             : 
     930         258 : void signal_init(void)
     931             : {
     932             : #if HAVE_SIGACTION
     933             :         struct sigaction sa;
     934             : 
     935         258 :         sa.sa_handler = signal_handler;
     936         258 :         sigemptyset(&sa.sa_mask);
     937             : 
     938             :         /* use the SA_RESTART to automatically restart interrupted system calls */
     939         258 :         sa.sa_flags = SA_RESTART;
     940             : 
     941         258 :         sigaction(SIGHUP, &sa, 0);
     942         258 :         sigaction(SIGTERM, &sa, 0);
     943         258 :         sigaction(SIGINT, &sa, 0);
     944             : #else
     945             :         signal(SIGINT, signal_handler);
     946             : #endif
     947         258 : }
     948             : 
     949             : #define OPERATION_DIFF 0
     950             : #define OPERATION_SYNC 1
     951             : #define OPERATION_CHECK 2
     952             : #define OPERATION_FIX 3
     953             : #define OPERATION_DRY 4
     954             : #define OPERATION_DUP 5
     955             : #define OPERATION_LIST 6
     956             : #define OPERATION_POOL 7
     957             : #define OPERATION_REHASH 8
     958             : #define OPERATION_SCRUB 9
     959             : #define OPERATION_STATUS 10
     960             : #define OPERATION_REWRITE 11
     961             : #define OPERATION_READ 12
     962             : #define OPERATION_TOUCH 13
     963             : #define OPERATION_SPINUP 14
     964             : #define OPERATION_SPINDOWN 15
     965             : #define OPERATION_DEVICES 16
     966             : #define OPERATION_SMART 17
     967             : #define OPERATION_PROBE 18
     968             : #define OPERATION_LOCATE 19
     969             : #define OPERATION_SPINDOWNIFUP 20
     970             : 
     971         327 : int snapraid_main(int argc, char* argv[])
     972             : {
     973             :         char esc_buffer[ESC_MAX];
     974             :         int c;
     975             :         struct snapraid_option opt;
     976             :         char conf[PATH_MAX];
     977             :         struct snapraid_state state;
     978             :         int operation;
     979             :         block_off_t blockstart;
     980             :         block_off_t blockcount;
     981             :         int ret;
     982             :         tommy_list filterlist_file;
     983             :         tommy_list filterlist_disk;
     984             :         int filter_missing;
     985             :         int filter_error;
     986             :         int plan100;
     987             :         int olderthan;
     988             :         char* e;
     989             :         const char* command;
     990             :         const char* import_timestamp;
     991             :         const char* import_content;
     992             :         const char* log_file;
     993             :         int lock;
     994             :         const char* gen_conf;
     995             :         const char* run;
     996             :         int speedtest;
     997             :         int speed_test_period;
     998             :         int speed_test_disks_number;
     999             :         int speed_test_blocks_size;
    1000             :         time_t t;
    1001             :         struct tm* tm;
    1002             : #if HAVE_LOCALTIME_R
    1003             :         struct tm tm_res;
    1004             : #endif
    1005             :         int i;
    1006             : 
    1007         327 :         test(argc, argv);
    1008             : 
    1009         326 :         lock_init();
    1010             : 
    1011             :         /* defaults */
    1012         326 :         config(conf, sizeof(conf), argv[0]);
    1013         326 :         memset(&opt, 0, sizeof(opt));
    1014         326 :         opt.io_error_limit = 100;
    1015         326 :         blockstart = 0;
    1016         326 :         blockcount = 0;
    1017         326 :         tommy_list_init(&filterlist_file);
    1018         326 :         tommy_list_init(&filterlist_disk);
    1019         326 :         speed_test_period = -1;
    1020         326 :         speed_test_blocks_size = -1;
    1021         326 :         speed_test_disks_number = -1;
    1022         326 :         filter_missing = 0;
    1023         326 :         filter_error = 0;
    1024         326 :         plan100 = SCRUB_AUTO;
    1025         326 :         olderthan = SCRUB_AUTO;
    1026         326 :         import_timestamp = 0;
    1027         326 :         import_content = 0;
    1028         326 :         log_file = 0;
    1029         326 :         lock = 0;
    1030         326 :         gen_conf = 0;
    1031         326 :         speedtest = 0;
    1032         326 :         run = 0;
    1033             : 
    1034         326 :         opterr = 0;
    1035         326 :         while ((c =
    1036             : #if HAVE_GETOPT_LONG
    1037        3947 :                 getopt_long(argc, argv, OPTIONS, long_options, 0))
    1038             : #else
    1039             :                 getopt(argc, argv, OPTIONS))
    1040             : #endif
    1041        3947 :                 != EOF) {
    1042        3625 :                 switch (c) {
    1043         311 :                 case 'c' :
    1044         311 :                         pathimport(conf, sizeof(conf), optarg);
    1045         311 :                         break;
    1046           5 :                 case 'f' : {
    1047           5 :                         struct snapraid_filter* filter = filter_alloc_file(1, "", optarg);
    1048           5 :                         if (!filter) {
    1049             :                                 /* LCOV_EXCL_START */
    1050             :                                 log_fatal(EUSER, "Invalid filter specification '%s'\n", optarg);
    1051             :                                 exit(EXIT_FAILURE);
    1052             :                                 /* LCOV_EXCL_STOP */
    1053             :                         }
    1054           3 :                         tommy_list_insert_tail(&filterlist_file, &filter->node, filter);
    1055           3 :                 } break;
    1056          16 :                 case 'd' : {
    1057          16 :                         struct snapraid_filter* filter = filter_alloc_disk(1, optarg);
    1058          16 :                         if (!filter) {
    1059             :                                 /* LCOV_EXCL_START */
    1060             :                                 log_fatal(EUSER, "Invalid filter specification '%s'\n", optarg);
    1061             :                                 exit(EXIT_FAILURE);
    1062             :                                 /* LCOV_EXCL_STOP */
    1063             :                         }
    1064          16 :                         tommy_list_insert_tail(&filterlist_disk, &filter->node, filter);
    1065          16 :                 } break;
    1066           2 :                 case 'm' :
    1067           2 :                         filter_missing = 1;
    1068           2 :                         opt.expected_missing = 1;
    1069           2 :                         break;
    1070           2 :                 case 'e' :
    1071             :                         /*
    1072             :                          * When processing only error, we filter files
    1073             :                          * and we apply fixes only to synced ones
    1074             :                          */
    1075           2 :                         filter_error = 1;
    1076           2 :                         opt.badfileonly = 1;
    1077           2 :                         opt.syncedonly = 1;
    1078           2 :                         break;
    1079           0 :                 case 'b' :
    1080             :                         /*
    1081             :                          * When processing only block with error, we filter both files and blocks
    1082             :                          * and we apply fixes only to synced ones
    1083             :                          */
    1084           0 :                         filter_error = 1;
    1085           0 :                         opt.badfileonly = 1;
    1086           0 :                         opt.badblockonly = 1;
    1087           0 :                         opt.syncedonly = 1;
    1088           0 :                         break;
    1089           6 :                 case 'p' :
    1090           6 :                         if (strcmp(optarg, "bad") == 0) {
    1091           1 :                                 plan100 = SCRUB_BAD;
    1092           5 :                         } else if (strcmp(optarg, "new") == 0) {
    1093           1 :                                 plan100 = SCRUB_NEW;
    1094           4 :                         } else if (strcmp(optarg, "full") == 0) {
    1095           3 :                                 plan100 = SCRUB_FULL;
    1096             :                         } else {
    1097           1 :                                 double plan_double = strtod(optarg, &e);
    1098           1 :                                 if (e == optarg
    1099           1 :                                         || *e != 0
    1100           1 :                                         || !isfinite(plan_double)
    1101           1 :                                         || plan_double < 0
    1102           1 :                                         || plan_double > 100
    1103             :                                 ) {
    1104             :                                         /* LCOV_EXCL_START */
    1105             :                                         log_fatal(EUSER, "Invalid plan/percentage '%s'\n", optarg);
    1106             :                                         exit(EXIT_FAILURE);
    1107             :                                         /* LCOV_EXCL_STOP */
    1108             :                                 }
    1109           1 :                                 plan100 = plan_double * 100;
    1110             :                         }
    1111           6 :                         break;
    1112           1 :                 case 'o' :
    1113           1 :                         olderthan = strtoul(optarg, &e, 10);
    1114           1 :                         if (e == optarg || *e || olderthan > 1000) {
    1115             :                                 /* LCOV_EXCL_START */
    1116             :                                 log_fatal(EUSER, "Invalid number of days '%s'\n", optarg);
    1117             :                                 exit(EXIT_FAILURE);
    1118             :                                 /* LCOV_EXCL_STOP */
    1119             :                         }
    1120           1 :                         break;
    1121           1 :                 case 'w' : /* --bw-limit */
    1122           1 :                         if (optarg == 0) {
    1123             :                                 /* LCOV_EXCL_START */
    1124             :                                 log_fatal(EUSER, "Missing bandwidth limit\n");
    1125             :                                 exit(EXIT_FAILURE);
    1126             :                                 /* LCOV_EXCL_STOP */
    1127             :                         }
    1128             : 
    1129           1 :                         if (parse_option_size(optarg, &opt.bwlimit) != 0) {
    1130             :                                 /* LCOV_EXCL_START */
    1131             :                                 log_fatal(EUSER, "Invalid bandwidth limit '%s'\n", optarg);
    1132             :                                 exit(EXIT_FAILURE);
    1133             :                                 /* LCOV_EXCL_STOP */
    1134             :                         }
    1135           1 :                         break;
    1136           2 :                 case 'S' :
    1137           2 :                         blockstart = strtoul(optarg, &e, 0);
    1138           2 :                         if (e == optarg || *e) {
    1139             :                                 /* LCOV_EXCL_START */
    1140             :                                 log_fatal(EUSER, "Invalid start position '%s'\n", optarg);
    1141             :                                 exit(EXIT_FAILURE);
    1142             :                                 /* LCOV_EXCL_STOP */
    1143             :                         }
    1144           2 :                         break;
    1145           7 :                 case 'B' :
    1146           7 :                         blockcount = strtoul(optarg, &e, 0);
    1147           7 :                         if (e == optarg || *e) {
    1148             :                                 /* LCOV_EXCL_START */
    1149             :                                 log_fatal(EUSER, "Invalid count number '%s'\n", optarg);
    1150             :                                 exit(EXIT_FAILURE);
    1151             :                                 /* LCOV_EXCL_STOP */
    1152             :                         }
    1153           7 :                         break;
    1154           0 :                 case 'L' :
    1155           0 :                         opt.io_error_limit = strtoul(optarg, &e, 0);
    1156           0 :                         if (e == optarg || *e) {
    1157             :                                 /* LCOV_EXCL_START */
    1158             :                                 log_fatal(EUSER, "Invalid error limit number '%s'\n", optarg);
    1159             :                                 exit(EXIT_FAILURE);
    1160             :                                 /* LCOV_EXCL_STOP */
    1161             :                         }
    1162           0 :                         break;
    1163           1 :                 case 'i' :
    1164           1 :                         if (import_timestamp) {
    1165             :                                 /* LCOV_EXCL_START */
    1166             :                                 log_fatal(EUSER, "Import directory '%s' already specified as '%s'\n", optarg, import_timestamp);
    1167             :                                 exit(EXIT_FAILURE);
    1168             :                                 /* LCOV_EXCL_STOP */
    1169             :                         }
    1170           1 :                         import_timestamp = optarg;
    1171           1 :                         break;
    1172           4 :                 case 't' :
    1173           4 :                         if (parse_option_size(optarg, &opt.parity_tail) != 0) {
    1174             :                                 /* LCOV_EXCL_START */
    1175             :                                 log_fatal(EUSER, "Invalid tail size '%s'\n", optarg);
    1176             :                                 exit(EXIT_FAILURE);
    1177             :                                 /* LCOV_EXCL_STOP */
    1178             :                         }
    1179           4 :                         break;
    1180           1 :                 case OPT_TEST_IMPORT_CONTENT :
    1181           1 :                         if (import_content) {
    1182             :                                 /* LCOV_EXCL_START */
    1183             :                                 log_fatal(EUSER, "Import directory '%s' already specified as '%s'\n", optarg, import_content);
    1184             :                                 exit(EXIT_FAILURE);
    1185             :                                 /* LCOV_EXCL_STOP */
    1186             :                         }
    1187           1 :                         import_content = optarg;
    1188           1 :                         break;
    1189          87 :                 case 'l' :
    1190          87 :                         if (log_file) {
    1191             :                                 /* LCOV_EXCL_START */
    1192             :                                 log_fatal(EUSER, "Log file '%s' already specified as '%s'\n", optarg, log_file);
    1193             :                                 exit(EXIT_FAILURE);
    1194             :                                 /* LCOV_EXCL_STOP */
    1195             :                         }
    1196          87 :                         log_file = optarg;
    1197          87 :                         break;
    1198           0 :                 case 'A' :
    1199           0 :                         opt.force_stats = 1;
    1200           0 :                         break;
    1201           0 :                 case 'Z' :
    1202           0 :                         opt.force_zero = 1;
    1203           0 :                         break;
    1204           6 :                 case 'E' :
    1205           6 :                         opt.force_empty = 1;
    1206           6 :                         break;
    1207           0 :                 case 'U' :
    1208           0 :                         opt.force_uuid = 1;
    1209           0 :                         break;
    1210           1 :                 case 'D' :
    1211           1 :                         opt.force_device = 1;
    1212           1 :                         break;
    1213           1 :                 case 'N' :
    1214           1 :                         opt.force_nocopy = 1;
    1215           1 :                         break;
    1216          11 :                 case 'F' :
    1217          11 :                         opt.force_full = 1;
    1218          11 :                         break;
    1219           1 :                 case 'R' :
    1220           1 :                         opt.force_realloc = 1;
    1221           1 :                         break;
    1222           1 :                 case 'W' :
    1223           1 :                         opt.force_realloc = 1;
    1224           1 :                         if (parse_option_size(optarg, &opt.parity_tail) != 0) {
    1225             :                                 /* LCOV_EXCL_START */
    1226             :                                 log_fatal(EUSER, "Invalid tail size '%s'\n", optarg);
    1227             :                                 exit(EXIT_FAILURE);
    1228             :                                 /* LCOV_EXCL_STOP */
    1229             :                         }
    1230           1 :                         break;
    1231           6 :                 case 'a' :
    1232           6 :                         opt.auditonly = 1;
    1233           6 :                         break;
    1234           7 :                 case 'h' :
    1235           7 :                         opt.prehash = 1;
    1236           7 :                         break;
    1237          28 :                 case 'v' :
    1238          28 :                         ++msg_level;
    1239          28 :                         break;
    1240         849 :                 case 'q' :
    1241         849 :                         --msg_level;
    1242         849 :                         break;
    1243         288 :                 case OPT_NO_WARNINGS :
    1244         288 :                         opt.no_warnings = 1;
    1245         288 :                         break;
    1246          28 :                 case OPT_GUI :
    1247          28 :                         opt.gui = 1;
    1248          28 :                         break;
    1249           0 :                 case OPT_GUI_VERBOSE :
    1250           0 :                         opt.gui = 1;
    1251           0 :                         opt.gui_verbose = 1;
    1252           0 :                         break;
    1253           0 :                 case OPT_GUI_RESCAN_AFTER :
    1254           0 :                         opt.gui_rescan_after = 1;
    1255           0 :                         break;
    1256           0 :                 case OPT_GUI_TOUCH_BEFORE :
    1257           0 :                         opt.gui_touch_before = 1;
    1258           0 :                         break;
    1259           0 :                 case OPT_GUI_THRESHOLD_REMOVES :
    1260           0 :                         opt.gui_threshold_removes = strtoul(optarg, &e, 0);
    1261           0 :                         if (e == optarg || *e) {
    1262             :                                 /* LCOV_EXCL_START */
    1263             :                                 log_fatal(EUSER, "Invalid threshold '%s'\n", optarg);
    1264             :                                 exit(EXIT_FAILURE);
    1265             :                                 /* LCOV_EXCL_STOP */
    1266             :                         }
    1267           0 :                         break;
    1268           0 :                 case OPT_GUI_THRESHOLD_UPDATES :
    1269           0 :                         opt.gui_threshold_updates = strtoul(optarg, &e, 0);
    1270           0 :                         if (e == optarg || *e) {
    1271             :                                 /* LCOV_EXCL_START */
    1272             :                                 log_fatal(EUSER, "Invalid threshold '%s'\n", optarg);
    1273             :                                 exit(EXIT_FAILURE);
    1274             :                                 /* LCOV_EXCL_STOP */
    1275             :                         }
    1276           0 :                         break;
    1277           1 :                 case 'H' :
    1278           1 :                         usage(conf);
    1279           1 :                         exit(EXIT_SUCCESS);
    1280           1 :                 case 'V' :
    1281           1 :                         version();
    1282           1 :                         exit(EXIT_SUCCESS);
    1283           5 :                 case 'T' :
    1284           5 :                         speedtest = 1;
    1285           5 :                         break;
    1286           0 :                 case OPT_TEST_SPEED_PERIOD :
    1287           0 :                         speed_test_period = atoi(optarg);
    1288           0 :                         break;
    1289           0 :                 case OPT_TEST_SPEED_DISKS_NUMBER :
    1290           0 :                         speed_test_disks_number = atoi(optarg);
    1291           0 :                         break;
    1292           0 :                 case OPT_TEST_SPEED_BLOCKS_SIZE :
    1293           0 :                         speed_test_blocks_size = atoi(optarg);
    1294           0 :                         break;
    1295           1 :                 case 'C' :
    1296           1 :                         gen_conf = optarg;
    1297           1 :                         break;
    1298          12 :                 case OPT_TEST_KILL_AFTER_SYNC :
    1299          12 :                         opt.kill_after_sync = 1;
    1300          12 :                         break;
    1301           2 :                 case OPT_TEST_KILL_BEFORE_SYNC :
    1302           2 :                         opt.kill_before_sync = 1;
    1303           2 :                         break;
    1304          13 :                 case OPT_TEST_EXPECT_UNRECOVERABLE :
    1305          13 :                         opt.expect_unrecoverable = 1;
    1306          13 :                         break;
    1307          24 :                 case OPT_TEST_EXPECT_RECOVERABLE :
    1308          24 :                         opt.expect_recoverable = 1;
    1309          24 :                         break;
    1310         311 :                 case OPT_TEST_SKIP_SELF :
    1311         311 :                         opt.skip_self = 1;
    1312         311 :                         break;
    1313           0 :                 case OPT_TEST_SKIP_SIGN :
    1314           0 :                         opt.skip_sign = 1;
    1315           0 :                         break;
    1316           1 :                 case OPT_TEST_SKIP_FALLOCATE :
    1317           1 :                         opt.skip_fallocate = 1;
    1318           1 :                         break;
    1319         319 :                 case OPT_TEST_SKIP_DEVICE :
    1320         319 :                         opt.skip_device = 1;
    1321         319 :                         speed_test_period = 50; /* reduce period of the speed test for running the make check */
    1322         319 :                         break;
    1323           0 :                 case OPT_TEST_SKIP_CONTENT_CHECK :
    1324           0 :                         opt.skip_content_check = 1;
    1325           0 :                         break;
    1326           0 :                 case OPT_TEST_SKIP_PARITY_ACCESS :
    1327           0 :                         opt.skip_parity_access = 1;
    1328           0 :                         break;
    1329           0 :                 case OPT_TEST_SKIP_DISK_ACCESS :
    1330           0 :                         opt.skip_disk_access = 1;
    1331           0 :                         break;
    1332           1 :                 case OPT_TEST_FORCE_MURMUR3 :
    1333           1 :                         opt.force_murmur3 = 1;
    1334           1 :                         break;
    1335           1 :                 case OPT_TEST_FORCE_SPOOKY2 :
    1336           1 :                         opt.force_spooky2 = 1;
    1337           1 :                         break;
    1338           0 :                 case OPT_TEST_SKIP_LOCK :
    1339           0 :                         opt.skip_lock = 1;
    1340           0 :                         break;
    1341           2 :                 case OPT_TEST_FORCE_ORDER_PHYSICAL :
    1342           2 :                         opt.force_order = SORT_PHYSICAL;
    1343           2 :                         break;
    1344           0 :                 case OPT_TEST_FORCE_ORDER_INODE :
    1345           0 :                         opt.force_order = SORT_INODE;
    1346           0 :                         break;
    1347         309 :                 case OPT_TEST_FORCE_ORDER_ALPHA :
    1348         309 :                         opt.force_order = SORT_ALPHA;
    1349         309 :                         break;
    1350           0 :                 case OPT_TEST_FORCE_ORDER_DIR :
    1351           0 :                         opt.force_order = SORT_DIR;
    1352           0 :                         break;
    1353           4 :                 case OPT_TEST_FORCE_SCRUB_AT :
    1354           4 :                         opt.force_scrub_at = atoi(optarg);
    1355           4 :                         break;
    1356           1 :                 case OPT_TEST_FORCE_SCRUB_EVEN :
    1357           1 :                         opt.force_scrub_even = 1;
    1358           1 :                         break;
    1359           0 :                 case OPT_TEST_FORCE_CONTENT_WRITE :
    1360           0 :                         opt.force_content_write = 1;
    1361           0 :                         break;
    1362          18 :                 case OPT_TEST_EXPECT_FAILURE :
    1363             :                         /* invert the exit codes */
    1364          18 :                         exit_success = 1;
    1365          18 :                         exit_failure = 0;
    1366          18 :                         break;
    1367           7 :                 case OPT_TEST_EXPECT_NEED_SYNC :
    1368             :                         /* invert the exit codes */
    1369           7 :                         exit_success = 1;
    1370           7 :                         exit_sync_needed = 0;
    1371           7 :                         break;
    1372          10 :                 case OPT_TEST_RUN :
    1373          10 :                         run = optarg;
    1374          10 :                         break;
    1375           0 :                 case OPT_TEST_FORCE_SCAN_WINFIND :
    1376           0 :                         opt.force_scan_winfind = 1;
    1377           0 :                         break;
    1378         288 :                 case OPT_TEST_FORCE_PROGRESS :
    1379         288 :                         opt.force_progress = 1;
    1380         288 :                         break;
    1381           1 :                 case OPT_TEST_FORCE_AUTOSAVE_AT :
    1382           1 :                         opt.force_autosave_at = atoi(optarg);
    1383           1 :                         break;
    1384           4 :                 case OPT_TEST_FAKE_DEVICE :
    1385           4 :                         opt.fake_device = 1;
    1386           4 :                         break;
    1387           1 :                 case OPT_TEST_FAKE_UUID :
    1388           1 :                         opt.fake_uuid = 2;
    1389           1 :                         break;
    1390           2 :                 case OPT_TEST_MATCH_FIRST_UUID :
    1391           2 :                         opt.match_first_uuid = 1;
    1392           2 :                         break;
    1393           0 :                 case OPT_TEST_FORCE_PARITY_UPDATE :
    1394           0 :                         opt.force_parity_update = 1;
    1395           0 :                         break;
    1396           2 :                 case OPT_TEST_IO_CACHE :
    1397           2 :                         opt.io_cache = atoi(optarg);
    1398           2 :                         if (opt.io_cache != 1 && (opt.io_cache < IO_MIN || opt.io_cache > IO_MAX)) {
    1399             :                                 /* LCOV_EXCL_START */
    1400             :                                 log_fatal(EUSER, "The IO cache should be between %u and %u.\n", IO_MIN, IO_MAX);
    1401             :                                 exit(EXIT_FAILURE);
    1402             :                                 /* LCOV_EXCL_STOP */
    1403             :                         }
    1404           2 :                         break;
    1405           1 :                 case OPT_TEST_IO_STATS :
    1406           1 :                         opt.force_stats = 1;
    1407           1 :                         break;
    1408           0 :                 case OPT_TEST_COND_SIGNAL_OUTSIDE :
    1409             : #if HAVE_THREAD
    1410           0 :                         thread_cond_signal_outside = 1;
    1411             : #endif
    1412           0 :                         break;
    1413         312 :                 case OPT_TEST_IO_ADVISE_NONE :
    1414         312 :                         opt.file_mode = ADVISE_NONE;
    1415         312 :                         break;
    1416           1 :                 case OPT_TEST_IO_ADVISE_SEQUENTIAL :
    1417           1 :                         opt.file_mode = ADVISE_SEQUENTIAL;
    1418           1 :                         break;
    1419           1 :                 case OPT_TEST_IO_ADVISE_FLUSH :
    1420           1 :                         opt.file_mode = ADVISE_FLUSH;
    1421           1 :                         break;
    1422           1 :                 case OPT_TEST_IO_ADVISE_FLUSH_WINDOW :
    1423           1 :                         opt.file_mode = ADVISE_FLUSH_WINDOW;
    1424           1 :                         break;
    1425           0 :                 case OPT_TEST_IO_ADVISE_DISCARD :
    1426           0 :                         opt.file_mode = ADVISE_DISCARD;
    1427           0 :                         break;
    1428           1 :                 case OPT_TEST_IO_ADVISE_DISCARD_WINDOW :
    1429           1 :                         opt.file_mode = ADVISE_DISCARD_WINDOW;
    1430           1 :                         break;
    1431           1 :                 case OPT_TEST_IO_ADVISE_DIRECT :
    1432           1 :                         opt.file_mode = ADVISE_DIRECT;
    1433           1 :                         break;
    1434         288 :                 case OPT_TEST_PARITY_LIMIT :
    1435         288 :                         opt.parity_limit_size = atoll(optarg);
    1436         288 :                         break;
    1437           0 :                 case OPT_TEST_SKIP_CONTENT_WRITE :
    1438           0 :                         opt.skip_content_write = 1;
    1439           0 :                         break;
    1440           0 :                 case OPT_TEST_SKIP_SPACE_HOLDER :
    1441           0 :                         opt.skip_space_holder = 1;
    1442           0 :                         break;
    1443           5 :                 case OPT_TEST_FORMAT :
    1444           5 :                         if (strcmp(optarg, "file") == 0)
    1445           2 :                                 FMT_MODE = FMT_FILE;
    1446           3 :                         else if (strcmp(optarg, "disk") == 0)
    1447           1 :                                 FMT_MODE = FMT_DISK;
    1448           2 :                         else if (strcmp(optarg, "path") == 0)
    1449           2 :                                 FMT_MODE = FMT_PATH;
    1450             :                         else {
    1451             :                                 /* LCOV_EXCL_START */
    1452             :                                 log_fatal(EUSER, "Unknown format '%s'\n", optarg);
    1453             :                                 exit(EXIT_FAILURE);
    1454             :                                 /* LCOV_EXCL_STOP */
    1455             :                         }
    1456           5 :                         break;
    1457           0 :                 case OPT_TEST_SKIP_MULTI_SCAN :
    1458           0 :                         opt.skip_multi_scan = 1;
    1459           0 :                         break;
    1460           0 :                 default :
    1461             :                         /* LCOV_EXCL_START */
    1462             :                         log_fatal(EUSER, "Unknown option '%c'\n", (char)c);
    1463             :                         exit(EXIT_FAILURE);
    1464             :                         /* LCOV_EXCL_STOP */
    1465             :                 }
    1466             :         }
    1467             : 
    1468         322 :         os_init(opt.force_scan_winfind);
    1469         322 :         raid_init();
    1470         322 :         crc32c_init();
    1471         322 :         random_reseed();
    1472             : 
    1473         322 :         if (speedtest != 0) {
    1474           5 :                 speed(speed_test_period, speed_test_disks_number, speed_test_blocks_size);
    1475           5 :                 os_done();
    1476           5 :                 exit(EXIT_SUCCESS);
    1477             :         }
    1478             : 
    1479         317 :         if (gen_conf != 0) {
    1480           1 :                 generate_configuration(gen_conf);
    1481           1 :                 os_done();
    1482           1 :                 exit(EXIT_SUCCESS);
    1483             :         }
    1484             : 
    1485         316 :         if (optind + 1 != argc) {
    1486             :                 /* LCOV_EXCL_START */
    1487             :                 usage(conf);
    1488             :                 exit(EXIT_FAILURE);
    1489             :                 /* LCOV_EXCL_STOP */
    1490             :         }
    1491             : 
    1492         316 :         command = argv[optind];
    1493         316 :         if (strcmp(command, "diff") == 0) {
    1494           8 :                 operation = OPERATION_DIFF;
    1495         308 :         } else if (strcmp(argv[optind], "sync") == 0) {
    1496         108 :                 operation = OPERATION_SYNC;
    1497         200 :         } else if (strcmp(argv[optind], "check") == 0) {
    1498          83 :                 operation = OPERATION_CHECK;
    1499         117 :         } else if (strcmp(argv[optind], "fix") == 0) {
    1500          52 :                 operation = OPERATION_FIX;
    1501          65 :         } else if (strcmp(argv[optind], "test-dry") == 0) {
    1502           3 :                 operation = OPERATION_DRY;
    1503          62 :         } else if (strcmp(argv[optind], "dup") == 0) {
    1504           3 :                 operation = OPERATION_DUP;
    1505          59 :         } else if (strcmp(argv[optind], "list") == 0) {
    1506           5 :                 operation = OPERATION_LIST;
    1507          54 :         } else if (strcmp(argv[optind], "pool") == 0) {
    1508           3 :                 operation = OPERATION_POOL;
    1509          51 :         } else if (strcmp(argv[optind], "rehash") == 0) {
    1510           1 :                 operation = OPERATION_REHASH;
    1511          50 :         } else if (strcmp(argv[optind], "scrub") == 0) {
    1512          12 :                 operation = OPERATION_SCRUB;
    1513          38 :         } else if (strcmp(argv[optind], "status") == 0) {
    1514          17 :                 operation = OPERATION_STATUS;
    1515          21 :         } else if (strcmp(argv[optind], "test-rewrite") == 0) {
    1516           1 :                 operation = OPERATION_REWRITE;
    1517          20 :         } else if (strcmp(argv[optind], "read") == 0) {
    1518           1 :                 operation = OPERATION_READ;
    1519          19 :         } else if (strcmp(argv[optind], "touch") == 0) {
    1520           1 :                 operation = OPERATION_TOUCH;
    1521          18 :         } else if (strcmp(argv[optind], "up") == 0) {
    1522           2 :                 operation = OPERATION_SPINUP;
    1523          16 :         } else if (strcmp(argv[optind], "down") == 0) {
    1524           2 :                 operation = OPERATION_SPINDOWN;
    1525          14 :         } else if (strcmp(argv[optind], "test-downifup") == 0) {
    1526           1 :                 operation = OPERATION_SPINDOWNIFUP;
    1527          13 :         } else if (strcmp(argv[optind], "devices") == 0) {
    1528           2 :                 operation = OPERATION_DEVICES;
    1529          11 :         } else if (strcmp(argv[optind], "smart") == 0) {
    1530           2 :                 operation = OPERATION_SMART;
    1531           9 :         } else if (strcmp(argv[optind], "probe") == 0) {
    1532           4 :                 operation = OPERATION_PROBE;
    1533           5 :         } else if (strcmp(argv[optind], "locate") == 0) {
    1534           5 :                 operation = OPERATION_LOCATE;
    1535             :         } else {
    1536             :                 /* LCOV_EXCL_START */
    1537             :                 log_fatal(EUSER, "Unknown command '%s'\n", argv[optind]);
    1538             :                 exit(EXIT_FAILURE);
    1539             :                 /* LCOV_EXCL_STOP */
    1540             :         }
    1541             : 
    1542             :         /* check options compatibility */
    1543         316 :         switch (operation) {
    1544          83 :         case OPERATION_CHECK :
    1545          83 :                 break;
    1546         233 :         default :
    1547         233 :                 if (opt.auditonly) {
    1548             :                         /* LCOV_EXCL_START */
    1549             :                         log_fatal(EUSER, "You cannot use -a, --audit-only with the '%s' command\n", command);
    1550             :                         exit(EXIT_FAILURE);
    1551             :                         /* LCOV_EXCL_STOP */
    1552             :                 }
    1553             :         }
    1554             : 
    1555         316 :         switch (operation) {
    1556         148 :         case OPERATION_FIX :
    1557             :         case OPERATION_CHECK :
    1558             :         case OPERATION_SMART :
    1559             :         case OPERATION_PROBE :
    1560             :         case OPERATION_DEVICES :
    1561             :         case OPERATION_SPINUP :
    1562             :         case OPERATION_SPINDOWN :
    1563             :         case OPERATION_SPINDOWNIFUP :
    1564         148 :                 break;
    1565         168 :         default :
    1566         168 :                 if (opt.force_device) {
    1567             :                         /* LCOV_EXCL_START */
    1568             :                         log_fatal(EUSER, "You cannot use -D, --force-device with the '%s' command\n", command);
    1569             :                         exit(EXIT_FAILURE);
    1570             :                         /* LCOV_EXCL_STOP */
    1571             :                 }
    1572             :         }
    1573             : 
    1574         316 :         switch (operation) {
    1575         243 :         case OPERATION_SYNC :
    1576             :         case OPERATION_CHECK :
    1577             :         case OPERATION_FIX :
    1578         243 :                 break;
    1579          73 :         default :
    1580          73 :                 if (opt.force_nocopy) {
    1581             :                         /* LCOV_EXCL_START */
    1582             :                         log_fatal(EUSER, "You cannot use -N, --force-nocopy with the '%s' command\n", command);
    1583             :                         exit(EXIT_FAILURE);
    1584             :                         /* LCOV_EXCL_STOP */
    1585             :                 }
    1586             :         }
    1587             : 
    1588         316 :         switch (operation) {
    1589         108 :         case OPERATION_SYNC :
    1590         108 :                 break;
    1591         208 :         default :
    1592         208 :                 if (opt.prehash) {
    1593             :                         /* LCOV_EXCL_START */
    1594             :                         log_fatal(EUSER, "You cannot use -h, --pre-hash with the '%s' command\n", command);
    1595             :                         exit(EXIT_FAILURE);
    1596             :                         /* LCOV_EXCL_STOP */
    1597             :                 }
    1598             : 
    1599         208 :                 if (opt.force_full) {
    1600             :                         /* LCOV_EXCL_START */
    1601             :                         log_fatal(EUSER, "You cannot use -F, --force-full with the '%s' command\n", command);
    1602             :                         exit(EXIT_FAILURE);
    1603             :                         /* LCOV_EXCL_STOP */
    1604             :                 }
    1605             : 
    1606         208 :                 if (opt.force_realloc) {
    1607             :                         /* LCOV_EXCL_START */
    1608             :                         log_fatal(EUSER, "You cannot use -R, --force-realloc with the '%s' command\n", command);
    1609             :                         exit(EXIT_FAILURE);
    1610             :                         /* LCOV_EXCL_STOP */
    1611             :                 }
    1612             :         }
    1613             : 
    1614         316 :         if (opt.force_full && opt.force_nocopy) {
    1615             :                 /* LCOV_EXCL_START */
    1616             :                 log_fatal(EUSER, "You cannot use the -F, --force-full and -N, --force-nocopy options simultaneously\n");
    1617             :                 exit(EXIT_FAILURE);
    1618             :                 /* LCOV_EXCL_STOP */
    1619             :         }
    1620             : 
    1621         316 :         if (opt.force_realloc && opt.force_nocopy) {
    1622             :                 /* LCOV_EXCL_START */
    1623             :                 log_fatal(EUSER, "You cannot use the -R, --force-realloc and -N, --force-nocopy options simultaneously\n");
    1624             :                 exit(EXIT_FAILURE);
    1625             :                 /* LCOV_EXCL_STOP */
    1626             :         }
    1627             : 
    1628         316 :         if (opt.force_realloc && opt.force_full) {
    1629             :                 /* LCOV_EXCL_START */
    1630             :                 log_fatal(EUSER, "You cannot use the -R, --force-realloc and -F, --force-full options simultaneously\n");
    1631             :                 exit(EXIT_FAILURE);
    1632             :                 /* LCOV_EXCL_STOP */
    1633             :         }
    1634             : 
    1635         316 :         if (opt.prehash && opt.force_nocopy) {
    1636             :                 /* LCOV_EXCL_START */
    1637             :                 log_fatal(EUSER, "You cannot use the -h, --pre-hash and -N, --force-nocopy options simultaneously\n");
    1638             :                 exit(EXIT_FAILURE);
    1639             :                 /* LCOV_EXCL_STOP */
    1640             :         }
    1641             : 
    1642         316 :         switch (operation) {
    1643         138 :         case OPERATION_CHECK :
    1644             :         case OPERATION_FIX :
    1645             :         case OPERATION_DRY :
    1646         138 :                 break;
    1647         173 :         default :
    1648         173 :                 if (!tommy_list_empty(&filterlist_disk)) {
    1649             :                         /* LCOV_EXCL_START */
    1650             :                         log_fatal(EUSER, "You cannot use -d, --filter-disk with the '%s' command\n", command);
    1651             :                         exit(EXIT_FAILURE);
    1652             :                         /* LCOV_EXCL_STOP */
    1653             :                 }
    1654             :         /* fallthrough */
    1655             :         case OPERATION_SPINUP :
    1656             :         case OPERATION_SPINDOWN :
    1657             :         case OPERATION_SPINDOWNIFUP :
    1658         178 :                 if (!tommy_list_empty(&filterlist_file)) {
    1659             :                         /* LCOV_EXCL_START */
    1660             :                         log_fatal(EUSER, "You cannot use -f, --filter with the '%s' command\n", command);
    1661             :                         exit(EXIT_FAILURE);
    1662             :                         /* LCOV_EXCL_STOP */
    1663             :                 }
    1664         178 :                 if (filter_missing != 0) {
    1665             :                         /* LCOV_EXCL_START */
    1666             :                         log_fatal(EUSER, "You cannot use -m, --filter-missing with the '%s' command\n", command);
    1667             :                         exit(EXIT_FAILURE);
    1668             :                         /* LCOV_EXCL_STOP */
    1669             :                 }
    1670         178 :                 if (filter_error != 0) {
    1671             :                         /* LCOV_EXCL_START */
    1672             :                         log_fatal(EUSER, "You cannot use -e, --filter-error with the '%s' command\n", command);
    1673             :                         exit(EXIT_FAILURE);
    1674             :                         /* LCOV_EXCL_STOP */
    1675             :                 }
    1676             :         }
    1677             : 
    1678             :         /*
    1679             :          * Errors must be always fixed on all disks
    1680             :          * because we don't keep the information on what disk is the error
    1681             :          */
    1682         316 :         if (filter_error != 0 && !tommy_list_empty(&filterlist_disk)) {
    1683             :                 /* LCOV_EXCL_START */
    1684             :                 log_fatal(EUSER, "You cannot use -e, --filter-error and -d, --filter-disk simultaneously\n");
    1685             :                 exit(EXIT_FAILURE);
    1686             :                 /* LCOV_EXCL_STOP */
    1687             :         }
    1688             : 
    1689         316 :         switch (operation) {
    1690         135 :         case OPERATION_CHECK :
    1691             :         case OPERATION_FIX :
    1692         135 :                 break;
    1693         181 :         default :
    1694         181 :                 if (import_timestamp != 0 || import_content != 0) {
    1695             :                         /* LCOV_EXCL_START */
    1696             :                         log_fatal(EUSER, "Import not allowed with the '%s' command\n", command);
    1697             :                         exit(EXIT_FAILURE);
    1698             :                         /* LCOV_EXCL_STOP */
    1699             :                 }
    1700             :         }
    1701             : 
    1702         316 :         switch (operation) {
    1703          28 :         case OPERATION_LIST :
    1704             :         case OPERATION_DUP :
    1705             :         case OPERATION_STATUS :
    1706             :         case OPERATION_REWRITE :
    1707             :         case OPERATION_READ :
    1708             :         case OPERATION_REHASH :
    1709             :                 /* avoid to check and access data disks if not needed */
    1710          28 :                 opt.skip_disk_access = 1;
    1711          28 :                 break;
    1712             :         }
    1713             : 
    1714         316 :         switch (operation) {
    1715          40 :         case OPERATION_DIFF :
    1716             :         case OPERATION_LIST :
    1717             :         case OPERATION_DUP :
    1718             :         case OPERATION_POOL :
    1719             :         case OPERATION_STATUS :
    1720             :         case OPERATION_REWRITE :
    1721             :         case OPERATION_READ :
    1722             :         case OPERATION_REHASH :
    1723             :         case OPERATION_TOUCH :
    1724             :                 /* avoid to check and access parity disks if not needed */
    1725          40 :                 opt.skip_parity_access = 1;
    1726          40 :                 break;
    1727             :         }
    1728             : 
    1729         316 :         switch (operation) {
    1730         135 :         case OPERATION_FIX :
    1731             :         case OPERATION_CHECK :
    1732             :                 /* avoid to stop processing if a content file is not accessible */
    1733         135 :                 opt.skip_content_access = 1;
    1734         135 :                 break;
    1735             :         }
    1736             : 
    1737         316 :         switch (operation) {
    1738          33 :         case OPERATION_DIFF :
    1739             :         case OPERATION_LIST :
    1740             :         case OPERATION_DUP :
    1741             :         case OPERATION_POOL :
    1742             :         case OPERATION_TOUCH :
    1743             :         case OPERATION_SPINUP :
    1744             :         case OPERATION_SPINDOWN :
    1745             :         case OPERATION_SPINDOWNIFUP :
    1746             :         case OPERATION_DEVICES :
    1747             :         case OPERATION_SMART :
    1748             :         case OPERATION_PROBE :
    1749          33 :                 opt.skip_self = 1;
    1750          33 :                 break;
    1751             :         }
    1752             : 
    1753         316 :         switch (operation) {
    1754             : #if HAVE_DIRECT_IO
    1755         123 :         case OPERATION_SYNC :
    1756             :         case OPERATION_SCRUB :
    1757             :         case OPERATION_DRY :
    1758         123 :                 break;
    1759             : #endif
    1760         193 :         default :
    1761             :                 /* we allow direct IO only on some commands */
    1762         193 :                 if (opt.file_mode == ADVISE_DIRECT)
    1763           0 :                         opt.file_mode = ADVISE_SEQUENTIAL;
    1764         193 :                 break;
    1765             :         }
    1766             : 
    1767         316 :         switch (operation) {
    1768           8 :         case OPERATION_DEVICES :
    1769             :         case OPERATION_SMART :
    1770             :         case OPERATION_PROBE :
    1771             :                 /* we may need to use these commands during operations */
    1772           8 :                 opt.skip_lock = 1;
    1773           8 :                 break;
    1774             :         }
    1775             : 
    1776             :         /* open the log file */
    1777         316 :         log_open(log_file);
    1778             : 
    1779             :         /* print generic info into the log */
    1780         316 :         t = time(0);
    1781             : #if HAVE_LOCALTIME_R
    1782         316 :         tm = localtime_r(&t, &tm_res);
    1783             : #else
    1784             :         tm = localtime(&t);
    1785             : #endif
    1786         316 :         log_tag("version:%s\n", PACKAGE_VERSION);
    1787         316 :         log_tag("unixtime:%" PRIi64 "\n", (int64_t)t);
    1788         316 :         if (tm) {
    1789             :                 char datetime[64];
    1790         316 :                 strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S", tm);
    1791         316 :                 log_tag("time:%s\n", datetime);
    1792             :         }
    1793         316 :         log_tag("command:%s\n", command);
    1794        4985 :         for (i = 0; i < argc; ++i)
    1795        4669 :                 log_tag("argv:%u:%s\n", i, esc_tag(argv[i], esc_buffer));
    1796         316 :         log_flush();
    1797             : 
    1798         316 :         if (!opt.skip_self)
    1799           1 :                 selftest();
    1800             : 
    1801         316 :         state_init(&state);
    1802             : 
    1803             :         /* read the configuration file */
    1804         316 :         state_config(&state, conf, command, &opt, &filterlist_disk);
    1805             : 
    1806             :         /* set the raid mode */
    1807         315 :         raid_mode(state.raid_mode);
    1808             : 
    1809             : #if HAVE_LOCKFILE
    1810             :         /* create the lock file */
    1811         315 :         if (!opt.skip_lock && state.lockfile[0]) {
    1812         307 :                 lock = lock_lock(state.lockfile);
    1813         307 :                 if (lock == -1) {
    1814             :                         /* LCOV_EXCL_START */
    1815             :                         if (errno != EWOULDBLOCK) {
    1816             :                                 log_fatal(errno, "Failed to create the lock file '%s'. %s.\n", state.lockfile, strerror(errno));
    1817             :                         } else {
    1818             :                                 log_fatal(errno, "The lock file '%s' is already in use!\n", state.lockfile);
    1819             :                                 log_fatal(errno, "SnapRAID is already in use!\n");
    1820             :                         }
    1821             :                         exit(EXIT_FAILURE);
    1822             :                         /* LCOV_EXCL_STOP */
    1823             :                 }
    1824             :         }
    1825             : #else
    1826             :         (void)lock;
    1827             : #endif
    1828             : 
    1829         315 :         ret = 0;
    1830         315 :         if (operation == OPERATION_DIFF) {
    1831           8 :                 state_read(&state);
    1832             : 
    1833             :                 /* refresh the size info to log correct info */
    1834           8 :                 state_refresh(&state);
    1835             : 
    1836           8 :                 ret = state_diff(&state);
    1837             : 
    1838             :                 /* abort if sync needed */
    1839           8 :                 if (ret > 0)
    1840           7 :                         exit(EXIT_SYNC_NEEDED);
    1841         307 :         } else if (operation == OPERATION_SYNC) {
    1842         108 :                 state_read(&state);
    1843             : 
    1844             :                 /*
    1845             :                  * Mark the files that have to be reallocated
    1846             :                  * it will happen in inside scan_file_keep() called in state_scan()
    1847             :                  */
    1848         107 :                 if (state.opt.force_realloc)
    1849           2 :                         state_locate_mark_tail_blocks_for_resync(&state, opt.parity_tail);
    1850             : 
    1851         107 :                 if (opt.gui_touch_before)
    1852           0 :                         state_touch(&state);
    1853             : 
    1854         107 :                 ret = state_snapshot_new(&state);
    1855         107 :                 if (ret != 0) {
    1856           0 :                         exit(EXIT_FAILURE);
    1857             :                 }
    1858             : 
    1859         107 :                 state_scan(&state);
    1860             : 
    1861         106 :                 if (opt.gui_threshold_removes != 0 && state.removed_files >= opt.gui_threshold_removes) {
    1862           0 :                         log_fatal(EUSER, "Too many files were removed (%u, limit is %u). Sync aborted.\n", state.removed_files, opt.gui_threshold_removes);
    1863           0 :                         exit(EXIT_SYNC_NEEDED);
    1864             :                 }
    1865         106 :                 if (opt.gui_threshold_updates != 0 && state.updated_files >= opt.gui_threshold_updates) {
    1866           0 :                         log_fatal(EUSER, "Too many files were updated (%u, limit is %u). Sync aborted.\n", state.updated_files, opt.gui_threshold_updates);
    1867           0 :                         exit(EXIT_SYNC_NEEDED);
    1868             :                 }
    1869             : 
    1870             :                 /* refresh the size info before the content write */
    1871         106 :                 state_refresh(&state);
    1872             : 
    1873         106 :                 memory();
    1874             : 
    1875             :                 /* intercept signals while operating */
    1876         106 :                 signal_init();
    1877             : 
    1878             :                 /* run a test command if required */
    1879         106 :                 if (run != 0) {
    1880          10 :                         ret = system(run); /* ignore error */
    1881          10 :                         if (ret != 0) {
    1882             :                                 /* LCOV_EXCL_START */
    1883             :                                 log_fatal(errno, "Error executing command '%s'.\n", run);
    1884             :                                 exit(EXIT_FAILURE);
    1885             :                                 /* LCOV_EXCL_STOP */
    1886             :                         }
    1887             :                 }
    1888             : 
    1889             :                 /*
    1890             :                  * Waits some time to ensure that any concurrent modification done at the files,
    1891             :                  * using the same mtime read by the scan process, will be read by sync.
    1892             :                  * Note that any later modification done, potentially not read by sync, will have
    1893             :                  * a different mtime, and it will be synchronized at the next sync.
    1894             :                  * The worst case is the FAT file-system with a two seconds resolution for mtime.
    1895             :                  * If you don't use FAT, the wait is not needed, because most file-systems have now
    1896             :                  * at least microseconds resolution, but better to be safe.
    1897             :                  */
    1898         106 :                 if (!opt.skip_self)
    1899           0 :                         sleep(2);
    1900             : 
    1901         106 :                 ret = state_sync(&state, blockstart, blockcount);
    1902             : 
    1903             :                 /* commit the snapshot as stable */
    1904          92 :                 if (ret == 0)
    1905          80 :                         state_snapshot_commit(&state);
    1906         199 :         } else if (operation == OPERATION_DRY) {
    1907           3 :                 state_read(&state);
    1908             : 
    1909             :                 /* refresh the size info to log correct info */
    1910           3 :                 state_refresh(&state);
    1911             : 
    1912             :                 /* filter */
    1913           3 :                 state_skip(&state);
    1914           3 :                 state_filter(&state, &filterlist_file, &filterlist_disk, filter_missing, filter_error);
    1915             : 
    1916           3 :                 memory();
    1917             : 
    1918             :                 /* intercept signals while operating */
    1919           3 :                 signal_init();
    1920             : 
    1921           3 :                 ret = state_dry(&state, blockstart, blockcount);
    1922         196 :         } else if (operation == OPERATION_REHASH) {
    1923           1 :                 state_read(&state);
    1924             : 
    1925             :                 /* intercept signals while operating */
    1926           1 :                 signal_init();
    1927             : 
    1928           1 :                 state_rehash(&state);
    1929             : 
    1930             :                 /* save the new state if required */
    1931           1 :                 if (state.need_write)
    1932           1 :                         state_write(&state);
    1933         195 :         } else if (operation == OPERATION_SCRUB) {
    1934          12 :                 state_read(&state);
    1935             : 
    1936             :                 /* refresh the size info before the content write */
    1937          12 :                 state_refresh(&state);
    1938             : 
    1939          12 :                 memory();
    1940             : 
    1941             :                 /* intercept signals while operating */
    1942          12 :                 signal_init();
    1943             : 
    1944          12 :                 state_snapshot_read(&state);
    1945             : 
    1946          12 :                 ret = state_scrub(&state, plan100, olderthan);
    1947         183 :         } else if (operation == OPERATION_REWRITE) {
    1948           1 :                 state_read(&state);
    1949             : 
    1950             :                 /* intercept signals while operating */
    1951           1 :                 signal_init();
    1952             : 
    1953           1 :                 state_write(&state);
    1954             : 
    1955           1 :                 memory();
    1956         182 :         } else if (operation == OPERATION_READ) {
    1957           1 :                 state_read(&state);
    1958             : 
    1959             :                 /* intentionally DO NOT CALL state_refresh() because it spins up disks */
    1960             : 
    1961           1 :                 memory();
    1962         181 :         } else if (operation == OPERATION_TOUCH) {
    1963           1 :                 state_read(&state);
    1964             : 
    1965           1 :                 memory();
    1966             : 
    1967           1 :                 state_touch(&state);
    1968             : 
    1969             :                 /* intercept signals while operating */
    1970           1 :                 signal_init();
    1971             : 
    1972           1 :                 state_write(&state);
    1973         180 :         } else if (operation == OPERATION_SPINUP) {
    1974           2 :                 state_device(&state, DEVICE_UP, &filterlist_disk);
    1975         178 :         } else if (operation == OPERATION_SPINDOWN) {
    1976           2 :                 state_device(&state, DEVICE_DOWN, &filterlist_disk);
    1977         176 :         } else if (operation == OPERATION_SPINDOWNIFUP) {
    1978           1 :                 state_device(&state, DEVICE_DOWNIFUP, &filterlist_disk);
    1979         175 :         } else if (operation == OPERATION_DEVICES) {
    1980           2 :                 state_device(&state, DEVICE_LIST, 0);
    1981         173 :         } else if (operation == OPERATION_SMART) {
    1982           2 :                 state_device(&state, DEVICE_SMART, 0);
    1983         171 :         } else if (operation == OPERATION_PROBE) {
    1984             :                 /* probe also the content file */
    1985           4 :                 state_probe(&state);
    1986             : 
    1987           4 :                 state_device(&state, DEVICE_PROBE, 0);
    1988         167 :         } else if (operation == OPERATION_STATUS) {
    1989          17 :                 state_read(&state);
    1990             : 
    1991          17 :                 memory();
    1992             : 
    1993          17 :                 state_status(&state);
    1994         150 :         } else if (operation == OPERATION_LOCATE) {
    1995           5 :                 state_read(&state);
    1996             : 
    1997           5 :                 state_locate(&state, opt.parity_tail);
    1998             : 
    1999         145 :         } else if (operation == OPERATION_DUP) {
    2000           3 :                 state_read(&state);
    2001             : 
    2002           3 :                 state_dup(&state);
    2003         142 :         } else if (operation == OPERATION_LIST) {
    2004           5 :                 state_read(&state);
    2005             : 
    2006           5 :                 state_list(&state);
    2007         137 :         } else if (operation == OPERATION_POOL) {
    2008           3 :                 state_read(&state);
    2009             : 
    2010           3 :                 state_pool(&state);
    2011         134 :         } else if (operation == OPERATION_CHECK || operation == OPERATION_FIX) {
    2012         134 :                 state_read(&state);
    2013             : 
    2014             :                 /* if we are also trying to recover */
    2015         134 :                 if (!state.opt.auditonly) {
    2016             :                         /* import the user specified dirs */
    2017         128 :                         if (import_timestamp != 0)
    2018           1 :                                 state_search(&state, import_timestamp);
    2019         128 :                         if (import_content != 0)
    2020           1 :                                 state_import(&state, import_content);
    2021             : 
    2022             :                         /* import from all the array */
    2023         128 :                         if (!state.opt.force_nocopy)
    2024         128 :                                 state_search_array(&state);
    2025             :                 }
    2026             : 
    2027             :                 /* filter */
    2028         134 :                 state_skip(&state);
    2029         134 :                 state_filter(&state, &filterlist_file, &filterlist_disk, filter_missing, filter_error);
    2030             : 
    2031         134 :                 memory();
    2032             : 
    2033             :                 /* intercept signals while operating */
    2034         134 :                 signal_init();
    2035             : 
    2036         134 :                 state_snapshot_write(&state, &filterlist_disk);
    2037             : 
    2038         134 :                 if (operation == OPERATION_CHECK) {
    2039          83 :                         ret = state_check(&state, 0, blockstart, blockcount);
    2040             :                 } else { /* it's fix */
    2041          51 :                         ret = state_check(&state, 1, blockstart, blockcount);
    2042             : 
    2043             :                         /* rescan if requested by the GUI */
    2044          51 :                         if (opt.gui_rescan_after)
    2045           0 :                                 state_scan(&state);
    2046             :                 }
    2047             :         } else {
    2048             :                 /* LCOV_EXCL_START */
    2049             :                 log_fatal(errno, "Unexpected command '%s'\n", command);
    2050             :                 exit(EXIT_FAILURE);
    2051             :                 /* LCOV_EXCL_STOP */
    2052             :         }
    2053             : 
    2054             :         /* close log file */
    2055         291 :         log_close(log_file);
    2056             : 
    2057             : #if HAVE_LOCKFILE
    2058         291 :         if (!opt.skip_lock && state.lockfile[0]) {
    2059         284 :                 if (lock_unlock(lock) == -1) {
    2060             :                         /* LCOV_EXCL_START */
    2061             :                         log_fatal(errno, "Failed to close the lock file '%s'. %s.\n", state.lockfile, strerror(errno));
    2062             :                         exit(EXIT_FAILURE);
    2063             :                         /* LCOV_EXCL_STOP */
    2064             :                 }
    2065             :         }
    2066             : #endif
    2067             : 
    2068         291 :         state_done(&state);
    2069         291 :         tommy_list_foreach(&filterlist_file, (tommy_foreach_func*)filter_free);
    2070         291 :         tommy_list_foreach(&filterlist_disk, (tommy_foreach_func*)filter_free);
    2071             : 
    2072         291 :         os_done();
    2073         291 :         lock_done();
    2074             : 
    2075             :         /* abort if required */
    2076         291 :         if (ret != 0) {
    2077             :                 /* LCOV_EXCL_START */
    2078             :                 exit(EXIT_FAILURE);
    2079             :                 /* LCOV_EXCL_STOP */
    2080             :         }
    2081         279 :         if (global_interrupt) {
    2082             :                 /* LCOV_EXCL_START */
    2083             : #ifdef _WIN32
    2084             :                 exit(STATUS_CONTROL_C_EXIT);
    2085             : #else
    2086             :                 /* restore default handler */
    2087             :                 signal(global_interrupt, SIG_DFL);
    2088             : 
    2089             :                 /* raise the signal again*/
    2090             :                 raise(global_interrupt);
    2091             : 
    2092             :                 /* if raise() didn't terminate */
    2093             :                 _exit(128 + global_interrupt);
    2094             : #endif
    2095             :                 /* LCOV_EXCL_STOP */
    2096             :         }
    2097             : 
    2098         279 :         return EXIT_SUCCESS;
    2099             : }
    2100             : 

Generated by: LCOV version 1.0