LCOV - code coverage report
Current view: top level - cmdline - snapraid.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 749 834 89.8 %
Date: 2025-10-28 11:59:11 Functions: 9 9 100.0 %

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

Generated by: LCOV version 1.0