1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright © 2014 Holger Hans Peter Freyther
11 #include <sys/prctl.h>
16 #include "sd-device.h"
18 #include "alloc-util.h"
19 #include "bus-common-errors.h"
20 #include "bus-error.h"
22 #include "device-util.h"
25 #include "main-func.h"
26 #include "parse-util.h"
27 #include "path-util.h"
28 #include "proc-cmdline.h"
29 #include "process-util.h"
30 #include "rlimit-util.h"
31 #include "signal-util.h"
32 #include "socket-util.h"
34 #include "stdio-util.h"
37 /* exit codes as defined in fsck(8) */
40 FSCK_ERROR_CORRECTED
= 1 << 0,
41 FSCK_SYSTEM_SHOULD_REBOOT
= 1 << 1,
42 FSCK_ERRORS_LEFT_UNCORRECTED
= 1 << 2,
43 FSCK_OPERATIONAL_ERROR
= 1 << 3,
44 FSCK_USAGE_OR_SYNTAX_ERROR
= 1 << 4,
45 FSCK_USER_CANCELLED
= 1 << 5,
46 FSCK_SHARED_LIB_ERROR
= 1 << 7,
49 static bool arg_skip
= false;
50 static bool arg_force
= false;
51 static bool arg_show_progress
= false;
52 static const char *arg_repair
= "-a";
54 static void start_target(const char *target
, const char *mode
) {
55 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
56 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
61 r
= bus_connect_system_systemd(&bus
);
63 log_error_errno(r
, "Failed to get D-Bus connection: %m");
67 log_info("Running request %s/start/replace", target
);
69 /* Start these units only if we can replace base.target with it */
70 r
= sd_bus_call_method(bus
,
71 "org.freedesktop.systemd1",
72 "/org/freedesktop/systemd1",
73 "org.freedesktop.systemd1.Manager",
77 "sss", "basic.target", target
, mode
);
79 /* Don't print a warning if we aren't called during startup */
80 if (r
< 0 && !sd_bus_error_has_name(&error
, BUS_ERROR_NO_SUCH_JOB
))
81 log_error("Failed to start unit: %s", bus_error_message(&error
, r
));
84 static int parse_proc_cmdline_item(const char *key
, const char *value
, void *data
) {
89 if (streq(key
, "fsck.mode")) {
91 if (proc_cmdline_value_missing(key
, value
))
94 if (streq(value
, "auto"))
95 arg_force
= arg_skip
= false;
96 else if (streq(value
, "force"))
98 else if (streq(value
, "skip"))
101 log_warning("Invalid fsck.mode= parameter '%s'. Ignoring.", value
);
103 } else if (streq(key
, "fsck.repair")) {
105 if (proc_cmdline_value_missing(key
, value
))
108 if (streq(value
, "preen"))
111 r
= parse_boolean(value
);
117 log_warning("Invalid fsck.repair= parameter '%s'. Ignoring.", value
);
122 else if (streq(key
, "fastboot") && !value
) {
123 log_warning("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line.");
126 } else if (streq(key
, "forcefsck") && !value
) {
127 log_warning("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line.");
135 static void test_files(void) {
138 if (access("/fastboot", F_OK
) >= 0) {
139 log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system.");
143 if (access("/forcefsck", F_OK
) >= 0) {
144 log_error("Please pass 'fsck.mode=force' on the kernel command line rather than creating /forcefsck on the root file system.");
149 arg_show_progress
= access("/run/systemd/show-status", F_OK
) >= 0;
152 static double percent(int pass
, unsigned long cur
, unsigned long max
) {
153 /* Values stolen from e2fsck */
155 static const int pass_table
[] = {
156 0, 70, 90, 92, 95, 100
162 if ((unsigned) pass
>= ELEMENTSOF(pass_table
) || max
== 0)
165 return (double) pass_table
[pass
-1] +
166 ((double) pass_table
[pass
] - (double) pass_table
[pass
-1]) *
167 (double) cur
/ (double) max
;
170 static int process_progress(int fd
) {
171 _cleanup_fclose_
FILE *console
= NULL
, *f
= NULL
;
176 /* No progress pipe to process? Then we are a NOP. */
186 console
= fopen("/dev/console", "we");
192 unsigned long cur
, max
;
193 _cleanup_free_
char *device
= NULL
;
197 if (fscanf(f
, "%i %lu %lu %ms", &pass
, &cur
, &max
, &device
) != 4) {
200 r
= log_warning_errno(errno
, "Failed to read from progress pipe: %m");
204 log_warning("Failed to parse progress pipe data");
210 /* Only show one progress counter at max */
212 if (flock(fileno(console
), LOCK_EX
|LOCK_NB
) < 0)
218 /* Only update once every 50ms */
219 t
= now(CLOCK_MONOTONIC
);
220 if (last
+ 50 * USEC_PER_MSEC
> t
)
225 p
= percent(pass
, cur
, max
);
226 fprintf(console
, "\r%s: fsck %3.1f%% complete...\r%n", device
, p
, &m
);
236 fputc('\r', console
);
237 for (j
= 0; j
< (unsigned) clear
; j
++)
239 fputc('\r', console
);
246 static int fsck_progress_socket(void) {
247 static const union sockaddr_union sa
= {
248 .un
.sun_family
= AF_UNIX
,
249 .un
.sun_path
= "/run/systemd/fsck.progress",
252 _cleanup_close_
int fd
= -1;
254 fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
256 return log_warning_errno(errno
, "socket(): %m");
258 if (connect(fd
, &sa
.sa
, SOCKADDR_UN_LEN(sa
.un
)) < 0)
259 return log_full_errno(IN_SET(errno
, ECONNREFUSED
, ENOENT
) ? LOG_DEBUG
: LOG_WARNING
,
260 errno
, "Failed to connect to progress socket %s, ignoring: %m", sa
.un
.sun_path
);
265 static int run(int argc
, char *argv
[]) {
266 _cleanup_close_pair_
int progress_pipe
[2] = { -1, -1 };
267 _cleanup_(sd_device_unrefp
) sd_device
*dev
= NULL
;
268 const char *device
, *type
;
277 log_error("This program expects one or no arguments.");
283 r
= proc_cmdline_parse(parse_proc_cmdline_item
, NULL
, PROC_CMDLINE_STRIP_RD_PREFIX
);
285 log_warning_errno(r
, "Failed to parse kernel command line, ignoring: %m");
289 if (!arg_force
&& arg_skip
)
295 if (stat(device
, &st
) < 0)
296 return log_error_errno(errno
, "Failed to stat %s: %m", device
);
298 if (!S_ISBLK(st
.st_mode
)) {
299 log_error("%s is not a block device.", device
);
303 r
= sd_device_new_from_devnum(&dev
, 'b', st
.st_rdev
);
305 return log_error_errno(r
, "Failed to detect device %s: %m", device
);
307 root_directory
= false;
309 struct timespec times
[2];
311 /* Find root device */
313 if (stat("/", &st
) < 0)
314 return log_error_errno(errno
, "Failed to stat() the root directory: %m");
316 /* Virtual root devices don't need an fsck */
317 if (major(st
.st_dev
) == 0) {
318 log_debug("Root directory is virtual or btrfs, skipping check.");
322 /* check if we are already writable */
323 times
[0] = st
.st_atim
;
324 times
[1] = st
.st_mtim
;
326 if (utimensat(AT_FDCWD
, "/", times
, 0) == 0) {
327 log_info("Root directory is writable, skipping check.");
331 r
= sd_device_new_from_devnum(&dev
, 'b', st
.st_dev
);
333 return log_error_errno(r
, "Failed to detect root device: %m");
335 r
= sd_device_get_devname(dev
, &device
);
337 return log_device_error_errno(dev
, r
, "Failed to detect device node of root directory: %m");
339 root_directory
= true;
342 if (sd_device_get_property_value(dev
, "ID_FS_TYPE", &type
) >= 0) {
343 r
= fsck_exists(type
);
345 log_device_warning_errno(dev
, r
, "Couldn't detect if fsck.%s may be used, proceeding: %m", type
);
347 log_device_info(dev
, "fsck.%s doesn't exist, not checking file system.", type
);
352 if (arg_show_progress
&&
353 pipe(progress_pipe
) < 0)
354 return log_error_errno(errno
, "pipe(): %m");
356 r
= safe_fork("(fsck)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
|FORK_LOG
, &pid
);
360 char dash_c
[STRLEN("-C") + DECIMAL_STR_MAX(int) + 1];
361 int progress_socket
= -1;
362 const char *cmdline
[9];
367 /* Close the reading side of the progress pipe */
368 progress_pipe
[0] = safe_close(progress_pipe
[0]);
370 /* Try to connect to a progress management daemon, if there is one */
371 progress_socket
= fsck_progress_socket();
372 if (progress_socket
>= 0) {
373 /* If this worked we close the progress pipe early, and just use the socket */
374 progress_pipe
[1] = safe_close(progress_pipe
[1]);
375 xsprintf(dash_c
, "-C%i", progress_socket
);
376 } else if (progress_pipe
[1] >= 0) {
377 /* Otherwise if we have the progress pipe to our own local handle, we use it */
378 xsprintf(dash_c
, "-C%i", progress_pipe
[1]);
382 cmdline
[i
++] = "/sbin/fsck";
383 cmdline
[i
++] = arg_repair
;
387 * Since util-linux v2.25 fsck uses /run/fsck/<diskname>.lock files.
388 * The previous versions use flock for the device and conflict with
389 * udevd, see https://bugs.freedesktop.org/show_bug.cgi?id=79576#c5
399 if (!isempty(dash_c
))
400 cmdline
[i
++] = dash_c
;
402 cmdline
[i
++] = device
;
405 (void) rlimit_nofile_safe();
407 execv(cmdline
[0], (char**) cmdline
);
408 _exit(FSCK_OPERATIONAL_ERROR
);
411 progress_pipe
[1] = safe_close(progress_pipe
[1]);
412 (void) process_progress(TAKE_FD(progress_pipe
[0]));
414 exit_status
= wait_for_terminate_and_check("fsck", pid
, WAIT_LOG_ABNORMAL
);
417 if ((exit_status
& ~FSCK_ERROR_CORRECTED
) != FSCK_SUCCESS
) {
418 log_error("fsck failed with exit status %i.", exit_status
);
420 if ((exit_status
& FSCK_SYSTEM_SHOULD_REBOOT
) && root_directory
) {
421 /* System should be rebooted. */
422 start_target(SPECIAL_REBOOT_TARGET
, "replace-irreversibly");
424 } else if (exit_status
& (FSCK_SYSTEM_SHOULD_REBOOT
| FSCK_ERRORS_LEFT_UNCORRECTED
))
425 /* Some other problem */
426 start_target(SPECIAL_EMERGENCY_TARGET
, "replace");
428 log_warning("Ignoring error.");
431 if (exit_status
& FSCK_ERROR_CORRECTED
)
432 (void) touch("/run/systemd/quotacheck");
434 return !!(exit_status
& (FSCK_SYSTEM_SHOULD_REBOOT
| FSCK_ERRORS_LEFT_UNCORRECTED
));
437 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run
);