]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/umount.c
util: replace close_nointr_nofail() by a more useful safe_close()
[thirdparty/systemd.git] / src / core / umount.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 ProFUSION embedded systems
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <sys/mount.h>
26 #include <sys/swap.h>
27 #include <unistd.h>
28 #include <linux/loop.h>
29 #include <linux/dm-ioctl.h>
30
31 #include "list.h"
32 #include "mount-setup.h"
33 #include "umount.h"
34 #include "path-util.h"
35 #include "util.h"
36 #include "virt.h"
37 #include "libudev.h"
38 #include "udev-util.h"
39
40 typedef struct MountPoint {
41 char *path;
42 dev_t devnum;
43 LIST_FIELDS(struct MountPoint, mount_point);
44 } MountPoint;
45
46 static void mount_point_free(MountPoint **head, MountPoint *m) {
47 assert(head);
48 assert(m);
49
50 LIST_REMOVE(mount_point, *head, m);
51
52 free(m->path);
53 free(m);
54 }
55
56 static void mount_points_list_free(MountPoint **head) {
57 assert(head);
58
59 while (*head)
60 mount_point_free(head, *head);
61 }
62
63 static int mount_points_list_get(MountPoint **head) {
64 FILE *proc_self_mountinfo;
65 char *path, *p;
66 unsigned int i;
67 int r;
68
69 assert(head);
70
71 if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
72 return -errno;
73
74 for (i = 1;; i++) {
75 int k;
76 MountPoint *m;
77
78 path = p = NULL;
79
80 if ((k = fscanf(proc_self_mountinfo,
81 "%*s " /* (1) mount id */
82 "%*s " /* (2) parent id */
83 "%*s " /* (3) major:minor */
84 "%*s " /* (4) root */
85 "%ms " /* (5) mount point */
86 "%*s" /* (6) mount options */
87 "%*[^-]" /* (7) optional fields */
88 "- " /* (8) separator */
89 "%*s " /* (9) file system type */
90 "%*s" /* (10) mount source */
91 "%*s" /* (11) mount options 2 */
92 "%*[^\n]", /* some rubbish at the end */
93 &path)) != 1) {
94 if (k == EOF)
95 break;
96
97 log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
98
99 free(path);
100 continue;
101 }
102
103 p = cunescape(path);
104 free(path);
105
106 if (!p) {
107 r = -ENOMEM;
108 goto finish;
109 }
110
111 /* Ignore mount points we can't unmount because they
112 * are API or because we are keeping them open (like
113 * /dev/console) */
114 if (mount_point_is_api(p) ||
115 mount_point_ignore(p) ||
116 path_equal(p, "/dev/console")) {
117 free(p);
118 continue;
119 }
120
121 if (!(m = new0(MountPoint, 1))) {
122 free(p);
123 r = -ENOMEM;
124 goto finish;
125 }
126
127 m->path = p;
128 LIST_PREPEND(mount_point, *head, m);
129 }
130
131 r = 0;
132
133 finish:
134 fclose(proc_self_mountinfo);
135
136 return r;
137 }
138
139 static int swap_list_get(MountPoint **head) {
140 FILE *proc_swaps;
141 unsigned int i;
142 int r;
143
144 assert(head);
145
146 if (!(proc_swaps = fopen("/proc/swaps", "re")))
147 return (errno == ENOENT) ? 0 : -errno;
148
149 (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
150
151 for (i = 2;; i++) {
152 MountPoint *swap;
153 char *dev = NULL, *d;
154 int k;
155
156 if ((k = fscanf(proc_swaps,
157 "%ms " /* device/file */
158 "%*s " /* type of swap */
159 "%*s " /* swap size */
160 "%*s " /* used */
161 "%*s\n", /* priority */
162 &dev)) != 1) {
163
164 if (k == EOF)
165 break;
166
167 log_warning("Failed to parse /proc/swaps:%u.", i);
168
169 free(dev);
170 continue;
171 }
172
173 if (endswith(dev, " (deleted)")) {
174 free(dev);
175 continue;
176 }
177
178 d = cunescape(dev);
179 free(dev);
180
181 if (!d) {
182 r = -ENOMEM;
183 goto finish;
184 }
185
186 if (!(swap = new0(MountPoint, 1))) {
187 free(d);
188 r = -ENOMEM;
189 goto finish;
190 }
191
192 swap->path = d;
193 LIST_PREPEND(mount_point, *head, swap);
194 }
195
196 r = 0;
197
198 finish:
199 fclose(proc_swaps);
200
201 return r;
202 }
203
204 static int loopback_list_get(MountPoint **head) {
205 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
206 struct udev_list_entry *item = NULL, *first = NULL;
207 _cleanup_udev_unref_ struct udev *udev = NULL;
208 int r;
209
210 assert(head);
211
212 udev = udev_new();
213 if (!udev)
214 return -ENOMEM;
215
216 e = udev_enumerate_new(udev);
217 if (!e)
218 return -ENOMEM;
219
220 r = udev_enumerate_add_match_subsystem(e, "block");
221 if (r < 0)
222 return r;
223
224 r = udev_enumerate_add_match_sysname(e, "loop*");
225 if (r < 0)
226 return r;
227
228 r = udev_enumerate_add_match_sysattr(e, "loop/backing_file", NULL);
229 if (r < 0)
230 return r;
231
232 r = udev_enumerate_scan_devices(e);
233 if (r < 0)
234 return r;
235
236 first = udev_enumerate_get_list_entry(e);
237 udev_list_entry_foreach(item, first) {
238 MountPoint *lb;
239 _cleanup_udev_device_unref_ struct udev_device *d;
240 char *loop;
241 const char *dn;
242
243 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
244 if (!d)
245 return -ENOMEM;
246
247 dn = udev_device_get_devnode(d);
248 if (!dn)
249 continue;
250
251 loop = strdup(dn);
252 if (!loop)
253 return -ENOMEM;
254
255 lb = new0(MountPoint, 1);
256 if (!lb) {
257 free(loop);
258 return -ENOMEM;
259 }
260
261 lb->path = loop;
262 LIST_PREPEND(mount_point, *head, lb);
263 }
264
265 return 0;
266 }
267
268 static int dm_list_get(MountPoint **head) {
269 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
270 struct udev_list_entry *item = NULL, *first = NULL;
271 _cleanup_udev_unref_ struct udev *udev = NULL;
272 int r;
273
274 assert(head);
275
276 udev = udev_new();
277 if (!udev)
278 return -ENOMEM;
279
280 e = udev_enumerate_new(udev);
281 if (!e)
282 return -ENOMEM;
283
284 r = udev_enumerate_add_match_subsystem(e, "block");
285 if (r < 0)
286 return r;
287
288 r = udev_enumerate_add_match_sysname(e, "dm-*");
289 if (r < 0)
290 return r;
291
292 r = udev_enumerate_scan_devices(e);
293 if (r < 0)
294 return r;
295
296 first = udev_enumerate_get_list_entry(e);
297 udev_list_entry_foreach(item, first) {
298 MountPoint *m;
299 _cleanup_udev_device_unref_ struct udev_device *d;
300 dev_t devnum;
301 char *node;
302 const char *dn;
303
304 d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item));
305 if (!d)
306 return -ENOMEM;
307
308 devnum = udev_device_get_devnum(d);
309 dn = udev_device_get_devnode(d);
310 if (major(devnum) == 0 || !dn)
311 continue;
312
313 node = strdup(dn);
314 if (!node)
315 return -ENOMEM;
316
317 m = new(MountPoint, 1);
318 if (!m) {
319 free(node);
320 return -ENOMEM;
321 }
322
323 m->path = node;
324 m->devnum = devnum;
325 LIST_PREPEND(mount_point, *head, m);
326 }
327
328 return 0;
329 }
330
331 static int delete_loopback(const char *device) {
332 _cleanup_close_ int fd = -1;
333 int r;
334
335 fd = open(device, O_RDONLY|O_CLOEXEC);
336 if (fd < 0)
337 return errno == ENOENT ? 0 : -errno;
338
339 r = ioctl(fd, LOOP_CLR_FD, 0);
340 if (r >= 0)
341 return 1;
342
343 /* ENXIO: not bound, so no error */
344 if (errno == ENXIO)
345 return 0;
346
347 return -errno;
348 }
349
350 static int delete_dm(dev_t devnum) {
351 _cleanup_close_ int fd = -1;
352 int r;
353 struct dm_ioctl dm = {
354 .version = {DM_VERSION_MAJOR,
355 DM_VERSION_MINOR,
356 DM_VERSION_PATCHLEVEL},
357 .data_size = sizeof(dm),
358 .dev = devnum,
359 };
360
361 assert(major(devnum) != 0);
362
363 fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
364 if (fd < 0)
365 return -errno;
366
367 r = ioctl(fd, DM_DEV_REMOVE, &dm);
368 return r >= 0 ? 0 : -errno;
369 }
370
371 static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) {
372 MountPoint *m, *n;
373 int n_failed = 0;
374
375 assert(head);
376
377 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
378
379 /* If we are in a container, don't attempt to
380 read-only mount anything as that brings no real
381 benefits, but might confuse the host, as we remount
382 the superblock here, not the bind mound. */
383 if (detect_container(NULL) <= 0) {
384 /* We always try to remount directories
385 * read-only first, before we go on and umount
386 * them.
387 *
388 * Mount points can be stacked. If a mount
389 * point is stacked below / or /usr, we
390 * cannot umount or remount it directly,
391 * since there is no way to refer to the
392 * underlying mount. There's nothing we can do
393 * about it for the general case, but we can
394 * do something about it if it is aliased
395 * somehwere else via a bind mount. If we
396 * explicitly remount the super block of that
397 * alias read-only we hence should be
398 * relatively safe regarding keeping the fs we
399 * can otherwise not see dirty. */
400 mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, NULL);
401 }
402
403 /* Skip / and /usr since we cannot unmount that
404 * anyway, since we are running from it. They have
405 * already been remounted ro. */
406 if (path_equal(m->path, "/")
407 #ifndef HAVE_SPLIT_USR
408 || path_equal(m->path, "/usr")
409 #endif
410 )
411 continue;
412
413 /* Trying to umount. We don't force here since we rely
414 * on busy NFS and FUSE file systems to return EBUSY
415 * until we closed everything on top of them. */
416 log_info("Unmounting %s.", m->path);
417 if (umount2(m->path, 0) == 0) {
418 if (changed)
419 *changed = true;
420
421 mount_point_free(head, m);
422 } else if (log_error) {
423 log_warning("Could not unmount %s: %m", m->path);
424 n_failed++;
425 }
426 }
427
428 return n_failed;
429 }
430
431 static int swap_points_list_off(MountPoint **head, bool *changed) {
432 MountPoint *m, *n;
433 int n_failed = 0;
434
435 assert(head);
436
437 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
438 log_info("Deactivating swap %s.", m->path);
439 if (swapoff(m->path) == 0) {
440 if (changed)
441 *changed = true;
442
443 mount_point_free(head, m);
444 } else {
445 log_warning("Could not deactivate swap %s: %m", m->path);
446 n_failed++;
447 }
448 }
449
450 return n_failed;
451 }
452
453 static int loopback_points_list_detach(MountPoint **head, bool *changed) {
454 MountPoint *m, *n;
455 int n_failed = 0, k;
456 struct stat root_st;
457
458 assert(head);
459
460 k = lstat("/", &root_st);
461
462 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
463 int r;
464 struct stat loopback_st;
465
466 if (k >= 0 &&
467 major(root_st.st_dev) != 0 &&
468 lstat(m->path, &loopback_st) >= 0 &&
469 root_st.st_dev == loopback_st.st_rdev) {
470 n_failed ++;
471 continue;
472 }
473
474 log_info("Detaching loopback %s.", m->path);
475 r = delete_loopback(m->path);
476 if (r >= 0) {
477 if (r > 0 && changed)
478 *changed = true;
479
480 mount_point_free(head, m);
481 } else {
482 log_warning("Could not detach loopback %s: %m", m->path);
483 n_failed++;
484 }
485 }
486
487 return n_failed;
488 }
489
490 static int dm_points_list_detach(MountPoint **head, bool *changed) {
491 MountPoint *m, *n;
492 int n_failed = 0, k;
493 struct stat root_st;
494
495 assert(head);
496
497 k = lstat("/", &root_st);
498
499 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
500 int r;
501
502 if (k >= 0 &&
503 major(root_st.st_dev) != 0 &&
504 root_st.st_dev == m->devnum) {
505 n_failed ++;
506 continue;
507 }
508
509 log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum));
510 r = delete_dm(m->devnum);
511 if (r >= 0) {
512 if (changed)
513 *changed = true;
514
515 mount_point_free(head, m);
516 } else {
517 log_warning("Could not detach DM %s: %m", m->path);
518 n_failed++;
519 }
520 }
521
522 return n_failed;
523 }
524
525 int umount_all(bool *changed) {
526 int r;
527 bool umount_changed;
528 LIST_HEAD(MountPoint, mp_list_head);
529
530 LIST_HEAD_INIT(mp_list_head);
531 r = mount_points_list_get(&mp_list_head);
532 if (r < 0)
533 goto end;
534
535 /* retry umount, until nothing can be umounted anymore */
536 do {
537 umount_changed = false;
538
539 mount_points_list_umount(&mp_list_head, &umount_changed, false);
540 if (umount_changed)
541 *changed = true;
542
543 } while (umount_changed);
544
545 /* umount one more time with logging enabled */
546 r = mount_points_list_umount(&mp_list_head, &umount_changed, true);
547 if (r <= 0)
548 goto end;
549
550 end:
551 mount_points_list_free(&mp_list_head);
552
553 return r;
554 }
555
556 int swapoff_all(bool *changed) {
557 int r;
558 LIST_HEAD(MountPoint, swap_list_head);
559
560 LIST_HEAD_INIT(swap_list_head);
561
562 r = swap_list_get(&swap_list_head);
563 if (r < 0)
564 goto end;
565
566 r = swap_points_list_off(&swap_list_head, changed);
567
568 end:
569 mount_points_list_free(&swap_list_head);
570
571 return r;
572 }
573
574 int loopback_detach_all(bool *changed) {
575 int r;
576 LIST_HEAD(MountPoint, loopback_list_head);
577
578 LIST_HEAD_INIT(loopback_list_head);
579
580 r = loopback_list_get(&loopback_list_head);
581 if (r < 0)
582 goto end;
583
584 r = loopback_points_list_detach(&loopback_list_head, changed);
585
586 end:
587 mount_points_list_free(&loopback_list_head);
588
589 return r;
590 }
591
592 int dm_detach_all(bool *changed) {
593 int r;
594 LIST_HEAD(MountPoint, dm_list_head);
595
596 LIST_HEAD_INIT(dm_list_head);
597
598 r = dm_list_get(&dm_list_head);
599 if (r < 0)
600 goto end;
601
602 r = dm_points_list_detach(&dm_list_head, changed);
603
604 end:
605 mount_points_list_free(&dm_list_head);
606
607 return r;
608 }