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|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(" smart SMART attributes of the array\n");
53 1 : printf(" pool Create or update the virtual view of the array\n");
54 1 : printf(" check Check the array\n");
55 1 : printf(" fix Fix the array\n");
56 1 : printf("\n");
57 1 : printf("Options:\n");
58 1 : printf(" " SWITCH_GETOPT_LONG("-c, --conf FILE ", "-c") " Configuration file\n");
59 1 : printf(" " SWITCH_GETOPT_LONG("-f, --filter PATTERN ", "-f") " Process only files matching the pattern\n");
60 1 : printf(" " SWITCH_GETOPT_LONG("-d, --filter-disk NAME", "-f") " Process only files in the specified disk\n");
61 1 : printf(" " SWITCH_GETOPT_LONG("-m, --filter-missing ", "-m") " Process only missing/deleted files\n");
62 1 : printf(" " SWITCH_GETOPT_LONG("-e, --filter-error ", "-e") " Process only files with errors\n");
63 1 : printf(" " SWITCH_GETOPT_LONG("-p, --plan PLAN ", "-p") " Define a scrub plan or percentage\n");
64 1 : printf(" " SWITCH_GETOPT_LONG("-o, --older-than DAYS ", "-o") " Process only the older part of the array\n");
65 1 : printf(" " SWITCH_GETOPT_LONG("-i, --import DIR ", "-i") " Import deleted files\n");
66 1 : printf(" " SWITCH_GETOPT_LONG("-l, --log FILE ", "-l") " Log file. Default none\n");
67 1 : printf(" " SWITCH_GETOPT_LONG("-a, --audit-only ", "-a") " Check only file data and not parity\n");
68 1 : printf(" " SWITCH_GETOPT_LONG("-h, --pre-hash ", "-h") " Pre-hash all the new data\n");
69 1 : printf(" " SWITCH_GETOPT_LONG("-Z, --force-zero ", "-Z") " Force syncing of files that get zero size\n");
70 1 : printf(" " SWITCH_GETOPT_LONG("-E, --force-empty ", "-E") " Force syncing of disks that get empty\n");
71 1 : printf(" " SWITCH_GETOPT_LONG("-U, --force-uuid ", "-U") " Force commands on disks with uuid changed\n");
72 1 : printf(" " SWITCH_GETOPT_LONG("-D, --force-device ", "-D") " Force commands with inaccessible/shared disks\n");
73 1 : printf(" " SWITCH_GETOPT_LONG("-N, --force-nocopy ", "-N") " Force commands disabling the copy detection\n");
74 1 : printf(" " SWITCH_GETOPT_LONG("-F, --force-full ", "-F") " Force a full parity computation in sync\n");
75 1 : printf(" " SWITCH_GETOPT_LONG("-R, --force-realloc ", "-R") " Force a full parity reallocation in sync\n");
76 1 : printf(" " SWITCH_GETOPT_LONG("-v, --verbose ", "-v") " Verbose\n");
77 1 : }
78 :
79 238 : void memory(void)
80 : {
81 238 : log_tag("memory:used:%" PRIu64 "\n", (uint64_t)malloc_counter_get());
82 :
83 : /* size of the block */
84 238 : log_tag("memory:block:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_block)));
85 238 : log_tag("memory:extent:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_extent)));
86 238 : log_tag("memory:file:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_file)));
87 238 : log_tag("memory:link:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_link)));
88 238 : log_tag("memory:dir:%" PRIu64 "\n", (uint64_t)(sizeof(struct snapraid_dir)));
89 :
90 238 : msg_progress("Using %u MiB of memory for the FileSystem.\n", (unsigned)(malloc_counter_get() / MEBI));
91 238 : }
92 :
93 277 : void test(int argc, char* argv[])
94 : {
95 : int i;
96 : char buffer[ESC_MAX];
97 :
98 : /* special testing code for quoting */
99 277 : if (argc < 2 || strcmp(argv[1], "test") != 0)
100 552 : return;
101 :
102 1 : for (i = 2; i < argc; ++i) {
103 0 : printf("argv[%d]\n", i);
104 0 : printf("\t#%s#\n", argv[i]);
105 0 : printf("\t#%s#\n", esc_shell(argv[i], buffer));
106 : }
107 :
108 : #ifdef _WIN32
109 : assert(strcmp(esc_shell(" ", buffer), "\" \"") == 0);
110 : assert(strcmp(esc_shell(" \" ", buffer), "\" \"\\\"\" \"") == 0);
111 : assert(strcmp(esc_shell("&|()<>^", buffer), "^&^|^(^)^<^>^^") == 0);
112 : assert(strcmp(esc_shell("&|()<>^ ", buffer), "\"&|()<>^ \"") == 0);
113 : #else
114 1 : assert(strcmp(esc_shell(",._+:@%%/-", buffer), ",._+:@%%/-") == 0);
115 1 : assert(strcmp(esc_shell(" ", buffer), "\\ ") == 0);
116 : #endif
117 :
118 1 : printf("Everything OK\n");
119 :
120 1 : exit(EXIT_SUCCESS);
121 : }
122 :
123 : /****************************************************************************/
124 : /* log */
125 :
126 266 : void log_open(const char* file)
127 : {
128 : char path[PATH_MAX];
129 : const char* mode;
130 : char text_T[32];
131 : char text_D[32];
132 : time_t t;
133 : struct tm* tm;
134 :
135 : /* leave stdlog at 0 if not specified */
136 266 : if (file == 0)
137 378 : return;
138 :
139 80 : t = time(0);
140 80 : tm = localtime(&t);
141 80 : if (tm) {
142 80 : strftime(text_T, sizeof(text_T), "%H%M%S", tm);
143 80 : strftime(text_D, sizeof(text_T), "%Y%m%d", tm);
144 : } else {
145 : /* LCOV_EXCL_START */
146 : strcpy(text_T, "invalid");
147 : strcpy(text_D, "invalid");
148 : /* LCOV_EXCL_STOP */
149 : }
150 :
151 : /* file mode */
152 80 : mode = "wt";
153 80 : if (*file == '>') {
154 7 : ++file;
155 :
156 7 : if (*file == '>') {
157 1 : mode = "at";
158 1 : ++file;
159 : }
160 :
161 7 : if (file[0] == '&' && file[1] == '1') {
162 5 : stdlog = stdout;
163 5 : return;
164 : }
165 :
166 2 : if (file[0] == '&' && file[1] == '2') {
167 1 : stdlog = stderr;
168 1 : return;
169 : }
170 : }
171 :
172 : /* process the path */
173 938 : for (*path = 0; *file != 0; ) {
174 790 : switch (*file) {
175 : case '%' :
176 2 : ++file;
177 2 : switch (*file) {
178 : case '%' :
179 0 : pathcatc(path, sizeof(path), '%');
180 0 : break;
181 : case 'T' :
182 1 : pathcat(path, sizeof(path), text_T);
183 1 : break;
184 : case 'D' :
185 1 : pathcat(path, sizeof(path), text_D);
186 1 : break;
187 : default :
188 : /* LCOV_EXCL_START */
189 : log_fatal("Invalid type specifier '%c' in the log file.\n", *file);
190 : exit(EXIT_FAILURE);
191 : /* LCOV_EXCL_STOP */
192 : }
193 2 : break;
194 : default :
195 788 : pathcatc(path, sizeof(path), *file);
196 788 : break;
197 : }
198 790 : ++file;
199 : }
200 :
201 74 : stdlog = fopen(path, mode);
202 74 : if (!stdlog) {
203 : /* LCOV_EXCL_START */
204 : log_fatal("Error opening the log file '%s'. %s.\n", path, strerror(errno));
205 : exit(EXIT_FAILURE);
206 : /* LCOV_EXCL_STOP */
207 : }
208 : }
209 :
210 243 : void log_close(const char* file)
211 : {
212 243 : if (stdlog != stdout && stdlog != stderr && stdlog != 0) {
213 74 : if (fclose(stdlog) != 0) {
214 : /* LCOV_EXCL_START */
215 : log_fatal("Error closing the log file '%s'. %s.\n", file, strerror(errno));
216 : exit(EXIT_FAILURE);
217 : /* LCOV_EXCL_STOP */
218 : }
219 : }
220 :
221 243 : stdlog = 0;
222 243 : }
223 :
224 : /****************************************************************************/
225 : /* config */
226 :
227 276 : void config(char* conf, size_t conf_size, const char* argv0)
228 : {
229 : #ifdef _WIN32
230 : char* slash;
231 :
232 : pathimport(conf, conf_size, argv0);
233 :
234 : slash = strrchr(conf, '/');
235 : if (slash) {
236 : slash[1] = 0;
237 : pathcat(conf, conf_size, PACKAGE ".conf");
238 : } else {
239 : pathcpy(conf, conf_size, PACKAGE ".conf");
240 : }
241 : #else
242 : (void)argv0;
243 :
244 : #ifdef SYSCONFDIR
245 : /* if it exists, give precedence at sysconfdir, usually /usr/local/etc */
246 276 : if (access(SYSCONFDIR "/" PACKAGE ".conf", F_OK) == 0)
247 0 : pathcpy(conf, conf_size, SYSCONFDIR "/" PACKAGE ".conf");
248 : else /* otherwise fallback to plain /etc */
249 : #endif
250 276 : pathcpy(conf, conf_size, "/etc/" PACKAGE ".conf");
251 : #endif
252 276 : }
253 :
254 : /****************************************************************************/
255 : /* main */
256 :
257 : #define OPT_TEST_SKIP_SELF 256
258 : #define OPT_TEST_KILL_AFTER_SYNC 257
259 : #define OPT_TEST_EXPECT_UNRECOVERABLE 258
260 : #define OPT_TEST_EXPECT_RECOVERABLE 259
261 : #define OPT_TEST_SKIP_SIGN 260
262 : #define OPT_TEST_SKIP_FALLOCATE 261
263 : #define OPT_TEST_SKIP_DEVICE 262
264 : #define OPT_TEST_FORCE_MURMUR3 264
265 : #define OPT_TEST_FORCE_SPOOKY2 265
266 : #define OPT_TEST_SKIP_LOCK 266
267 : #define OPT_TEST_FORCE_ORDER_PHYSICAL 267
268 : #define OPT_TEST_FORCE_ORDER_INODE 268
269 : #define OPT_TEST_FORCE_ORDER_ALPHA 269
270 : #define OPT_TEST_FORCE_ORDER_DIR 270
271 : #define OPT_TEST_FORCE_SCRUB_AT 271
272 : #define OPT_TEST_FORCE_SCRUB_EVEN 272
273 : #define OPT_TEST_FORCE_CONTENT_WRITE 273
274 : #define OPT_TEST_SKIP_CONTENT_CHECK 275
275 : #define OPT_TEST_SKIP_PARITY_ACCESS 276
276 : #define OPT_TEST_EXPECT_FAILURE 277
277 : #define OPT_TEST_RUN 278
278 : #define OPT_TEST_FORCE_SCAN_WINFIND 279
279 : #define OPT_TEST_IMPORT_CONTENT 280
280 : #define OPT_TEST_FORCE_PROGRESS 281
281 : #define OPT_TEST_SKIP_DISK_ACCESS 282
282 : #define OPT_TEST_FORCE_AUTOSAVE_AT 283
283 : #define OPT_TEST_FAKE_DEVICE 284
284 : #define OPT_TEST_EXPECT_NEED_SYNC 285
285 : #define OPT_NO_WARNINGS 286
286 : #define OPT_TEST_FAKE_UUID 287
287 : #define OPT_TEST_MATCH_FIRST_UUID 288
288 : #define OPT_TEST_FORCE_PARITY_UPDATE 289
289 : #define OPT_TEST_IO_CACHE 290
290 : #define OPT_TEST_IO_STATS 291
291 : #define OPT_TEST_COND_SIGNAL_OUTSIDE 292
292 : #define OPT_TEST_IO_ADVISE_NONE 293
293 : #define OPT_TEST_IO_ADVISE_SEQUENTIAL 294
294 : #define OPT_TEST_IO_ADVISE_FLUSH 295
295 : #define OPT_TEST_IO_ADVISE_FLUSH_WINDOW 296
296 : #define OPT_TEST_IO_ADVISE_DISCARD 297
297 : #define OPT_TEST_IO_ADVISE_DISCARD_WINDOW 298
298 : #define OPT_TEST_IO_ADVISE_DIRECT 299
299 : #define OPT_TEST_PARITY_LIMIT 301
300 : #define OPT_TEST_SKIP_CONTENT_WRITE 302
301 : #define OPT_TEST_SKIP_SPACE_HOLDER 303
302 : #define OPT_TEST_FORMAT 304
303 :
304 : #if HAVE_GETOPT_LONG
305 : struct option long_options[] = {
306 : { "conf", 1, 0, 'c' },
307 : { "filter", 1, 0, 'f' },
308 : { "filter-disk", 1, 0, 'd' },
309 : { "filter-missing", 0, 0, 'm' },
310 : { "filter-error", 0, 0, 'e' },
311 : { "percentage", 1, 0, 'p' }, /* legacy name for --plan */
312 : { "plan", 1, 0, 'p' },
313 : { "older-than", 1, 0, 'o' },
314 : { "start", 1, 0, 'S' },
315 : { "count", 1, 0, 'B' },
316 : { "error-limit", 1, 0, 'L' },
317 : { "import", 1, 0, 'i' },
318 : { "log", 1, 0, 'l' },
319 : { "force-zero", 0, 0, 'Z' },
320 : { "force-empty", 0, 0, 'E' },
321 : { "force-uuid", 0, 0, 'U' },
322 : { "force-device", 0, 0, 'D' },
323 : { "force-nocopy", 0, 0, 'N' },
324 : { "force-full", 0, 0, 'F' },
325 : { "force-realloc", 0, 0, 'R' },
326 : { "audit-only", 0, 0, 'a' },
327 : { "pre-hash", 0, 0, 'h' },
328 : { "speed-test", 0, 0, 'T' }, /* undocumented speed test command */
329 : { "gen-conf", 1, 0, 'C' },
330 : { "verbose", 0, 0, 'v' },
331 : { "quiet", 0, 0, 'q' }, /* undocumented quiet option */
332 : { "gui", 0, 0, 'G' }, /* undocumented GUI interface option */
333 : { "help", 0, 0, 'H' },
334 : { "version", 0, 0, 'V' },
335 :
336 : /* The following are test specific options, DO NOT USE! */
337 :
338 : /* After syncing, do not write the new content file */
339 : { "test-kill-after-sync", 0, 0, OPT_TEST_KILL_AFTER_SYNC },
340 :
341 : /* Exit with failure if after check/fix there ARE NOT unrecoverable errors. */
342 : { "test-expect-unrecoverable", 0, 0, OPT_TEST_EXPECT_UNRECOVERABLE },
343 :
344 : /* Exit with failure if after check/fix there ARE NOT recoverable errors. */
345 : { "test-expect-recoverable", 0, 0, OPT_TEST_EXPECT_RECOVERABLE },
346 :
347 : /* Skip the initial self test */
348 : { "test-skip-self", 0, 0, OPT_TEST_SKIP_SELF },
349 :
350 : /* Skip the initial sign check when reading the content file */
351 : { "test-skip-sign", 0, 0, OPT_TEST_SKIP_SIGN },
352 :
353 : /* Skip the fallocate() when growing the parity files */
354 : { "test-skip-fallocate", 0, 0, OPT_TEST_SKIP_FALLOCATE },
355 :
356 : /* Skip the device check */
357 : { "test-skip-device", 0, 0, OPT_TEST_SKIP_DEVICE },
358 :
359 : /* Force Murmur3 hash */
360 : { "test-force-murmur3", 0, 0, OPT_TEST_FORCE_MURMUR3 },
361 :
362 : /* Force Spooky2 hash */
363 : { "test-force-spooky2", 0, 0, OPT_TEST_FORCE_SPOOKY2 },
364 :
365 : /* Skip the use of lock file */
366 : { "test-skip-lock", 0, 0, OPT_TEST_SKIP_LOCK },
367 :
368 : /* Force a sort order for files */
369 : { "test-force-order-physical", 0, 0, OPT_TEST_FORCE_ORDER_PHYSICAL },
370 : { "test-force-order-inode", 0, 0, OPT_TEST_FORCE_ORDER_INODE },
371 : { "test-force-order-alpha", 0, 0, OPT_TEST_FORCE_ORDER_ALPHA },
372 : { "test-force-order-dir", 0, 0, OPT_TEST_FORCE_ORDER_DIR },
373 :
374 : /* Force scrub of the specified number of blocks */
375 : { "test-force-scrub-at", 1, 0, OPT_TEST_FORCE_SCRUB_AT },
376 :
377 : /* Force scrub of all the even blocks. This is really for testing, don't try it */
378 : { "test-force-scrub-even", 0, 0, OPT_TEST_FORCE_SCRUB_EVEN },
379 :
380 : /* Force write of the content file even if no modification is done */
381 : { "test-force-content-write", 0, 0, OPT_TEST_FORCE_CONTENT_WRITE },
382 :
383 : /* Relax the checks done at the content file */
384 : { "test-skip-content-check", 0, 0, OPT_TEST_SKIP_CONTENT_CHECK },
385 :
386 : /* Skip the parity access */
387 : { "test-skip-parity-access", 0, 0, OPT_TEST_SKIP_PARITY_ACCESS },
388 :
389 : /* Exit generic failure */
390 : { "test-expect-failure", 0, 0, OPT_TEST_EXPECT_FAILURE },
391 :
392 : /* Exit generic need sync */
393 : { "test-expect-need-sync", 0, 0, OPT_TEST_EXPECT_NEED_SYNC },
394 :
395 : /* Run some command after loading the state and before the command */
396 : { "test-run", 1, 0, OPT_TEST_RUN },
397 :
398 : /* Use the FindFirst/Next approach in Windows to list files */
399 : { "test-force-scan-winfind", 0, 0, OPT_TEST_FORCE_SCAN_WINFIND },
400 :
401 : /* Alternative import working by data */
402 : { "test-import-content", 1, 0, OPT_TEST_IMPORT_CONTENT },
403 :
404 : /* Force immediate progress state update */
405 : { "test-force-progress", 0, 0, OPT_TEST_FORCE_PROGRESS },
406 :
407 : /* Skip the disk access */
408 : { "test-skip-disk-access", 0, 0, OPT_TEST_SKIP_DISK_ACCESS },
409 :
410 : /* Force autosave at the specified block */
411 : { "test-force-autosave-at", 1, 0, OPT_TEST_FORCE_AUTOSAVE_AT },
412 :
413 : /* Fake device data */
414 : { "test-fake-device", 0, 0, OPT_TEST_FAKE_DEVICE },
415 :
416 : /* Disable annoying warnings */
417 : { "no-warnings", 0, 0, OPT_NO_WARNINGS },
418 :
419 : /* Fake UUID */
420 : { "test-fake-uuid", 0, 0, OPT_TEST_FAKE_UUID },
421 :
422 : /* Match first UUID */
423 : { "test-match-first-uuid", 0, 0, OPT_TEST_MATCH_FIRST_UUID },
424 :
425 : /* Force parity update even if all the data hash is already matching */
426 : { "test-force-parity-update", 0, 0, OPT_TEST_FORCE_PARITY_UPDATE },
427 :
428 : /* Number of IO buffers */
429 : { "test-io-cache", 1, 0, OPT_TEST_IO_CACHE },
430 :
431 : /* Print IO stats */
432 : { "test-io-stats", 0, 0, OPT_TEST_IO_STATS },
433 :
434 : /* Signal condition variable outside the mutex */
435 : { "test-cond-signal-outside", 0, 0, OPT_TEST_COND_SIGNAL_OUTSIDE },
436 :
437 : /* Set the io advise to none */
438 : { "test-io-advise-none", 0, 0, OPT_TEST_IO_ADVISE_NONE },
439 :
440 : /* Set the io advise to sequential */
441 : { "test-io-advise-sequential", 0, 0, OPT_TEST_IO_ADVISE_SEQUENTIAL },
442 :
443 : /* Set the io advise to flush */
444 : { "test-io-advise-flush", 0, 0, OPT_TEST_IO_ADVISE_FLUSH },
445 :
446 : /* Set the io advise to flush window */
447 : { "test-io-advise-flush-window", 0, 0, OPT_TEST_IO_ADVISE_FLUSH_WINDOW },
448 :
449 : /* Set the io advise to discard */
450 : { "test-io-advise-discard", 0, 0, OPT_TEST_IO_ADVISE_DISCARD },
451 :
452 : /* Set the io advise to discard window */
453 : { "test-io-advise-discard-window", 0, 0, OPT_TEST_IO_ADVISE_DISCARD_WINDOW },
454 :
455 : /* Set the io advise to direct */
456 : { "test-io-advise-direct", 0, 0, OPT_TEST_IO_ADVISE_DIRECT },
457 :
458 : /* Set an artificial parity limit */
459 : { "test-parity-limit", 1, 0, OPT_TEST_PARITY_LIMIT },
460 :
461 : /* Skip content write */
462 : { "test-skip-content-write", 0, 0, OPT_TEST_SKIP_CONTENT_WRITE },
463 :
464 : /* Skip space holder file in parity disks */
465 : { "test-skip-space-holder", 0, 0, OPT_TEST_SKIP_SPACE_HOLDER },
466 :
467 : /* Set the output format */
468 : { "test-fmt", 1, 0, OPT_TEST_FORMAT },
469 :
470 : { 0, 0, 0, 0 }
471 : };
472 : #endif
473 :
474 : #define OPTIONS "c:f:d:mep:o:S:B:L:i:l:ZEUDNFRahTC:vqHVG"
475 :
476 : volatile int global_interrupt = 0;
477 :
478 : /* LCOV_EXCL_START */
479 : void signal_handler(int signum)
480 : {
481 : (void)signum;
482 :
483 : /* report the request of interruption */
484 : global_interrupt = 1;
485 : }
486 : /* LCOV_EXCL_STOP */
487 :
488 223 : void signal_init(void)
489 : {
490 : #if HAVE_SIGACTION
491 : struct sigaction sa;
492 :
493 223 : sa.sa_handler = signal_handler;
494 223 : sigemptyset(&sa.sa_mask);
495 :
496 : /* use the SA_RESTART to automatically restart interrupted system calls */
497 223 : sa.sa_flags = SA_RESTART;
498 :
499 223 : sigaction(SIGHUP, &sa, 0);
500 223 : sigaction(SIGTERM, &sa, 0);
501 223 : sigaction(SIGINT, &sa, 0);
502 223 : sigaction(SIGQUIT, &sa, 0);
503 : #else
504 : signal(SIGINT, signal_handler);
505 : #endif
506 223 : }
507 :
508 : #define OPERATION_DIFF 0
509 : #define OPERATION_SYNC 1
510 : #define OPERATION_CHECK 2
511 : #define OPERATION_FIX 3
512 : #define OPERATION_DRY 4
513 : #define OPERATION_DUP 5
514 : #define OPERATION_LIST 6
515 : #define OPERATION_POOL 7
516 : #define OPERATION_REHASH 8
517 : #define OPERATION_SCRUB 9
518 : #define OPERATION_STATUS 10
519 : #define OPERATION_REWRITE 11
520 : #define OPERATION_READ 12
521 : #define OPERATION_TOUCH 13
522 : #define OPERATION_SPINUP 14
523 : #define OPERATION_SPINDOWN 15
524 : #define OPERATION_DEVICES 16
525 : #define OPERATION_SMART 17
526 :
527 277 : int main(int argc, char* argv[])
528 : {
529 : int c;
530 : struct snapraid_option opt;
531 : char conf[PATH_MAX];
532 : struct snapraid_state state;
533 : int operation;
534 : block_off_t blockstart;
535 : block_off_t blockcount;
536 : int ret;
537 : tommy_list filterlist_file;
538 : tommy_list filterlist_disk;
539 : int filter_missing;
540 : int filter_error;
541 : int plan;
542 : int olderthan;
543 : char* e;
544 : const char* command;
545 : const char* import_timestamp;
546 : const char* import_content;
547 : const char* log_file;
548 : int lock;
549 : const char* gen_conf;
550 : const char* run;
551 : int speedtest;
552 : int period;
553 : time_t t;
554 : struct tm* tm;
555 : int i;
556 :
557 277 : test(argc, argv);
558 :
559 276 : lock_init();
560 :
561 : /* defaults */
562 276 : config(conf, sizeof(conf), argv[0]);
563 276 : memset(&opt, 0, sizeof(opt));
564 276 : opt.io_error_limit = 100;
565 276 : blockstart = 0;
566 276 : blockcount = 0;
567 276 : tommy_list_init(&filterlist_file);
568 276 : tommy_list_init(&filterlist_disk);
569 276 : period = 1000;
570 276 : filter_missing = 0;
571 276 : filter_error = 0;
572 276 : plan = SCRUB_AUTO;
573 276 : olderthan = SCRUB_AUTO;
574 276 : import_timestamp = 0;
575 276 : import_content = 0;
576 276 : log_file = 0;
577 276 : lock = 0;
578 276 : gen_conf = 0;
579 276 : speedtest = 0;
580 276 : run = 0;
581 :
582 276 : opterr = 0;
583 3475 : while ((c =
584 : #if HAVE_GETOPT_LONG
585 : getopt_long(argc, argv, OPTIONS, long_options, 0))
586 : #else
587 : getopt(argc, argv, OPTIONS))
588 : #endif
589 : != EOF) {
590 2928 : switch (c) {
591 : case 'c' :
592 269 : pathimport(conf, sizeof(conf), optarg);
593 269 : break;
594 : case 'f' : {
595 6 : struct snapraid_filter* filter = filter_alloc_file(1, optarg);
596 6 : if (!filter) {
597 : /* LCOV_EXCL_START */
598 : log_fatal("Invalid filter specification '%s'\n", optarg);
599 : log_fatal("Filters using relative paths are not supported. Ensure to add an initial slash\n");
600 : exit(EXIT_FAILURE);
601 : /* LCOV_EXCL_STOP */
602 : }
603 3 : tommy_list_insert_tail(&filterlist_file, &filter->node, filter);
604 3 : } break;
605 : case 'd' : {
606 10 : struct snapraid_filter* filter = filter_alloc_disk(1, optarg);
607 10 : if (!filter) {
608 : /* LCOV_EXCL_START */
609 : log_fatal("Invalid filter specification '%s'\n", optarg);
610 : exit(EXIT_FAILURE);
611 : /* LCOV_EXCL_STOP */
612 : }
613 10 : tommy_list_insert_tail(&filterlist_disk, &filter->node, filter);
614 10 : } break;
615 : case 'm' :
616 2 : filter_missing = 1;
617 2 : opt.expected_missing = 1;
618 2 : break;
619 : case 'e' :
620 : /* when processing only error, we filter both files and blocks */
621 : /* and we apply fixes only to synced ones */
622 2 : filter_error = 1;
623 2 : opt.badonly = 1;
624 2 : opt.syncedonly = 1;
625 2 : break;
626 : case 'p' :
627 5 : if (strcmp(optarg, "bad") == 0) {
628 1 : plan = SCRUB_BAD;
629 4 : } else if (strcmp(optarg, "new") == 0) {
630 1 : plan = SCRUB_NEW;
631 3 : } else if (strcmp(optarg, "full") == 0) {
632 2 : plan = SCRUB_FULL;
633 : } else {
634 1 : plan = strtoul(optarg, &e, 10);
635 1 : if (!e || *e || plan > 100) {
636 : /* LCOV_EXCL_START */
637 : log_fatal("Invalid plan/percentage '%s'\n", optarg);
638 : exit(EXIT_FAILURE);
639 : /* LCOV_EXCL_STOP */
640 : }
641 : }
642 5 : break;
643 : case 'o' :
644 1 : olderthan = strtoul(optarg, &e, 10);
645 1 : if (!e || *e || olderthan > 1000) {
646 : /* LCOV_EXCL_START */
647 : log_fatal("Invalid number of days '%s'\n", optarg);
648 : exit(EXIT_FAILURE);
649 : /* LCOV_EXCL_STOP */
650 : }
651 1 : break;
652 : case 'S' :
653 2 : blockstart = strtoul(optarg, &e, 0);
654 2 : if (!e || *e) {
655 : /* LCOV_EXCL_START */
656 : log_fatal("Invalid start position '%s'\n", optarg);
657 : exit(EXIT_FAILURE);
658 : /* LCOV_EXCL_STOP */
659 : }
660 2 : break;
661 : case 'B' :
662 7 : blockcount = strtoul(optarg, &e, 0);
663 7 : if (!e || *e) {
664 : /* LCOV_EXCL_START */
665 : log_fatal("Invalid count number '%s'\n", optarg);
666 : exit(EXIT_FAILURE);
667 : /* LCOV_EXCL_STOP */
668 : }
669 7 : break;
670 : case 'L' :
671 0 : opt.io_error_limit = strtoul(optarg, &e, 0);
672 0 : if (!e || *e) {
673 : /* LCOV_EXCL_START */
674 : log_fatal("Invalid error limit number '%s'\n", optarg);
675 : exit(EXIT_FAILURE);
676 : /* LCOV_EXCL_STOP */
677 : }
678 0 : break;
679 : case 'i' :
680 1 : if (import_timestamp) {
681 : /* LCOV_EXCL_START */
682 : log_fatal("Import directory '%s' already specified as '%s'\n", optarg, import_timestamp);
683 : exit(EXIT_FAILURE);
684 : /* LCOV_EXCL_STOP */
685 : }
686 1 : import_timestamp = optarg;
687 1 : break;
688 : case OPT_TEST_IMPORT_CONTENT :
689 1 : if (import_content) {
690 : /* LCOV_EXCL_START */
691 : log_fatal("Import directory '%s' already specified as '%s'\n", optarg, import_content);
692 : exit(EXIT_FAILURE);
693 : /* LCOV_EXCL_STOP */
694 : }
695 1 : import_content = optarg;
696 1 : break;
697 : case 'l' :
698 80 : if (log_file) {
699 : /* LCOV_EXCL_START */
700 : log_fatal("Log file '%s' already specified as '%s'\n", optarg, log_file);
701 : exit(EXIT_FAILURE);
702 : /* LCOV_EXCL_STOP */
703 : }
704 80 : log_file = optarg;
705 80 : break;
706 : case 'Z' :
707 0 : opt.force_zero = 1;
708 0 : break;
709 : case 'E' :
710 6 : opt.force_empty = 1;
711 6 : break;
712 : case 'U' :
713 0 : opt.force_uuid = 1;
714 0 : break;
715 : case 'D' :
716 1 : opt.force_device = 1;
717 1 : break;
718 : case 'N' :
719 1 : opt.force_nocopy = 1;
720 1 : break;
721 : case 'F' :
722 6 : opt.force_full = 1;
723 6 : break;
724 : case 'R' :
725 1 : opt.force_realloc = 1;
726 1 : break;
727 : case 'a' :
728 6 : opt.auditonly = 1;
729 6 : break;
730 : case 'h' :
731 7 : opt.prehash = 1;
732 7 : break;
733 : case 'v' :
734 26 : ++msg_level;
735 26 : break;
736 : case 'q' :
737 729 : --msg_level;
738 729 : break;
739 : case 'G' :
740 26 : opt.gui = 1;
741 26 : break;
742 : case 'H' :
743 1 : usage();
744 1 : exit(EXIT_SUCCESS);
745 : case 'V' :
746 1 : version();
747 1 : exit(EXIT_SUCCESS);
748 : case 'T' :
749 4 : speedtest = 1;
750 4 : break;
751 : case 'C' :
752 1 : gen_conf = optarg;
753 1 : break;
754 : case OPT_TEST_KILL_AFTER_SYNC :
755 10 : opt.kill_after_sync = 1;
756 10 : break;
757 : case OPT_TEST_EXPECT_UNRECOVERABLE :
758 13 : opt.expect_unrecoverable = 1;
759 13 : break;
760 : case OPT_TEST_EXPECT_RECOVERABLE :
761 23 : opt.expect_recoverable = 1;
762 23 : break;
763 : case OPT_TEST_SKIP_SELF :
764 269 : opt.skip_self = 1;
765 269 : break;
766 : case OPT_TEST_SKIP_SIGN :
767 0 : opt.skip_sign = 1;
768 0 : break;
769 : case OPT_TEST_SKIP_FALLOCATE :
770 1 : opt.skip_fallocate = 1;
771 1 : break;
772 : case OPT_TEST_SKIP_DEVICE :
773 276 : opt.skip_device = 1;
774 276 : period = 50; /* reduce period of the speed test */
775 276 : break;
776 : case OPT_TEST_SKIP_CONTENT_CHECK :
777 0 : opt.skip_content_check = 1;
778 0 : break;
779 : case OPT_TEST_SKIP_PARITY_ACCESS :
780 0 : opt.skip_parity_access = 1;
781 0 : break;
782 : case OPT_TEST_SKIP_DISK_ACCESS :
783 0 : opt.skip_disk_access = 1;
784 0 : break;
785 : case OPT_TEST_FORCE_MURMUR3 :
786 1 : opt.force_murmur3 = 1;
787 1 : break;
788 : case OPT_TEST_FORCE_SPOOKY2 :
789 1 : opt.force_spooky2 = 1;
790 1 : break;
791 : case OPT_TEST_SKIP_LOCK :
792 0 : opt.skip_lock = 1;
793 0 : break;
794 : case OPT_TEST_FORCE_ORDER_PHYSICAL :
795 2 : opt.force_order = SORT_PHYSICAL;
796 2 : break;
797 : case OPT_TEST_FORCE_ORDER_INODE :
798 0 : opt.force_order = SORT_INODE;
799 0 : break;
800 : case OPT_TEST_FORCE_ORDER_ALPHA :
801 267 : opt.force_order = SORT_ALPHA;
802 267 : break;
803 : case OPT_TEST_FORCE_ORDER_DIR :
804 0 : opt.force_order = SORT_DIR;
805 0 : break;
806 : case OPT_TEST_FORCE_SCRUB_AT :
807 4 : opt.force_scrub_at = atoi(optarg);
808 4 : break;
809 : case OPT_TEST_FORCE_SCRUB_EVEN :
810 1 : opt.force_scrub_even = 1;
811 1 : break;
812 : case OPT_TEST_FORCE_CONTENT_WRITE :
813 0 : opt.force_content_write = 1;
814 0 : break;
815 : case OPT_TEST_EXPECT_FAILURE :
816 : /* invert the exit codes */
817 19 : exit_success = 1;
818 19 : exit_failure = 0;
819 19 : break;
820 : case OPT_TEST_EXPECT_NEED_SYNC :
821 : /* invert the exit codes */
822 7 : exit_success = 1;
823 7 : exit_sync_needed = 0;
824 7 : break;
825 : case OPT_TEST_RUN :
826 10 : run = optarg;
827 10 : break;
828 : case OPT_TEST_FORCE_SCAN_WINFIND :
829 0 : opt.force_scan_winfind = 1;
830 0 : break;
831 : case OPT_TEST_FORCE_PROGRESS :
832 269 : opt.force_progress = 1;
833 269 : break;
834 : case OPT_TEST_FORCE_AUTOSAVE_AT :
835 1 : opt.force_autosave_at = atoi(optarg);
836 1 : break;
837 : case OPT_TEST_FAKE_DEVICE :
838 1 : opt.fake_device = 1;
839 1 : break;
840 : case OPT_NO_WARNINGS :
841 270 : opt.no_warnings = 1;
842 270 : break;
843 : case OPT_TEST_FAKE_UUID :
844 1 : opt.fake_uuid = 2;
845 1 : break;
846 : case OPT_TEST_MATCH_FIRST_UUID :
847 2 : opt.match_first_uuid = 1;
848 2 : break;
849 : case OPT_TEST_FORCE_PARITY_UPDATE :
850 0 : opt.force_parity_update = 1;
851 0 : break;
852 : case OPT_TEST_IO_CACHE :
853 1 : opt.io_cache = atoi(optarg);
854 1 : if (opt.io_cache != 1 && (opt.io_cache < IO_MIN || opt.io_cache > IO_MAX)) {
855 : /* LCOV_EXCL_START */
856 : log_fatal("The IO cache should be between %u and %u.\n", IO_MIN, IO_MAX);
857 : exit(EXIT_FAILURE);
858 : /* LCOV_EXCL_STOP */
859 : }
860 1 : break;
861 : case OPT_TEST_IO_STATS :
862 1 : opt.force_stats = 1;
863 1 : break;
864 : case OPT_TEST_COND_SIGNAL_OUTSIDE :
865 : #if HAVE_PTHREAD
866 0 : thread_cond_signal_outside = 1;
867 : #endif
868 0 : break;
869 : case OPT_TEST_IO_ADVISE_NONE :
870 1 : opt.file_mode = ADVISE_NONE;
871 1 : break;
872 : case OPT_TEST_IO_ADVISE_SEQUENTIAL :
873 1 : opt.file_mode = ADVISE_SEQUENTIAL;
874 1 : break;
875 : case OPT_TEST_IO_ADVISE_FLUSH :
876 0 : opt.file_mode = ADVISE_FLUSH;
877 0 : break;
878 : case OPT_TEST_IO_ADVISE_FLUSH_WINDOW :
879 1 : opt.file_mode = ADVISE_FLUSH_WINDOW;
880 1 : break;
881 : case OPT_TEST_IO_ADVISE_DISCARD :
882 0 : opt.file_mode = ADVISE_DISCARD;
883 0 : break;
884 : case OPT_TEST_IO_ADVISE_DISCARD_WINDOW :
885 1 : opt.file_mode = ADVISE_DISCARD_WINDOW;
886 1 : break;
887 : case OPT_TEST_IO_ADVISE_DIRECT :
888 0 : opt.file_mode = ADVISE_DIRECT;
889 0 : break;
890 : case OPT_TEST_PARITY_LIMIT :
891 269 : opt.parity_limit_size = atoll(optarg);
892 269 : break;
893 : case OPT_TEST_SKIP_CONTENT_WRITE :
894 0 : opt.skip_content_write = 1;
895 0 : break;
896 : case OPT_TEST_SKIP_SPACE_HOLDER :
897 0 : opt.skip_space_holder = 1;
898 0 : break;
899 : case OPT_TEST_FORMAT :
900 3 : if (strcmp(optarg, "file") == 0)
901 1 : FMT_MODE = FMT_FILE;
902 2 : else if (strcmp(optarg, "disk") == 0)
903 1 : FMT_MODE = FMT_DISK;
904 1 : else if (strcmp(optarg, "path") == 0)
905 1 : FMT_MODE = FMT_PATH;
906 : else {
907 : /* LCOV_EXCL_START */
908 : log_fatal("Unknown format '%s'\n", optarg);
909 : exit(EXIT_FAILURE);
910 : /* LCOV_EXCL_STOP */
911 : }
912 3 : break;
913 : default :
914 : /* LCOV_EXCL_START */
915 : log_fatal("Unknown option '%c'\n", (char)c);
916 : exit(EXIT_FAILURE);
917 : /* LCOV_EXCL_STOP */
918 : }
919 : }
920 :
921 271 : os_init(opt.force_scan_winfind);
922 271 : raid_init();
923 271 : crc32c_init();
924 :
925 271 : if (speedtest != 0) {
926 4 : speed(period);
927 4 : os_done();
928 4 : exit(EXIT_SUCCESS);
929 : }
930 :
931 267 : if (gen_conf != 0) {
932 1 : generate_configuration(gen_conf);
933 1 : os_done();
934 1 : exit(EXIT_SUCCESS);
935 : }
936 :
937 266 : if (optind + 1 != argc) {
938 : /* LCOV_EXCL_START */
939 : usage();
940 : exit(EXIT_FAILURE);
941 : /* LCOV_EXCL_STOP */
942 : }
943 :
944 266 : command = argv[optind];
945 266 : if (strcmp(command, "diff") == 0) {
946 8 : operation = OPERATION_DIFF;
947 258 : } else if (strcmp(argv[optind], "sync") == 0) {
948 89 : operation = OPERATION_SYNC;
949 169 : } else if (strcmp(argv[optind], "check") == 0) {
950 75 : operation = OPERATION_CHECK;
951 94 : } else if (strcmp(argv[optind], "fix") == 0) {
952 46 : operation = OPERATION_FIX;
953 48 : } else if (strcmp(argv[optind], "test-dry") == 0) {
954 2 : operation = OPERATION_DRY;
955 46 : } else if (strcmp(argv[optind], "dup") == 0) {
956 3 : operation = OPERATION_DUP;
957 43 : } else if (strcmp(argv[optind], "list") == 0) {
958 5 : operation = OPERATION_LIST;
959 38 : } else if (strcmp(argv[optind], "pool") == 0) {
960 3 : operation = OPERATION_POOL;
961 35 : } else if (strcmp(argv[optind], "rehash") == 0) {
962 1 : operation = OPERATION_REHASH;
963 34 : } else if (strcmp(argv[optind], "scrub") == 0) {
964 11 : operation = OPERATION_SCRUB;
965 23 : } else if (strcmp(argv[optind], "status") == 0) {
966 15 : operation = OPERATION_STATUS;
967 8 : } else if (strcmp(argv[optind], "test-rewrite") == 0) {
968 1 : operation = OPERATION_REWRITE;
969 7 : } else if (strcmp(argv[optind], "test-read") == 0) {
970 1 : operation = OPERATION_READ;
971 6 : } else if (strcmp(argv[optind], "touch") == 0) {
972 1 : operation = OPERATION_TOUCH;
973 5 : } else if (strcmp(argv[optind], "up") == 0) {
974 1 : operation = OPERATION_SPINUP;
975 4 : } else if (strcmp(argv[optind], "down") == 0) {
976 1 : operation = OPERATION_SPINDOWN;
977 3 : } else if (strcmp(argv[optind], "devices") == 0) {
978 1 : operation = OPERATION_DEVICES;
979 2 : } else if (strcmp(argv[optind], "smart") == 0) {
980 2 : operation = OPERATION_SMART;
981 : } else {
982 : /* LCOV_EXCL_START */
983 : log_fatal("Unknown command '%s'\n", argv[optind]);
984 : exit(EXIT_FAILURE);
985 : /* LCOV_EXCL_STOP */
986 : }
987 :
988 : /* check options compatibility */
989 266 : switch (operation) {
990 : case OPERATION_CHECK :
991 75 : break;
992 : default :
993 191 : if (opt.auditonly) {
994 : /* LCOV_EXCL_START */
995 : log_fatal("You cannot use -a, --audit-only with the '%s' command\n", command);
996 : exit(EXIT_FAILURE);
997 : /* LCOV_EXCL_STOP */
998 : }
999 : }
1000 :
1001 266 : switch (operation) {
1002 : case OPERATION_FIX :
1003 : case OPERATION_CHECK :
1004 121 : break;
1005 : default :
1006 145 : if (opt.force_device) {
1007 : /* LCOV_EXCL_START */
1008 : log_fatal("You cannot use -D, --force-device with the '%s' command\n", command);
1009 : exit(EXIT_FAILURE);
1010 : /* LCOV_EXCL_STOP */
1011 : }
1012 : }
1013 :
1014 266 : switch (operation) {
1015 : case OPERATION_SYNC :
1016 : case OPERATION_CHECK :
1017 : case OPERATION_FIX :
1018 210 : break;
1019 : default :
1020 56 : if (opt.force_nocopy) {
1021 : /* LCOV_EXCL_START */
1022 : log_fatal("You cannot use -N, --force-nocopy with the '%s' command\n", command);
1023 : exit(EXIT_FAILURE);
1024 : /* LCOV_EXCL_STOP */
1025 : }
1026 : }
1027 :
1028 266 : switch (operation) {
1029 : case OPERATION_SYNC :
1030 89 : break;
1031 : default :
1032 177 : if (opt.prehash) {
1033 : /* LCOV_EXCL_START */
1034 : log_fatal("You cannot use -h, --pre-hash with the '%s' command\n", command);
1035 : exit(EXIT_FAILURE);
1036 : /* LCOV_EXCL_STOP */
1037 : }
1038 :
1039 177 : if (opt.force_full) {
1040 : /* LCOV_EXCL_START */
1041 : log_fatal("You cannot use -F, --force-full with the '%s' command\n", command);
1042 : exit(EXIT_FAILURE);
1043 : /* LCOV_EXCL_STOP */
1044 : }
1045 :
1046 177 : if (opt.force_realloc) {
1047 : /* LCOV_EXCL_START */
1048 : log_fatal("You cannot use -R, --force-realloc with the '%s' command\n", command);
1049 : exit(EXIT_FAILURE);
1050 : /* LCOV_EXCL_STOP */
1051 : }
1052 : }
1053 :
1054 266 : if (opt.force_full && opt.force_nocopy) {
1055 : /* LCOV_EXCL_START */
1056 : log_fatal("You cannot use the -F, --force-full and -N, --force-nocopy options at the same time\n");
1057 : exit(EXIT_FAILURE);
1058 : /* LCOV_EXCL_STOP */
1059 : }
1060 :
1061 266 : if (opt.force_realloc && opt.force_nocopy) {
1062 : /* LCOV_EXCL_START */
1063 : log_fatal("You cannot use the -R, --force-realloc and -N, --force-nocopy options at the same time\n");
1064 : exit(EXIT_FAILURE);
1065 : /* LCOV_EXCL_STOP */
1066 : }
1067 :
1068 266 : if (opt.force_realloc && opt.force_full) {
1069 : /* LCOV_EXCL_START */
1070 : log_fatal("You cannot use the -R, --force-realloc and -F, --force-full options at the same time\n");
1071 : exit(EXIT_FAILURE);
1072 : /* LCOV_EXCL_STOP */
1073 : }
1074 :
1075 266 : if (opt.prehash && opt.force_nocopy) {
1076 : /* LCOV_EXCL_START */
1077 : log_fatal("You cannot use the -h, --pre-hash and -N, --force-nocopy options at the same time\n");
1078 : exit(EXIT_FAILURE);
1079 : /* LCOV_EXCL_STOP */
1080 : }
1081 :
1082 266 : switch (operation) {
1083 : case OPERATION_CHECK :
1084 : case OPERATION_FIX :
1085 : case OPERATION_DRY :
1086 123 : break;
1087 : default :
1088 141 : if (!tommy_list_empty(&filterlist_disk)) {
1089 : /* LCOV_EXCL_START */
1090 : log_fatal("You cannot use -d, --filter-disk with the '%s' command\n", command);
1091 : exit(EXIT_FAILURE);
1092 : /* LCOV_EXCL_STOP */
1093 : }
1094 : /* follow */
1095 : case OPERATION_SPINUP :
1096 : case OPERATION_SPINDOWN :
1097 143 : if (!tommy_list_empty(&filterlist_file)) {
1098 : /* LCOV_EXCL_START */
1099 : log_fatal("You cannot use -f, --filter with the '%s' command\n", command);
1100 : exit(EXIT_FAILURE);
1101 : /* LCOV_EXCL_STOP */
1102 : }
1103 143 : if (filter_missing != 0) {
1104 : /* LCOV_EXCL_START */
1105 : log_fatal("You cannot use -m, --filter-missing with the '%s' command\n", command);
1106 : exit(EXIT_FAILURE);
1107 : /* LCOV_EXCL_STOP */
1108 : }
1109 143 : if (filter_error != 0) {
1110 : /* LCOV_EXCL_START */
1111 : log_fatal("You cannot use -e, --filter-error with the '%s' command\n", command);
1112 : exit(EXIT_FAILURE);
1113 : /* LCOV_EXCL_STOP */
1114 : }
1115 : }
1116 :
1117 : /* errors must be always fixed on all disks */
1118 : /* because we don't keep the information on what disk is the error */
1119 266 : if (filter_error != 0 && !tommy_list_empty(&filterlist_disk)) {
1120 : /* LCOV_EXCL_START */
1121 : log_fatal("You cannot use -e, --filter-error and -d, --filter-disk at the same time\n");
1122 : exit(EXIT_FAILURE);
1123 : /* LCOV_EXCL_STOP */
1124 : }
1125 :
1126 266 : switch (operation) {
1127 : case OPERATION_CHECK :
1128 : case OPERATION_FIX :
1129 121 : break;
1130 : default :
1131 145 : if (import_timestamp != 0 || import_content != 0) {
1132 : /* LCOV_EXCL_START */
1133 : log_fatal("You cannot import with the '%s' command\n", command);
1134 : exit(EXIT_FAILURE);
1135 : /* LCOV_EXCL_STOP */
1136 : }
1137 : }
1138 :
1139 266 : switch (operation) {
1140 : case OPERATION_LIST :
1141 : case OPERATION_DUP :
1142 : case OPERATION_STATUS :
1143 : case OPERATION_REWRITE :
1144 : case OPERATION_READ :
1145 : case OPERATION_REHASH :
1146 : case OPERATION_SPINUP : /* we want to do it in different threads to avoid blocking */
1147 : /* avoid to check and access data disks if not needed */
1148 27 : opt.skip_disk_access = 1;
1149 27 : break;
1150 : }
1151 :
1152 266 : switch (operation) {
1153 : case OPERATION_DIFF :
1154 : case OPERATION_LIST :
1155 : case OPERATION_DUP :
1156 : case OPERATION_POOL :
1157 : case OPERATION_STATUS :
1158 : case OPERATION_REWRITE :
1159 : case OPERATION_READ :
1160 : case OPERATION_REHASH :
1161 : case OPERATION_TOUCH :
1162 : case OPERATION_SPINUP : /* we want to do it in different threads to avoid blocking */
1163 : /* avoid to check and access parity disks if not needed */
1164 39 : opt.skip_parity_access = 1;
1165 39 : break;
1166 : }
1167 :
1168 266 : switch (operation) {
1169 : case OPERATION_FIX :
1170 : case OPERATION_CHECK :
1171 : /* avoid to stop processing if a content file is not accessible */
1172 121 : opt.skip_content_access = 1;
1173 121 : break;
1174 : }
1175 :
1176 266 : switch (operation) {
1177 : case OPERATION_DIFF :
1178 : case OPERATION_LIST :
1179 : case OPERATION_DUP :
1180 : case OPERATION_POOL :
1181 : case OPERATION_TOUCH :
1182 : case OPERATION_SPINUP :
1183 : case OPERATION_SPINDOWN :
1184 : case OPERATION_DEVICES :
1185 : case OPERATION_SMART :
1186 25 : opt.skip_self = 1;
1187 25 : break;
1188 : }
1189 :
1190 266 : switch (operation) {
1191 : #if HAVE_DIRECT_IO
1192 : case OPERATION_SYNC :
1193 : case OPERATION_SCRUB :
1194 : case OPERATION_DRY :
1195 102 : break;
1196 : #endif
1197 : default:
1198 : /* we allow direct IO only on some commands */
1199 164 : if (opt.file_mode == ADVISE_DIRECT)
1200 0 : opt.file_mode = ADVISE_SEQUENTIAL;
1201 164 : break;
1202 : }
1203 :
1204 266 : switch (operation) {
1205 : case OPERATION_DEVICES :
1206 : case OPERATION_SMART :
1207 : /* we may need to use these commands during operations */
1208 3 : opt.skip_lock = 1;
1209 3 : break;
1210 : }
1211 :
1212 266 : switch (operation) {
1213 : case OPERATION_SMART :
1214 : /* allow to run without configuration file */
1215 2 : opt.auto_conf = 1;
1216 2 : break;
1217 : }
1218 :
1219 : /* open the log file */
1220 266 : log_open(log_file);
1221 :
1222 : /* print generic info into the log */
1223 266 : t = time(0);
1224 266 : tm = localtime(&t);
1225 266 : log_tag("version:%s\n", PACKAGE_VERSION);
1226 266 : log_tag("unixtime:%" PRIi64 "\n", (int64_t)t);
1227 266 : if (tm) {
1228 : char datetime[64];
1229 266 : strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S", tm);
1230 266 : log_tag("time:%s\n", datetime);
1231 : }
1232 266 : log_tag("command:%s\n", command);
1233 4066 : for (i = 0; i < argc; ++i)
1234 3800 : log_tag("argv:%u:%s\n", i, argv[i]);
1235 266 : log_flush();
1236 :
1237 266 : if (!opt.skip_self)
1238 1 : selftest();
1239 :
1240 266 : state_init(&state);
1241 :
1242 : /* read the configuration file */
1243 266 : state_config(&state, conf, command, &opt, &filterlist_disk);
1244 :
1245 : /* set the raid mode */
1246 265 : raid_mode(state.raid_mode);
1247 :
1248 : #if HAVE_LOCKFILE
1249 : /* create the lock file */
1250 265 : if (!opt.skip_lock && state.lockfile[0]) {
1251 262 : lock = lock_lock(state.lockfile);
1252 262 : if (lock == -1) {
1253 : /* LCOV_EXCL_START */
1254 : if (errno != EWOULDBLOCK) {
1255 : log_fatal("Error creating the lock file '%s'. %s.\n", state.lockfile, strerror(errno));
1256 : } else {
1257 : log_fatal("The lock file '%s' is already locked!\n", state.lockfile);
1258 : log_fatal("SnapRAID is already in use!\n");
1259 : }
1260 : exit(EXIT_FAILURE);
1261 : /* LCOV_EXCL_STOP */
1262 : }
1263 : }
1264 : #else
1265 : (void)lock;
1266 : #endif
1267 :
1268 265 : if (operation == OPERATION_DIFF) {
1269 8 : state_read(&state);
1270 :
1271 8 : ret = state_diff(&state);
1272 :
1273 : /* abort if sync needed */
1274 8 : if (ret > 0)
1275 7 : exit(EXIT_SYNC_NEEDED);
1276 257 : } else if (operation == OPERATION_SYNC) {
1277 :
1278 : /* in the next state read ensures to clear all the past hashes in case */
1279 : /* we are reading from an incomplete sync */
1280 : /* The indeterminate hash are only for CHG/DELETED blocks for which we don't */
1281 : /* know if the previous interrupted sync was able to update or not the parity. */
1282 : /* The sync process instead needs to trust this information because it's used */
1283 : /* to avoid to recompute the parity if all the input are equals as before. */
1284 :
1285 : /* In these cases we don't know if the old state is still the one */
1286 : /* stored inside the parity, because after an aborted sync, the parity */
1287 : /* may be or may be not have been updated with the data that may be now */
1288 : /* deleted. Then we reset the hash to a bogus value. */
1289 :
1290 : /* An example for CHG blocks is: */
1291 : /* - One file is added creating a CHG block with ZERO state */
1292 : /* - Sync aborted after updating the parity to the new state, */
1293 : /* but without saving the content file representing this new BLK state. */
1294 : /* - File is now deleted after the aborted sync */
1295 : /* - Sync again, deleting the blocks over the CHG ones */
1296 : /* with the hash of CHG blocks not representing the real parity state */
1297 :
1298 : /* An example for DELETED blocks is: */
1299 : /* - One file is deleted creating DELETED blocks */
1300 : /* - Sync aborted after, updating the parity to the new state, */
1301 : /* but without saving the content file representing this new EMPTY state. */
1302 : /* - Another file is added again over the DELETE ones */
1303 : /* with the hash of DELETED blocks not representing the real parity state */
1304 89 : state.clear_past_hash = 1;
1305 :
1306 89 : state_read(&state);
1307 :
1308 88 : state_scan(&state);
1309 :
1310 : /* refresh the size info before the content write */
1311 87 : state_refresh(&state);
1312 :
1313 87 : memory();
1314 :
1315 : /* intercept signals while operating */
1316 87 : signal_init();
1317 :
1318 : /* run a test command if required */
1319 87 : if (run != 0) {
1320 10 : ret = system(run); /* ignore error */
1321 10 : if (ret != 0) {
1322 : /* LCOV_EXCL_START */
1323 : log_fatal("Error in running command '%s'.\n", run);
1324 : exit(EXIT_FAILURE);
1325 : /* LCOV_EXCL_STOP */
1326 : }
1327 : }
1328 :
1329 : /* waits some time to ensure that any concurrent modification done at the files, */
1330 : /* using the same mtime read by the scan process, will be read by sync. */
1331 : /* Note that any later modification done, potentially not read by sync, will have */
1332 : /* a different mtime, and it will be synchronized at the next sync. */
1333 : /* The worst case is the FAT file-system with a two seconds resolution for mtime. */
1334 : /* If you don't use FAT, the wait is not needed, because most file-systems have now */
1335 : /* at least microseconds resolution, but better to be safe. */
1336 87 : if (!opt.skip_self)
1337 0 : sleep(2);
1338 :
1339 87 : ret = state_sync(&state, blockstart, blockcount);
1340 :
1341 : /* save the new state if required */
1342 87 : if (!opt.kill_after_sync) {
1343 77 : if ((state.need_write || state.opt.force_content_write))
1344 58 : state_write(&state);
1345 : } else {
1346 10 : log_fatal("WARNING! Skipped state write for --test-kill-after-sync option.\n");
1347 : }
1348 :
1349 : /* abort if required */
1350 87 : if (ret != 0) {
1351 : /* LCOV_EXCL_START */
1352 : exit(EXIT_FAILURE);
1353 : /* LCOV_EXCL_STOP */
1354 : }
1355 168 : } else if (operation == OPERATION_DRY) {
1356 2 : state_read(&state);
1357 :
1358 : /* filter */
1359 2 : state_skip(&state);
1360 2 : state_filter(&state, &filterlist_file, &filterlist_disk, filter_missing, filter_error);
1361 :
1362 2 : memory();
1363 :
1364 : /* intercept signals while operating */
1365 2 : signal_init();
1366 :
1367 2 : state_dry(&state, blockstart, blockcount);
1368 166 : } else if (operation == OPERATION_REHASH) {
1369 1 : state_read(&state);
1370 :
1371 : /* intercept signals while operating */
1372 1 : signal_init();
1373 :
1374 1 : state_rehash(&state);
1375 :
1376 : /* save the new state if required */
1377 1 : if (state.need_write)
1378 1 : state_write(&state);
1379 165 : } else if (operation == OPERATION_SCRUB) {
1380 11 : state_read(&state);
1381 :
1382 11 : memory();
1383 :
1384 : /* intercept signals while operating */
1385 11 : signal_init();
1386 :
1387 11 : ret = state_scrub(&state, plan, olderthan);
1388 :
1389 : /* save the new state if required */
1390 11 : if (state.need_write || state.opt.force_content_write)
1391 10 : state_write(&state);
1392 :
1393 : /* abort if required */
1394 11 : if (ret != 0) {
1395 : /* LCOV_EXCL_START */
1396 : exit(EXIT_FAILURE);
1397 : /* LCOV_EXCL_STOP */
1398 : }
1399 154 : } else if (operation == OPERATION_REWRITE) {
1400 1 : state_read(&state);
1401 :
1402 : /* intercept signals while operating */
1403 1 : signal_init();
1404 :
1405 1 : state_write(&state);
1406 :
1407 1 : memory();
1408 153 : } else if (operation == OPERATION_READ) {
1409 1 : state_read(&state);
1410 :
1411 1 : memory();
1412 152 : } else if (operation == OPERATION_TOUCH) {
1413 1 : state_read(&state);
1414 :
1415 1 : state_touch(&state);
1416 :
1417 : /* intercept signals while operating */
1418 1 : signal_init();
1419 :
1420 1 : state_write(&state);
1421 :
1422 1 : memory();
1423 151 : } else if (operation == OPERATION_SPINUP) {
1424 1 : state_device(&state, DEVICE_UP, &filterlist_disk);
1425 150 : } else if (operation == OPERATION_SPINDOWN) {
1426 1 : state_device(&state, DEVICE_DOWN, &filterlist_disk);
1427 149 : } else if (operation == OPERATION_DEVICES) {
1428 1 : state_device(&state, DEVICE_LIST, 0);
1429 148 : } else if (operation == OPERATION_SMART) {
1430 2 : state_device(&state, DEVICE_SMART, 0);
1431 146 : } else if (operation == OPERATION_STATUS) {
1432 15 : state_read(&state);
1433 :
1434 15 : memory();
1435 :
1436 15 : state_status(&state);
1437 131 : } else if (operation == OPERATION_DUP) {
1438 3 : state_read(&state);
1439 :
1440 3 : state_dup(&state);
1441 128 : } else if (operation == OPERATION_LIST) {
1442 5 : state_read(&state);
1443 :
1444 5 : state_list(&state);
1445 123 : } else if (operation == OPERATION_POOL) {
1446 3 : state_read(&state);
1447 :
1448 3 : state_pool(&state);
1449 : } else {
1450 120 : state_read(&state);
1451 :
1452 : /* if we are also trying to recover */
1453 120 : if (!state.opt.auditonly) {
1454 : /* import the user specified dirs */
1455 114 : if (import_timestamp != 0)
1456 1 : state_search(&state, import_timestamp);
1457 114 : if (import_content != 0)
1458 1 : state_import(&state, import_content);
1459 :
1460 : /* import from all the array */
1461 114 : if (!state.opt.force_nocopy)
1462 114 : state_search_array(&state);
1463 : }
1464 :
1465 : /* filter */
1466 120 : state_skip(&state);
1467 120 : state_filter(&state, &filterlist_file, &filterlist_disk, filter_missing, filter_error);
1468 :
1469 120 : memory();
1470 :
1471 : /* intercept signals while operating */
1472 120 : signal_init();
1473 :
1474 120 : if (operation == OPERATION_CHECK) {
1475 75 : ret = state_check(&state, 0, blockstart, blockcount);
1476 : } else { /* it's fix */
1477 45 : ret = state_check(&state, 1, blockstart, blockcount);
1478 : }
1479 :
1480 : /* abort if required */
1481 120 : if (ret != 0) {
1482 : /* LCOV_EXCL_START */
1483 : exit(EXIT_FAILURE);
1484 : /* LCOV_EXCL_STOP */
1485 : }
1486 : }
1487 :
1488 : /* close log file */
1489 243 : log_close(log_file);
1490 :
1491 : #if HAVE_LOCKFILE
1492 243 : if (!opt.skip_lock && state.lockfile[0]) {
1493 241 : if (lock_unlock(lock) == -1) {
1494 : /* LCOV_EXCL_START */
1495 : log_fatal("Error closing the lock file '%s'. %s.\n", state.lockfile, strerror(errno));
1496 : exit(EXIT_FAILURE);
1497 : /* LCOV_EXCL_STOP */
1498 : }
1499 : }
1500 : #endif
1501 :
1502 243 : state_done(&state);
1503 243 : tommy_list_foreach(&filterlist_file, (tommy_foreach_func*)filter_free);
1504 243 : tommy_list_foreach(&filterlist_disk, (tommy_foreach_func*)filter_free);
1505 :
1506 243 : os_done();
1507 243 : lock_done();
1508 :
1509 243 : return EXIT_SUCCESS;
1510 : }
1511 :
|