LCOV - code coverage report
Current view: top level - cmdline - snapraid.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 788 903 87.3 %
Date: 2026-03-15 15:58:19 Functions: 10 10 100.0 %

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

Generated by: LCOV version 1.0