]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/fsck/fsck.c
fsck: re-enable fsck -l
[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>
25#include <string.h>
26#include <errno.h>
27#include <unistd.h>
a84f5192 28#include <fcntl.h>
27d340c7 29#include <sys/file.h>
3d20ed6d 30
0c842e0a
TG
31#include "sd-bus.h"
32#include "libudev.h"
3d20ed6d
LP
33
34#include "util.h"
3d20ed6d 35#include "special.h"
0c842e0a
TG
36#include "bus-util.h"
37#include "bus-error.h"
ef1de59b 38#include "bus-errors.h"
a5c32cff 39#include "fileio.h"
1ca208fb 40#include "udev-util.h"
eb66db55 41#include "path-util.h"
3d20ed6d
LP
42
43static bool arg_skip = false;
44static bool arg_force = false;
27d340c7 45static bool arg_show_progress = false;
f1f0198c 46static const char *arg_repair = "-a";
3d20ed6d 47
80cfe9e1 48static void start_target(const char *target) {
0c842e0a 49 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
24996861 50 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
0c842e0a 51 int r;
3d20ed6d
LP
52
53 assert(target);
54
0f8bd8de 55 r = bus_open_system_systemd(&bus);
0c842e0a
TG
56 if (r < 0) {
57 log_error("Failed to get D-Bus connection: %s", strerror(-r));
58 return;
3d20ed6d
LP
59 }
60
0c842e0a 61 log_info("Running request %s/start/replace", target);
3d20ed6d 62
90bb85e1 63 /* Start these units only if we can replace base.target with it */
0c842e0a
TG
64 r = sd_bus_call_method(bus,
65 "org.freedesktop.systemd1",
66 "/org/freedesktop/systemd1",
67 "org.freedesktop.systemd1.Manager",
68 "StartUnitReplace",
69 &error,
70 NULL,
71 "sss", "basic.target", target, "replace");
3d20ed6d 72
5220a6f3
LP
73 /* Don't print a warning if we aren't called during startup */
74 if (r < 0 && !sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_JOB))
75 log_error("Failed to start unit: %s", bus_error_message(&error, -r));
3d20ed6d
LP
76}
77
059cb385
LP
78static int parse_proc_cmdline_item(const char *key, const char *value) {
79
80 if (streq(key, "fsck.mode") && value) {
81
82 if (streq(value, "auto"))
83 arg_force = arg_skip = false;
84 else if (streq(value, "force"))
85 arg_force = true;
86 else if (streq(value, "skip"))
87 arg_skip = true;
88 else
85013844
LP
89 log_warning("Invalid fsck.mode= parameter '%s'. Ignoring.", value);
90
f1f0198c
HHPF
91 } else if (streq(key, "fsck.repair") && value) {
92
93 if (streq(value, "preen"))
94 arg_repair = "-a";
95 else if (streq(value, "yes"))
96 arg_repair = "-y";
97 else if (streq(value, "no"))
98 arg_repair = "-n";
99 else
85013844
LP
100 log_warning("Invalid fsck.repair= parameter '%s'. Ignoring.", value);
101 }
102
32f992a5 103#ifdef HAVE_SYSV_COMPAT
059cb385
LP
104 else if (streq(key, "fastboot") && !value) {
105 log_warning("Please pass 'fsck.mode=skip' rather than 'fastboot' on the kernel command line.");
141a79f4 106 arg_skip = true;
85013844 107
059cb385
LP
108 } else if (streq(key, "forcefsck") && !value) {
109 log_warning("Please pass 'fsck.mode=force' rather than 'forcefsck' on the kernel command line.");
141a79f4 110 arg_force = true;
3d20ed6d 111 }
141a79f4 112#endif
3d20ed6d 113
3d20ed6d
LP
114 return 0;
115}
116
117static void test_files(void) {
85013844 118
32f992a5
LP
119#ifdef HAVE_SYSV_COMPAT
120 if (access("/fastboot", F_OK) >= 0) {
121 log_error("Please pass 'fsck.mode=skip' on the kernel command line rather than creating /fastboot on the root file system.");
3d20ed6d 122 arg_skip = true;
32f992a5 123 }
3d20ed6d 124
32f992a5
LP
125 if (access("/forcefsck", F_OK) >= 0) {
126 log_error("Please pass 'fsck.mode=force' on the kernel command line rather than creating /forcefsck on the root file system.");
3d20ed6d 127 arg_force = true;
32f992a5
LP
128 }
129#endif
27d340c7
LP
130
131 if (access("/run/systemd/show-status", F_OK) >= 0 || plymouth_running())
132 arg_show_progress = true;
133}
134
135static double percent(int pass, unsigned long cur, unsigned long max) {
136 /* Values stolen from e2fsck */
137
138 static const int pass_table[] = {
139 0, 70, 90, 92, 95, 100
140 };
141
142 if (pass <= 0)
143 return 0.0;
144
145 if ((unsigned) pass >= ELEMENTSOF(pass_table) || max == 0)
146 return 100.0;
147
148 return (double) pass_table[pass-1] +
149 ((double) pass_table[pass] - (double) pass_table[pass-1]) *
150 (double) cur / (double) max;
151}
152
153static int process_progress(int fd) {
e375825d 154 _cleanup_fclose_ FILE *console = NULL, *f = NULL;
27d340c7
LP
155 usec_t last = 0;
156 bool locked = false;
157 int clear = 0;
158
159 f = fdopen(fd, "r");
160 if (!f) {
03e334a1 161 safe_close(fd);
27d340c7
LP
162 return -errno;
163 }
164
c8a202b7 165 console = fopen("/dev/console", "we");
e375825d 166 if (!console)
27d340c7 167 return -ENOMEM;
27d340c7
LP
168
169 while (!feof(f)) {
170 int pass, m;
171 unsigned long cur, max;
e375825d 172 _cleanup_free_ char *device = NULL;
27d340c7
LP
173 double p;
174 usec_t t;
175
176 if (fscanf(f, "%i %lu %lu %ms", &pass, &cur, &max, &device) != 4)
177 break;
178
179 /* Only show one progress counter at max */
180 if (!locked) {
e375825d 181 if (flock(fileno(console), LOCK_EX|LOCK_NB) < 0)
27d340c7 182 continue;
27d340c7
LP
183
184 locked = true;
185 }
186
187 /* Only update once every 50ms */
188 t = now(CLOCK_MONOTONIC);
e375825d 189 if (last + 50 * USEC_PER_MSEC > t)
27d340c7 190 continue;
27d340c7
LP
191
192 last = t;
193
194 p = percent(pass, cur, max);
195 fprintf(console, "\r%s: fsck %3.1f%% complete...\r%n", device, p, &m);
196 fflush(console);
197
27d340c7
LP
198 if (m > clear)
199 clear = m;
200 }
201
202 if (clear > 0) {
203 unsigned j;
204
205 fputc('\r', console);
206 for (j = 0; j < (unsigned) clear; j++)
207 fputc(' ', console);
208 fputc('\r', console);
209 fflush(console);
210 }
211
27d340c7 212 return 0;
3d20ed6d
LP
213}
214
215int main(int argc, char *argv[]) {
27d340c7 216 const char *cmdline[9];
3d20ed6d
LP
217 int i = 0, r = EXIT_FAILURE, q;
218 pid_t pid;
219 siginfo_t status;
1ca208fb
ZJS
220 _cleanup_udev_unref_ struct udev *udev = NULL;
221 _cleanup_udev_device_unref_ struct udev_device *udev_device = NULL;
94192cda 222 const char *device, *type;
dc8e15c2 223 bool root_directory;
27d340c7
LP
224 int progress_pipe[2] = { -1, -1 };
225 char dash_c[2+10+1];
94192cda 226 struct stat st;
3d20ed6d 227
a9e1f5ec
LP
228 if (argc > 2) {
229 log_error("This program expects one or no arguments.");
3d20ed6d
LP
230 return EXIT_FAILURE;
231 }
232
27d340c7 233 log_set_target(LOG_TARGET_AUTO);
3d20ed6d
LP
234 log_parse_environment();
235 log_open();
236
4c12626c
LP
237 umask(0022);
238
059cb385 239 parse_proc_cmdline(parse_proc_cmdline_item);
3d20ed6d
LP
240 test_files();
241
a9e1f5ec
LP
242 if (!arg_force && arg_skip)
243 return 0;
244
94192cda
ZJS
245 udev = udev_new();
246 if (!udev) {
247 log_oom();
248 return EXIT_FAILURE;
249 }
250
dc8e15c2 251 if (argc > 1) {
a9e1f5ec 252 device = argv[1];
dc8e15c2 253 root_directory = false;
94192cda
ZJS
254
255 if (stat(device, &st) < 0) {
256 log_error("Failed to stat '%s': %m", device);
257 return EXIT_FAILURE;
258 }
259
260 udev_device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
261 if (!udev_device) {
262 log_error("Failed to detect device %s", device);
263 return EXIT_FAILURE;
264 }
dc8e15c2 265 } else {
a84f5192 266 struct timespec times[2];
3d20ed6d 267
a9e1f5ec
LP
268 /* Find root device */
269
270 if (stat("/", &st) < 0) {
271 log_error("Failed to stat() the root directory: %m");
e375825d 272 return EXIT_FAILURE;
a9e1f5ec
LP
273 }
274
275 /* Virtual root devices don't need an fsck */
276 if (major(st.st_dev) == 0)
e375825d 277 return EXIT_SUCCESS;
a9e1f5ec 278
a84f5192
KS
279 /* check if we are already writable */
280 times[0] = st.st_atim;
281 times[1] = st.st_mtim;
282 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
cf1a1055 283 log_info("Root directory is writable, skipping check.");
e375825d 284 return EXIT_SUCCESS;
a84f5192
KS
285 }
286
e375825d
ZJS
287 udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev);
288 if (!udev_device) {
a9e1f5ec 289 log_error("Failed to detect root device.");
e375825d 290 return EXIT_FAILURE;
a9e1f5ec
LP
291 }
292
e375825d
ZJS
293 device = udev_device_get_devnode(udev_device);
294 if (!device) {
a9e1f5ec 295 log_error("Failed to detect device node of root directory.");
e375825d 296 return EXIT_FAILURE;
3e33a44a 297 }
dc8e15c2
LP
298
299 root_directory = true;
3d20ed6d
LP
300 }
301
94192cda
ZJS
302 type = udev_device_get_property_value(udev_device, "ID_FS_TYPE");
303 if (type) {
eb66db55 304 r = fsck_exists(type);
571d0134
LP
305 if (r == -ENOENT) {
306 log_info("fsck.%s doesn't exist, not checking file system on %s", type, device);
307 return EXIT_SUCCESS;
308 } else if (r < 0)
309 log_warning("fsck.%s cannot be used for %s: %s", type, device, strerror(-r));
94192cda
ZJS
310 }
311
27d340c7
LP
312 if (arg_show_progress)
313 if (pipe(progress_pipe) < 0) {
314 log_error("pipe(): %m");
e375825d 315 return EXIT_FAILURE;
27d340c7
LP
316 }
317
3d20ed6d 318 cmdline[i++] = "/sbin/fsck";
f1f0198c 319 cmdline[i++] = arg_repair;
3d20ed6d 320 cmdline[i++] = "-T";
c343be28
KS
321
322 /*
48d3e8d0
KZ
323 * Since util-linux v2.25 fsck uses /run/fsck/<diskname>.lock files.
324 * The previous versions use flock for the device and conflict with
325 * udevd, see https://bugs.freedesktop.org/show_bug.cgi?id=79576#c5
c343be28 326 */
48d3e8d0 327 cmdline[i++] = "-l";
dc8e15c2
LP
328
329 if (!root_directory)
330 cmdline[i++] = "-M";
3d20ed6d
LP
331
332 if (arg_force)
333 cmdline[i++] = "-f";
334
27d340c7
LP
335 if (progress_pipe[1] >= 0) {
336 snprintf(dash_c, sizeof(dash_c), "-C%i", progress_pipe[1]);
337 char_array_0(dash_c);
338 cmdline[i++] = dash_c;
339 }
340
a9e1f5ec 341 cmdline[i++] = device;
3d20ed6d
LP
342 cmdline[i++] = NULL;
343
27d340c7
LP
344 pid = fork();
345 if (pid < 0) {
3d20ed6d
LP
346 log_error("fork(): %m");
347 goto finish;
348 } else if (pid == 0) {
349 /* Child */
27d340c7 350 if (progress_pipe[0] >= 0)
03e334a1 351 safe_close(progress_pipe[0]);
3d20ed6d
LP
352 execv(cmdline[0], (char**) cmdline);
353 _exit(8); /* Operational error */
354 }
355
03e334a1 356 progress_pipe[1] = safe_close(progress_pipe[1]);
27d340c7
LP
357
358 if (progress_pipe[0] >= 0) {
359 process_progress(progress_pipe[0]);
360 progress_pipe[0] = -1;
361 }
362
363 q = wait_for_terminate(pid, &status);
364 if (q < 0) {
3d20ed6d
LP
365 log_error("waitid(): %s", strerror(-q));
366 goto finish;
367 }
368
a9e1f5ec 369 if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
3d20ed6d 370
a9e1f5ec
LP
371 if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
372 log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
373 else if (status.si_code == CLD_EXITED)
374 log_error("fsck failed with error code %i.", status.si_status);
375 else
376 log_error("fsck failed due to unknown reason.");
3d20ed6d 377
a4c24ff7 378 if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
3d20ed6d 379 /* System should be rebooted. */
80cfe9e1 380 start_target(SPECIAL_REBOOT_TARGET);
a4c24ff7 381 else if (status.si_code == CLD_EXITED && (status.si_status & 6))
3d20ed6d 382 /* Some other problem */
80cfe9e1 383 start_target(SPECIAL_EMERGENCY_TARGET);
a4c24ff7
LP
384 else {
385 r = EXIT_SUCCESS;
386 log_warning("Ignoring error.");
387 }
3d20ed6d
LP
388
389 } else
390 r = EXIT_SUCCESS;
391
a9e1f5ec 392 if (status.si_code == CLD_EXITED && (status.si_status & 1))
2b583ce6 393 touch("/run/systemd/quotacheck");
3d20ed6d
LP
394
395finish:
3d94f76c 396 safe_close_pair(progress_pipe);
27d340c7 397
3d20ed6d
LP
398 return r;
399}