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