]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/fsck/fsck.c
test: utf8 - fix utf16 tests on BE machines
[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"
96aad8d1 38#include "bus-common-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 56 if (r < 0) {
da927ba9 57 log_error_errno(r, "Failed to get D-Bus connection: %m");
0c842e0a 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 224 int progress_pipe[2] = { -1, -1 };
5ffa8c81 225 char dash_c[sizeof("-C")-1 + DECIMAL_STR_MAX(int) + 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
b5884878
LP
239 q = parse_proc_cmdline(parse_proc_cmdline_item);
240 if (q < 0)
da927ba9 241 log_warning_errno(q, "Failed to parse kernel command line, ignoring: %m");
b5884878 242
3d20ed6d
LP
243 test_files();
244
a9e1f5ec
LP
245 if (!arg_force && arg_skip)
246 return 0;
247
94192cda
ZJS
248 udev = udev_new();
249 if (!udev) {
250 log_oom();
251 return EXIT_FAILURE;
252 }
253
dc8e15c2 254 if (argc > 1) {
a9e1f5ec 255 device = argv[1];
dc8e15c2 256 root_directory = false;
94192cda
ZJS
257
258 if (stat(device, &st) < 0) {
56f64d95 259 log_error_errno(errno, "Failed to stat '%s': %m", device);
94192cda
ZJS
260 return EXIT_FAILURE;
261 }
262
263 udev_device = udev_device_new_from_devnum(udev, 'b', st.st_rdev);
264 if (!udev_device) {
265 log_error("Failed to detect device %s", device);
266 return EXIT_FAILURE;
267 }
dc8e15c2 268 } else {
a84f5192 269 struct timespec times[2];
3d20ed6d 270
a9e1f5ec
LP
271 /* Find root device */
272
273 if (stat("/", &st) < 0) {
56f64d95 274 log_error_errno(errno, "Failed to stat() the root directory: %m");
e375825d 275 return EXIT_FAILURE;
a9e1f5ec
LP
276 }
277
278 /* Virtual root devices don't need an fsck */
279 if (major(st.st_dev) == 0)
e375825d 280 return EXIT_SUCCESS;
a9e1f5ec 281
a84f5192
KS
282 /* check if we are already writable */
283 times[0] = st.st_atim;
284 times[1] = st.st_mtim;
285 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
cf1a1055 286 log_info("Root directory is writable, skipping check.");
e375825d 287 return EXIT_SUCCESS;
a84f5192
KS
288 }
289
e375825d
ZJS
290 udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev);
291 if (!udev_device) {
a9e1f5ec 292 log_error("Failed to detect root device.");
e375825d 293 return EXIT_FAILURE;
a9e1f5ec
LP
294 }
295
e375825d
ZJS
296 device = udev_device_get_devnode(udev_device);
297 if (!device) {
a9e1f5ec 298 log_error("Failed to detect device node of root directory.");
e375825d 299 return EXIT_FAILURE;
3e33a44a 300 }
dc8e15c2
LP
301
302 root_directory = true;
3d20ed6d
LP
303 }
304
94192cda
ZJS
305 type = udev_device_get_property_value(udev_device, "ID_FS_TYPE");
306 if (type) {
eb66db55 307 r = fsck_exists(type);
571d0134
LP
308 if (r == -ENOENT) {
309 log_info("fsck.%s doesn't exist, not checking file system on %s", type, device);
310 return EXIT_SUCCESS;
311 } else if (r < 0)
da927ba9 312 log_warning_errno(r, "fsck.%s cannot be used for %s: %m", type, device);
94192cda
ZJS
313 }
314
27d340c7
LP
315 if (arg_show_progress)
316 if (pipe(progress_pipe) < 0) {
56f64d95 317 log_error_errno(errno, "pipe(): %m");
e375825d 318 return EXIT_FAILURE;
27d340c7
LP
319 }
320
3d20ed6d 321 cmdline[i++] = "/sbin/fsck";
f1f0198c 322 cmdline[i++] = arg_repair;
3d20ed6d 323 cmdline[i++] = "-T";
c343be28
KS
324
325 /*
48d3e8d0
KZ
326 * Since util-linux v2.25 fsck uses /run/fsck/<diskname>.lock files.
327 * The previous versions use flock for the device and conflict with
328 * udevd, see https://bugs.freedesktop.org/show_bug.cgi?id=79576#c5
c343be28 329 */
48d3e8d0 330 cmdline[i++] = "-l";
dc8e15c2
LP
331
332 if (!root_directory)
333 cmdline[i++] = "-M";
3d20ed6d
LP
334
335 if (arg_force)
336 cmdline[i++] = "-f";
337
27d340c7 338 if (progress_pipe[1] >= 0) {
5ffa8c81 339 xsprintf(dash_c, "-C%i", progress_pipe[1]);
27d340c7
LP
340 cmdline[i++] = dash_c;
341 }
342
a9e1f5ec 343 cmdline[i++] = device;
3d20ed6d
LP
344 cmdline[i++] = NULL;
345
27d340c7
LP
346 pid = fork();
347 if (pid < 0) {
56f64d95 348 log_error_errno(errno, "fork(): %m");
3d20ed6d
LP
349 goto finish;
350 } else if (pid == 0) {
351 /* Child */
27d340c7 352 if (progress_pipe[0] >= 0)
03e334a1 353 safe_close(progress_pipe[0]);
3d20ed6d
LP
354 execv(cmdline[0], (char**) cmdline);
355 _exit(8); /* Operational error */
356 }
357
03e334a1 358 progress_pipe[1] = safe_close(progress_pipe[1]);
27d340c7
LP
359
360 if (progress_pipe[0] >= 0) {
361 process_progress(progress_pipe[0]);
362 progress_pipe[0] = -1;
363 }
364
365 q = wait_for_terminate(pid, &status);
366 if (q < 0) {
da927ba9 367 log_error_errno(q, "waitid(): %m");
3d20ed6d
LP
368 goto finish;
369 }
370
a9e1f5ec 371 if (status.si_code != CLD_EXITED || (status.si_status & ~1)) {
3d20ed6d 372
a9e1f5ec
LP
373 if (status.si_code == CLD_KILLED || status.si_code == CLD_DUMPED)
374 log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
375 else if (status.si_code == CLD_EXITED)
376 log_error("fsck failed with error code %i.", status.si_status);
377 else
378 log_error("fsck failed due to unknown reason.");
3d20ed6d 379
a4c24ff7 380 if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
3d20ed6d 381 /* System should be rebooted. */
80cfe9e1 382 start_target(SPECIAL_REBOOT_TARGET);
a4c24ff7 383 else if (status.si_code == CLD_EXITED && (status.si_status & 6))
3d20ed6d 384 /* Some other problem */
80cfe9e1 385 start_target(SPECIAL_EMERGENCY_TARGET);
a4c24ff7
LP
386 else {
387 r = EXIT_SUCCESS;
388 log_warning("Ignoring error.");
389 }
3d20ed6d
LP
390
391 } else
392 r = EXIT_SUCCESS;
393
a9e1f5ec 394 if (status.si_code == CLD_EXITED && (status.si_status & 1))
2b583ce6 395 touch("/run/systemd/quotacheck");
3d20ed6d
LP
396
397finish:
3d94f76c 398 safe_close_pair(progress_pipe);
27d340c7 399
3d20ed6d
LP
400 return r;
401}