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