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