]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/fsck/fsck.c
build-sys: use #if Y instead of #ifdef Y everywhere
[thirdparty/systemd.git] / src / fsck / fsck.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
5 Copyright 2014 Holger Hans Peter Freyther
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdbool.h>
24 #include <stdio.h>
25 #include <sys/file.h>
26 #include <sys/prctl.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29
30 #include "sd-bus.h"
31 #include "sd-device.h"
32
33 #include "alloc-util.h"
34 #include "bus-common-errors.h"
35 #include "bus-error.h"
36 #include "bus-util.h"
37 #include "device-util.h"
38 #include "fd-util.h"
39 #include "fs-util.h"
40 #include "parse-util.h"
41 #include "path-util.h"
42 #include "proc-cmdline.h"
43 #include "process-util.h"
44 #include "signal-util.h"
45 #include "socket-util.h"
46 #include "special.h"
47 #include "stdio-util.h"
48 #include "util.h"
49
50 /* exit codes as defined in fsck(8) */
51 enum {
52 FSCK_SUCCESS = 0,
53 FSCK_ERROR_CORRECTED = 1,
54 FSCK_SYSTEM_SHOULD_REBOOT = 2,
55 FSCK_ERRORS_LEFT_UNCORRECTED = 4,
56 FSCK_OPERATIONAL_ERROR = 8,
57 FSCK_USAGE_OR_SYNTAX_ERROR = 16,
58 FSCK_USER_CANCELLED = 32,
59 FSCK_SHARED_LIB_ERROR = 128,
60 };
61
62 static bool arg_skip = false;
63 static bool arg_force = false;
64 static bool arg_show_progress = false;
65 static const char *arg_repair = "-a";
66
67 static void start_target(const char *target, const char *mode) {
68 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
69 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
70 int r;
71
72 assert(target);
73
74 r = bus_connect_system_systemd(&bus);
75 if (r < 0) {
76 log_error_errno(r, "Failed to get D-Bus connection: %m");
77 return;
78 }
79
80 log_info("Running request %s/start/replace", target);
81
82 /* Start these units only if we can replace base.target with it */
83 r = sd_bus_call_method(bus,
84 "org.freedesktop.systemd1",
85 "/org/freedesktop/systemd1",
86 "org.freedesktop.systemd1.Manager",
87 "StartUnitReplace",
88 &error,
89 NULL,
90 "sss", "basic.target", target, mode);
91
92 /* Don't print a warning if we aren't called during startup */
93 if (r < 0 && !sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
94 log_error("Failed to start unit: %s", bus_error_message(&error, r));
95 }
96
97 static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
98 int r;
99
100 assert(key);
101
102 if (streq(key, "fsck.mode")) {
103
104 if (proc_cmdline_value_missing(key, value))
105 return 0;
106
107 if (streq(value, "auto"))
108 arg_force = arg_skip = false;
109 else if (streq(value, "force"))
110 arg_force = true;
111 else if (streq(value, "skip"))
112 arg_skip = true;
113 else
114 log_warning("Invalid fsck.mode= parameter '%s'. Ignoring.", value);
115
116 } else if (streq(key, "fsck.repair")) {
117
118 if (proc_cmdline_value_missing(key, value))
119 return 0;
120
121 if (streq(value, "preen"))
122 arg_repair = "-a";
123 else {
124 r = parse_boolean(value);
125 if (r > 0)
126 arg_repair = "-y";
127 else if (r == 0)
128 arg_repair = "-n";
129 else
130 log_warning("Invalid fsck.repair= parameter '%s'. Ignoring.", value);
131 }
132 }
133
134 #if HAVE_SYSV_COMPAT
135 else if (streq(key, "fastboot") && !value) {
136 log_warning("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line.");
137 arg_skip = true;
138
139 } else if (streq(key, "forcefsck") && !value) {
140 log_warning("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line.");
141 arg_force = true;
142 }
143 #endif
144
145 return 0;
146 }
147
148 static void test_files(void) {
149
150 #if HAVE_SYSV_COMPAT
151 if (access("/fastboot", F_OK) >= 0) {
152 log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system.");
153 arg_skip = true;
154 }
155
156 if (access("/forcefsck", F_OK) >= 0) {
157 log_error("Please pass 'fsck.mode=force' on the kernel command line rather than creating /forcefsck on the root file system.");
158 arg_force = true;
159 }
160 #endif
161
162 arg_show_progress = access("/run/systemd/show-status", F_OK) >= 0;
163 }
164
165 static double percent(int pass, unsigned long cur, unsigned long max) {
166 /* Values stolen from e2fsck */
167
168 static const int pass_table[] = {
169 0, 70, 90, 92, 95, 100
170 };
171
172 if (pass <= 0)
173 return 0.0;
174
175 if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
176 return 100.0;
177
178 return (double) pass_table[pass-1] +
179 ((double) pass_table[pass] - (double) pass_table[pass-1]) *
180 (double) cur / (double) max;
181 }
182
183 static int process_progress(int fd) {
184 _cleanup_fclose_ FILE *console = NULL, *f = NULL;
185 usec_t last = 0;
186 bool locked = false;
187 int clear = 0, r;
188
189 /* No progress pipe to process? Then we are a NOP. */
190 if (fd < 0)
191 return 0;
192
193 f = fdopen(fd, "re");
194 if (!f) {
195 safe_close(fd);
196 return -errno;
197 }
198
199 console = fopen("/dev/console", "we");
200 if (!console)
201 return -ENOMEM;
202
203 for (;;) {
204 int pass, m;
205 unsigned long cur, max;
206 _cleanup_free_ char *device = NULL;
207 double p;
208 usec_t t;
209
210 if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4) {
211
212 if (ferror(f))
213 r = log_warning_errno(errno, "Failed to read from progress pipe: %m");
214 else if (feof(f))
215 r = 0;
216 else {
217 log_warning("Failed to parse progress pipe data");
218 r = -EBADMSG;
219 }
220 break;
221 }
222
223 /* Only show one progress counter at max */
224 if (!locked) {
225 if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0)
226 continue;
227
228 locked = true;
229 }
230
231 /* Only update once every 50ms */
232 t = now(CLOCK_MONOTONIC);
233 if (last + 50 * USEC_PER_MSEC > t)
234 continue;
235
236 last = t;
237
238 p = percent(pass, cur, max);
239 fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
240 fflush(console);
241
242 if (m > clear)
243 clear = m;
244 }
245
246 if (clear > 0) {
247 unsigned j;
248
249 fputc('\r', console);
250 for (j = 0; j < (unsigned) clear; j++)
251 fputc(' ', console);
252 fputc('\r', console);
253 fflush(console);
254 }
255
256 return r;
257 }
258
259 static int fsck_progress_socket(void) {
260 static const union sockaddr_union sa = {
261 .un.sun_family = AF_UNIX,
262 .un.sun_path = "/run/systemd/fsck.progress",
263 };
264
265 int fd, r;
266
267 fd = socket(AF_UNIX, SOCK_STREAM, 0);
268 if (fd < 0)
269 return log_warning_errno(errno, "socket(): %m");
270
271 if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) {
272 r = log_full_errno(IN_SET(errno, ECONNREFUSED, ENOENT) ? LOG_DEBUG : LOG_WARNING,
273 errno, "Failed to connect to progress socket %s, ignoring: %m", sa.un.sun_path);
274 safe_close(fd);
275 return r;
276 }
277
278 return fd;
279 }
280
281 int main(int argc, char *argv[]) {
282 _cleanup_close_pair_ int progress_pipe[2] = { -1, -1 };
283 _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
284 const char *device, *type;
285 bool root_directory;
286 siginfo_t status;
287 struct stat st;
288 int r;
289 pid_t pid;
290
291 if (argc > 2) {
292 log_error("This program expects one or no arguments.");
293 return EXIT_FAILURE;
294 }
295
296 log_set_target(LOG_TARGET_AUTO);
297 log_parse_environment();
298 log_open();
299
300 umask(0022);
301
302 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
303 if (r < 0)
304 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
305
306 test_files();
307
308 if (!arg_force && arg_skip) {
309 r = 0;
310 goto finish;
311 }
312
313 if (argc > 1) {
314 device = argv[1];
315
316 if (stat(device, &st) < 0) {
317 r = log_error_errno(errno, "Failed to stat %s: %m", device);
318 goto finish;
319 }
320
321 if (!S_ISBLK(st.st_mode)) {
322 log_error("%s is not a block device.", device);
323 r = -EINVAL;
324 goto finish;
325 }
326
327 r = sd_device_new_from_devnum(&dev, 'b', st.st_rdev);
328 if (r < 0) {
329 log_error_errno(r, "Failed to detect device %s: %m", device);
330 goto finish;
331 }
332
333 root_directory = false;
334 } else {
335 struct timespec times[2];
336
337 /* Find root device */
338
339 if (stat("/", &st) < 0) {
340 r = log_error_errno(errno, "Failed to stat() the root directory: %m");
341 goto finish;
342 }
343
344 /* Virtual root devices don't need an fsck */
345 if (major(st.st_dev) == 0) {
346 log_debug("Root directory is virtual or btrfs, skipping check.");
347 r = 0;
348 goto finish;
349 }
350
351 /* check if we are already writable */
352 times[0] = st.st_atim;
353 times[1] = st.st_mtim;
354
355 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
356 log_info("Root directory is writable, skipping check.");
357 r = 0;
358 goto finish;
359 }
360
361 r = sd_device_new_from_devnum(&dev, 'b', st.st_dev);
362 if (r < 0) {
363 log_error_errno(r, "Failed to detect root device: %m");
364 goto finish;
365 }
366
367 r = sd_device_get_devname(dev, &device);
368 if (r < 0) {
369 log_error_errno(r, "Failed to detect device node of root directory: %m");
370 goto finish;
371 }
372
373 root_directory = true;
374 }
375
376 r = sd_device_get_property_value(dev, "ID_FS_TYPE", &type);
377 if (r >= 0) {
378 r = fsck_exists(type);
379 if (r < 0)
380 log_warning_errno(r, "Couldn't detect if fsck.%s may be used for %s, proceeding: %m", type, device);
381 else if (r == 0) {
382 log_info("fsck.%s doesn't exist, not checking file system on %s.", type, device);
383 goto finish;
384 }
385 }
386
387 if (arg_show_progress) {
388 if (pipe(progress_pipe) < 0) {
389 r = log_error_errno(errno, "pipe(): %m");
390 goto finish;
391 }
392 }
393
394 pid = fork();
395 if (pid < 0) {
396 r = log_error_errno(errno, "fork(): %m");
397 goto finish;
398 }
399 if (pid == 0) {
400 char dash_c[sizeof("-C")-1 + DECIMAL_STR_MAX(int) + 1];
401 int progress_socket = -1;
402 const char *cmdline[9];
403 int i = 0;
404
405 /* Child */
406
407 (void) reset_all_signal_handlers();
408 (void) reset_signal_mask();
409 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
410
411 /* Close the reading side of the progress pipe */
412 progress_pipe[0] = safe_close(progress_pipe[0]);
413
414 /* Try to connect to a progress management daemon, if there is one */
415 progress_socket = fsck_progress_socket();
416 if (progress_socket >= 0) {
417 /* If this worked we close the progress pipe early, and just use the socket */
418 progress_pipe[1] = safe_close(progress_pipe[1]);
419 xsprintf(dash_c, "-C%i", progress_socket);
420 } else if (progress_pipe[1] >= 0) {
421 /* Otherwise if we have the progress pipe to our own local handle, we use it */
422 xsprintf(dash_c, "-C%i", progress_pipe[1]);
423 } else
424 dash_c[0] = 0;
425
426 cmdline[i++] = "/sbin/fsck";
427 cmdline[i++] = arg_repair;
428 cmdline[i++] = "-T";
429
430 /*
431 * Since util-linux v2.25 fsck uses /run/fsck/<diskname>.lock files.
432 * The previous versions use flock for the device and conflict with
433 * udevd, see https://bugs.freedesktop.org/show_bug.cgi?id=79576#c5
434 */
435 cmdline[i++] = "-l";
436
437 if (!root_directory)
438 cmdline[i++] = "-M";
439
440 if (arg_force)
441 cmdline[i++] = "-f";
442
443 if (!isempty(dash_c))
444 cmdline[i++] = dash_c;
445
446 cmdline[i++] = device;
447 cmdline[i++] = NULL;
448
449 execv(cmdline[0], (char**) cmdline);
450 _exit(FSCK_OPERATIONAL_ERROR);
451 }
452
453 progress_pipe[1] = safe_close(progress_pipe[1]);
454 (void) process_progress(progress_pipe[0]);
455 progress_pipe[0] = -1;
456
457 r = wait_for_terminate(pid, &status);
458 if (r < 0) {
459 log_error_errno(r, "waitid(): %m");
460 goto finish;
461 }
462
463 if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
464
465 if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
466 log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
467 else if (status.si_code == CLD_EXITED)
468 log_error("fsck failed with error code %i.", status.si_status);
469 else
470 log_error("fsck failed due to unknown reason.");
471
472 r = -EINVAL;
473
474 if (status.si_code == CLD_EXITED && (status.si_status & FSCK_SYSTEM_SHOULD_REBOOT) && root_directory)
475 /* System should be rebooted. */
476 start_target(SPECIAL_REBOOT_TARGET, "replace-irreversibly");
477 else if (status.si_code == CLD_EXITED && (status.si_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED)))
478 /* Some other problem */
479 start_target(SPECIAL_EMERGENCY_TARGET, "replace");
480 else {
481 log_warning("Ignoring error.");
482 r = 0;
483 }
484
485 } else
486 r = 0;
487
488 if (status.si_code == CLD_EXITED && (status.si_status & FSCK_ERROR_CORRECTED))
489 (void) touch("/run/systemd/quotacheck");
490
491 finish:
492 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
493 }