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