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