]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/fsck/fsck.c
fsck: port from libudev to sd-device
[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>
3d20ed6d 30
0c842e0a 31#include "sd-bus.h"
9102fdc5 32#include "sd-device.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"
9102fdc5 39#include "device-util.h"
eb66db55 40#include "path-util.h"
ac6e2f0d
DR
41#include "socket-util.h"
42#include "fsckd/fsckd.h"
3d20ed6d
LP
43
44static bool arg_skip = false;
45static bool arg_force = 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 130
27d340c7
LP
131}
132
07f9a21b 133static int process_progress(int fd, pid_t fsck_pid, dev_t device_num) {
ac6e2f0d
DR
134 _cleanup_fclose_ FILE *f = NULL;
135 usec_t last = 0;
136 _cleanup_close_ int fsckd_fd = -1;
137 static const union sockaddr_union sa = {
138 .un.sun_family = AF_UNIX,
139 .un.sun_path = FSCKD_SOCKET_PATH,
27d340c7
LP
140 };
141
ac6e2f0d
DR
142 fsckd_fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
143 if (fsckd_fd < 0)
144 return log_warning_errno(errno, "Cannot open fsckd socket, we won't report fsck progress: %m");
145 if (connect(fsckd_fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0)
146 return log_warning_errno(errno, "Cannot connect to fsckd socket, we won't report fsck progress: %m");
27d340c7
LP
147
148 f = fdopen(fd, "r");
ac6e2f0d
DR
149 if (!f)
150 return log_warning_errno(errno, "Cannot connect to fsck, we won't report fsck progress: %m");
27d340c7
LP
151
152 while (!feof(f)) {
ac6e2f0d 153 int pass;
07f9a21b 154 size_t buflen;
ac6e2f0d 155 size_t cur, max;
07f9a21b 156 ssize_t r;
27d340c7 157 usec_t t;
ac6e2f0d
DR
158 _cleanup_free_ char *device = NULL;
159 FsckProgress progress;
07f9a21b 160 FsckdMessage fsckd_message;
27d340c7 161
1bc48c04 162 if (fscanf(f, "%i %zu %zu %ms", &pass, &cur, &max, &device) != 4)
27d340c7
LP
163 break;
164
27d340c7
LP
165 /* Only update once every 50ms */
166 t = now(CLOCK_MONOTONIC);
e375825d 167 if (last + 50 * USEC_PER_MSEC > t)
27d340c7 168 continue;
27d340c7
LP
169
170 last = t;
171
ac6e2f0d
DR
172 /* send progress to fsckd */
173 progress.devnum = device_num;
174 progress.cur = cur;
175 progress.max = max;
176 progress.pass = pass;
27d340c7 177
07f9a21b
DR
178 r = send(fsckd_fd, &progress, sizeof(FsckProgress), 0);
179 if (r < 0 || (size_t) r < sizeof(FsckProgress))
ac6e2f0d 180 log_warning_errno(errno, "Cannot communicate fsck progress to fsckd: %m");
07f9a21b
DR
181
182 /* get fsckd requests, only read when we have coherent size data */
183 r = ioctl(fsckd_fd, FIONREAD, &buflen);
184 if (r == 0 && (size_t) buflen >= sizeof(FsckdMessage)) {
185 r = recv(fsckd_fd, &fsckd_message, sizeof(FsckdMessage), 0);
186 if (r > 0 && fsckd_message.cancel == 1) {
187 log_info("Request to cancel fsck from fsckd");
188 kill(fsck_pid, SIGTERM);
189 }
190 }
27d340c7
LP
191 }
192
27d340c7 193 return 0;
3d20ed6d
LP
194}
195
196int main(int argc, char *argv[]) {
27d340c7 197 const char *cmdline[9];
3d20ed6d
LP
198 int i = 0, r = EXIT_FAILURE, q;
199 pid_t pid;
07f9a21b 200 int progress_rc;
3d20ed6d 201 siginfo_t status;
9102fdc5 202 _cleanup_device_unref_ sd_device *dev = NULL;
94192cda 203 const char *device, *type;
dc8e15c2 204 bool root_directory;
1952708a 205 _cleanup_close_pair_ int progress_pipe[2] = { -1, -1 };
5ffa8c81 206 char dash_c[sizeof("-C")-1 + DECIMAL_STR_MAX(int) + 1];
94192cda 207 struct stat st;
3d20ed6d 208
a9e1f5ec
LP
209 if (argc > 2) {
210 log_error("This program expects one or no arguments.");
3d20ed6d
LP
211 return EXIT_FAILURE;
212 }
213
27d340c7 214 log_set_target(LOG_TARGET_AUTO);
3d20ed6d
LP
215 log_parse_environment();
216 log_open();
217
4c12626c
LP
218 umask(0022);
219
b5884878
LP
220 q = parse_proc_cmdline(parse_proc_cmdline_item);
221 if (q < 0)
da927ba9 222 log_warning_errno(q, "Failed to parse kernel command line, ignoring: %m");
b5884878 223
3d20ed6d
LP
224 test_files();
225
566690fd
LP
226 if (!arg_force && arg_skip) {
227 r = 0;
228 goto finish;
229 }
a9e1f5ec 230
dc8e15c2 231 if (argc > 1) {
a9e1f5ec 232 device = argv[1];
dc8e15c2 233 root_directory = false;
94192cda
ZJS
234
235 if (stat(device, &st) < 0) {
566690fd
LP
236 r = log_error_errno(errno, "Failed to stat '%s': %m", device);
237 goto finish;
94192cda
ZJS
238 }
239
9102fdc5
TG
240 r = sd_device_new_from_devnum(&dev, 'b', st.st_rdev);
241 if (r < 0) {
242 log_error_errno(r, "Failed to detect device %s: %m", device);
566690fd 243 goto finish;
94192cda 244 }
dc8e15c2 245 } else {
a84f5192 246 struct timespec times[2];
3d20ed6d 247
a9e1f5ec
LP
248 /* Find root device */
249
250 if (stat("/", &st) < 0) {
566690fd
LP
251 r = log_error_errno(errno, "Failed to stat() the root directory: %m");
252 goto finish;
a9e1f5ec
LP
253 }
254
255 /* Virtual root devices don't need an fsck */
566690fd
LP
256 if (major(st.st_dev) == 0) {
257 log_debug("Root directory is virtual, skipping check.");
258 r = 0;
259 goto finish;
260 }
a9e1f5ec 261
a84f5192
KS
262 /* check if we are already writable */
263 times[0] = st.st_atim;
264 times[1] = st.st_mtim;
265 if (utimensat(AT_FDCWD, "/", times, 0) == 0) {
cf1a1055 266 log_info("Root directory is writable, skipping check.");
566690fd
LP
267 r = 0;
268 goto finish;
a84f5192
KS
269 }
270
9102fdc5
TG
271 r = sd_device_new_from_devnum(&dev, 'b', st.st_dev);
272 if (r < 0) {
273 log_error_errno(r, "Failed to detect root device: %m");
566690fd 274 goto finish;
a9e1f5ec
LP
275 }
276
9102fdc5
TG
277 r = sd_device_get_devname(dev, &device);
278 if (r < 0) {
279 log_error_errno(r, "Failed to detect device node of root directory: %m");
566690fd
LP
280 r = -ENXIO;
281 goto finish;
3e33a44a 282 }
dc8e15c2
LP
283
284 root_directory = true;
3d20ed6d
LP
285 }
286
9102fdc5
TG
287 r = sd_device_get_property_value(dev, "ID_FS_TYPE", &type);
288 if (r >= 0) {
eb66db55 289 r = fsck_exists(type);
571d0134
LP
290 if (r == -ENOENT) {
291 log_info("fsck.%s doesn't exist, not checking file system on %s", type, device);
566690fd
LP
292 r = 0;
293 goto finish;
571d0134 294 } else if (r < 0)
da927ba9 295 log_warning_errno(r, "fsck.%s cannot be used for %s: %m", type, device);
94192cda
ZJS
296 }
297
19e887e7 298 if (pipe(progress_pipe) < 0) {
566690fd
LP
299 r = log_error_errno(errno, "pipe(): %m");
300 goto finish;
19e887e7 301 }
27d340c7 302
3d20ed6d 303 cmdline[i++] = "/sbin/fsck";
f1f0198c 304 cmdline[i++] = arg_repair;
3d20ed6d 305 cmdline[i++] = "-T";
c343be28
KS
306
307 /*
48d3e8d0
KZ
308 * Since util-linux v2.25 fsck uses /run/fsck/<diskname>.lock files.
309 * The previous versions use flock for the device and conflict with
310 * udevd, see https://bugs.freedesktop.org/show_bug.cgi?id=79576#c5
c343be28 311 */
48d3e8d0 312 cmdline[i++] = "-l";
dc8e15c2
LP
313
314 if (!root_directory)
315 cmdline[i++] = "-M";
3d20ed6d
LP
316
317 if (arg_force)
318 cmdline[i++] = "-f";
319
576a13ea
ZJS
320 xsprintf(dash_c, "-C%i", progress_pipe[1]);
321 cmdline[i++] = dash_c;
27d340c7 322
a9e1f5ec 323 cmdline[i++] = device;
3d20ed6d
LP
324 cmdline[i++] = NULL;
325
27d340c7
LP
326 pid = fork();
327 if (pid < 0) {
566690fd 328 r = log_error_errno(errno, "fork(): %m");
3d20ed6d
LP
329 goto finish;
330 } else if (pid == 0) {
331 /* Child */
1952708a 332 progress_pipe[0] = safe_close(progress_pipe[0]);
3d20ed6d
LP
333 execv(cmdline[0], (char**) cmdline);
334 _exit(8); /* Operational error */
335 }
336
03e334a1 337 progress_pipe[1] = safe_close(progress_pipe[1]);
27d340c7 338
576a13ea
ZJS
339 progress_rc = process_progress(progress_pipe[0], pid, st.st_rdev);
340 progress_pipe[0] = -1;
27d340c7 341
566690fd
LP
342 r = wait_for_terminate(pid, &status);
343 if (r < 0) {
344 log_error_errno(r, "waitid(): %m");
3d20ed6d
LP
345 goto finish;
346 }
347
07f9a21b 348 if (status.si_code != CLD_EXITED || (status.si_status & ~1) || progress_rc != 0) {
3d20ed6d 349
07f9a21b
DR
350 /* cancel will kill fsck (but process_progress returns 0) */
351 if ((progress_rc != 0 && status.si_code == CLD_KILLED) || status.si_code == CLD_DUMPED)
a9e1f5ec
LP
352 log_error("fsck terminated by signal %s.", signal_to_string(status.si_status));
353 else if (status.si_code == CLD_EXITED)
354 log_error("fsck failed with error code %i.", status.si_status);
07f9a21b 355 else if (progress_rc != 0)
a9e1f5ec 356 log_error("fsck failed due to unknown reason.");
3d20ed6d 357
566690fd
LP
358 r = -EINVAL;
359
a4c24ff7 360 if (status.si_code == CLD_EXITED && (status.si_status & 2) && root_directory)
3d20ed6d 361 /* System should be rebooted. */
80cfe9e1 362 start_target(SPECIAL_REBOOT_TARGET);
a4c24ff7 363 else if (status.si_code == CLD_EXITED && (status.si_status & 6))
3d20ed6d 364 /* Some other problem */
80cfe9e1 365 start_target(SPECIAL_EMERGENCY_TARGET);
a4c24ff7 366 else {
566690fd 367 r = 0;
07f9a21b
DR
368 if (progress_rc != 0)
369 log_warning("Ignoring error.");
a4c24ff7 370 }
3d20ed6d
LP
371
372 } else
566690fd 373 r = 0;
3d20ed6d 374
a9e1f5ec 375 if (status.si_code == CLD_EXITED && (status.si_status & 1))
2b583ce6 376 touch("/run/systemd/quotacheck");
3d20ed6d
LP
377
378finish:
566690fd 379 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
3d20ed6d 380}