]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/fsck/fsck.c
tree-wide: use IN_SET macro (#6977)
[thirdparty/systemd.git] / src / fsck / fsck.c
CommitLineData
3d20ed6d
LP
1/***
2 This file is part of systemd.
3
4 Copyright 2010 Lennart Poettering
f1f0198c 5 Copyright 2014 Holger Hans Peter Freyther
3d20ed6d
LP
6
7 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
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
3d20ed6d
LP
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
5430f7f2 15 Lesser General Public License for more details.
3d20ed6d 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
3d20ed6d
LP
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
3d20ed6d 21#include <errno.h>
a84f5192 22#include <fcntl.h>
cf0fbc49
TA
23#include <stdbool.h>
24#include <stdio.h>
27d340c7 25#include <sys/file.h>
96d9117a 26#include <sys/prctl.h>
cf0fbc49
TA
27#include <sys/stat.h>
28#include <unistd.h>
3d20ed6d 29
0c842e0a 30#include "sd-bus.h"
9102fdc5 31#include "sd-device.h"
3d20ed6d 32
b5efdb8a 33#include "alloc-util.h"
96aad8d1 34#include "bus-common-errors.h"
3ffd4af2
LP
35#include "bus-error.h"
36#include "bus-util.h"
9102fdc5 37#include "device-util.h"
3ffd4af2 38#include "fd-util.h"
f4f15635 39#include "fs-util.h"
6bedfcbb 40#include "parse-util.h"
eb66db55 41#include "path-util.h"
4e731273 42#include "proc-cmdline.h"
3ffd4af2
LP
43#include "process-util.h"
44#include "signal-util.h"
ac6e2f0d 45#include "socket-util.h"
3ffd4af2 46#include "special.h"
15a5e950 47#include "stdio-util.h"
3ffd4af2 48#include "util.h"
3d20ed6d 49
91077af6
TA
50/* exit codes as defined in fsck(8) */
51enum {
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
3d20ed6d
LP
62static bool arg_skip = false;
63static bool arg_force = false;
96d9117a 64static bool arg_show_progress = false;
f1f0198c 65static const char *arg_repair = "-a";
3d20ed6d 66
48db40b3 67static void start_target(const char *target, const char *mode) {
4afd3348
LP
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;
0c842e0a 70 int r;
3d20ed6d
LP
71
72 assert(target);
73
266f3e26 74 r = bus_connect_system_systemd(&bus);
0c842e0a 75 if (r < 0) {
da927ba9 76 log_error_errno(r, "Failed to get D-Bus connection: %m");
0c842e0a 77 return;
3d20ed6d
LP
78 }
79
0c842e0a 80 log_info("Running request %s/start/replace", target);
3d20ed6d 81
90bb85e1 82 /* Start these units only if we can replace base.target with it */
0c842e0a
TG
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,
48db40b3 90 "sss", "basic.target", target, mode);
3d20ed6d 91
5220a6f3
LP
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))
24b52437 94 log_error("Failed to start unit: %s", bus_error_message(&error, r));
3d20ed6d
LP
95}
96
96287a49 97static int parse_proc_cmdline_item(const char *key, const char *value, void *data) {
24b52437
LP
98 int r;
99
100 assert(key);
059cb385 101
1d84ad94
LP
102 if (streq(key, "fsck.mode")) {
103
104 if (proc_cmdline_value_missing(key, value))
105 return 0;
059cb385
LP
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
85013844
LP
114 log_warning("Invalid fsck.mode= parameter '%s'. Ignoring.", value);
115
1d84ad94
LP
116 } else if (streq(key, "fsck.repair")) {
117
118 if (proc_cmdline_value_missing(key, value))
119 return 0;
f1f0198c
HHPF
120
121 if (streq(value, "preen"))
122 arg_repair = "-a";
24b52437
LP
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 }
85013844
LP
132 }
133
32f992a5 134#ifdef HAVE_SYSV_COMPAT
059cb385
LP
135 else if (streq(key, "fastboot") && !value) {
136 log_warning("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line.");
141a79f4 137 arg_skip = true;
85013844 138
059cb385
LP
139 } else if (streq(key, "forcefsck") && !value) {
140 log_warning("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line.");
141a79f4 141 arg_force = true;
3d20ed6d 142 }
141a79f4 143#endif
3d20ed6d 144
3d20ed6d
LP
145 return 0;
146}
147
148static void test_files(void) {
85013844 149
32f992a5
LP
150#ifdef 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.");
3d20ed6d 153 arg_skip = true;
32f992a5 154 }
3d20ed6d 155
32f992a5
LP
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.");
3d20ed6d 158 arg_force = true;
32f992a5
LP
159 }
160#endif
27d340c7 161
96d9117a 162 arg_show_progress = access("/run/systemd/show-status", F_OK) >= 0;
27d340c7
LP
163}
164
96d9117a
LP
165static 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
27d340c7
LP
170 };
171
96d9117a
LP
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
183static 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;
ac6e2f0d 206 _cleanup_free_ char *device = NULL;
96d9117a
LP
207 double p;
208 usec_t t;
27d340c7 209
96d9117a
LP
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 }
27d340c7 220 break;
96d9117a
LP
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 }
27d340c7 230
27d340c7
LP
231 /* Only update once every 50ms */
232 t = now(CLOCK_MONOTONIC);
e375825d 233 if (last + 50 * USEC_PER_MSEC > t)
27d340c7 234 continue;
27d340c7
LP
235
236 last = t;
237
96d9117a
LP
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;
27d340c7
LP
244 }
245
96d9117a
LP
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
259static 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
fc2fffe7 271 if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) {
3742095b 272 r = log_full_errno(IN_SET(errno, ECONNREFUSED, ENOENT) ? LOG_DEBUG : LOG_WARNING,
96d9117a
LP
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;
3d20ed6d
LP
279}
280
281int main(int argc, char *argv[]) {
96d9117a 282 _cleanup_close_pair_ int progress_pipe[2] = { -1, -1 };
4afd3348 283 _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
94192cda 284 const char *device, *type;
dc8e15c2 285 bool root_directory;
96d9117a 286 siginfo_t status;
94192cda 287 struct stat st;
96d9117a
LP
288 int r;
289 pid_t pid;
3d20ed6d 290
a9e1f5ec
LP
291 if (argc > 2) {
292 log_error("This program expects one or no arguments.");
3d20ed6d
LP
293 return EXIT_FAILURE;
294 }
295
27d340c7 296 log_set_target(LOG_TARGET_AUTO);
3d20ed6d
LP
297 log_parse_environment();
298 log_open();
299
4c12626c
LP
300 umask(0022);
301
1d84ad94 302 r = proc_cmdline_parse(parse_proc_cmdline_item, NULL, PROC_CMDLINE_STRIP_RD_PREFIX);
e7a3aa3d
LP
303 if (r < 0)
304 log_warning_errno(r, "Failed to parse kernel command line, ignoring: %m");
b5884878 305
3d20ed6d
LP
306 test_files();
307
566690fd
LP
308 if (!arg_force && arg_skip) {
309 r = 0;
310 goto finish;
311 }
a9e1f5ec 312
dc8e15c2 313 if (argc > 1) {
a9e1f5ec 314 device = argv[1];
94192cda
ZJS
315
316 if (stat(device, &st) < 0) {
e7a3aa3d
LP
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;
566690fd 324 goto finish;
94192cda
ZJS
325 }
326
9102fdc5
TG
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);
566690fd 330 goto finish;
94192cda 331 }
e7a3aa3d
LP
332
333 root_directory = false;
dc8e15c2 334 } else {
a84f5192 335 struct timespec times[2];
3d20ed6d 336
a9e1f5ec
LP
337 /* Find root device */
338
339 if (stat("/", &st) < 0) {
566690fd
LP
340 r = log_error_errno(errno, "Failed to stat() the root directory: %m");
341 goto finish;
a9e1f5ec
LP
342 }
343
344 /* Virtual root devices don't need an fsck */
566690fd 345 if (major(st.st_dev) == 0) {
e7a3aa3d 346 log_debug("Root directory is virtual or btrfs, skipping check.");
566690fd
LP
347 r = 0;
348 goto finish;
349 }
a9e1f5ec 350
a84f5192
KS
351 /* check if we are already writable */
352 times[0] = st.st_atim;
353 times[1] = st.st_mtim;
e7a3aa3d 354
a84f5192 355 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
cf1a1055 356 log_info("Root directory is writable, skipping check.");
566690fd
LP
357 r = 0;
358 goto finish;
a84f5192
KS
359 }
360
9102fdc5
TG
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");
566690fd 364 goto finish;
a9e1f5ec
LP
365 }
366
9102fdc5
TG
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");
566690fd 370 goto finish;
3e33a44a 371 }
dc8e15c2
LP
372
373 root_directory = true;
3d20ed6d
LP
374 }
375
9102fdc5
TG
376 r = sd_device_get_property_value(dev, "ID_FS_TYPE", &type);
377 if (r >= 0) {
eb66db55 378 r = fsck_exists(type);
85eca92e
LP
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);
566690fd 383 goto finish;
85eca92e 384 }
94192cda
ZJS
385 }
386
96d9117a
LP
387 if (arg_show_progress) {
388 if (pipe(progress_pipe) < 0) {
389 r = log_error_errno(errno, "pipe(): %m");
390 goto finish;
391 }
19e887e7 392 }
27d340c7 393
27d340c7
LP
394 pid = fork();
395 if (pid < 0) {
566690fd 396 r = log_error_errno(errno, "fork(): %m");
3d20ed6d 397 goto finish;
96d9117a
LP
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
3d20ed6d 405 /* Child */
96d9117a 406
ce30c8dc
LP
407 (void) reset_all_signal_handlers();
408 (void) reset_signal_mask();
96d9117a
LP
409 assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
410
411 /* Close the reading side of the progress pipe */
1952708a 412 progress_pipe[0] = safe_close(progress_pipe[0]);
96d9117a
LP
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
3d20ed6d 449 execv(cmdline[0], (char**) cmdline);
91077af6 450 _exit(FSCK_OPERATIONAL_ERROR);
3d20ed6d
LP
451 }
452
03e334a1 453 progress_pipe[1] = safe_close(progress_pipe[1]);
96d9117a 454 (void) process_progress(progress_pipe[0]);
576a13ea 455 progress_pipe[0] = -1;
27d340c7 456
566690fd
LP
457 r = wait_for_terminate(pid, &status);
458 if (r < 0) {
459 log_error_errno(r, "waitid(): %m");
3d20ed6d
LP
460 goto finish;
461 }
462
96d9117a 463 if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
3d20ed6d 464
4c701096 465 if (IN_SET(status.si_code, CLD_KILLED, CLD_DUMPED))
a9e1f5ec
LP
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);
96d9117a 469 else
a9e1f5ec 470 log_error("fsck failed due to unknown reason.");
3d20ed6d 471
566690fd
LP
472 r = -EINVAL;
473
91077af6 474 if (status.si_code == CLD_EXITED && (status.si_status & FSCK_SYSTEM_SHOULD_REBOOT) && root_directory)
3d20ed6d 475 /* System should be rebooted. */
48db40b3 476 start_target(SPECIAL_REBOOT_TARGET, "replace-irreversibly");
91077af6 477 else if (status.si_code == CLD_EXITED && (status.si_status & (FSCK_SYSTEM_SHOULD_REBOOT | FSCK_ERRORS_LEFT_UNCORRECTED)))
3d20ed6d 478 /* Some other problem */
48db40b3 479 start_target(SPECIAL_EMERGENCY_TARGET, "replace");
a4c24ff7 480 else {
96d9117a 481 log_warning("Ignoring error.");
566690fd 482 r = 0;
a4c24ff7 483 }
3d20ed6d
LP
484
485 } else
566690fd 486 r = 0;
3d20ed6d 487
91077af6 488 if (status.si_code == CLD_EXITED && (status.si_status & FSCK_ERROR_CORRECTED))
e7a3aa3d 489 (void) touch("/run/systemd/quotacheck");
3d20ed6d
LP
490
491finish:
566690fd 492 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
3d20ed6d 493}