]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/umount.c
24c0947f2172c624f76940ab7faf5b7af2897430
[thirdparty/systemd.git] / src / 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 #include <libudev.h>
31
32 #include "list.h"
33 #include "mount-setup.h"
34 #include "umount.h"
35 #include "util.h"
36
37 typedef struct MountPoint {
38 char *path;
39 dev_t devnum;
40 bool skip_ro;
41 LIST_FIELDS (struct MountPoint, mount_point);
42 } MountPoint;
43
44 static void mount_point_free(MountPoint **head, MountPoint *m) {
45 assert(head);
46 assert(m);
47
48 LIST_REMOVE(MountPoint, mount_point, *head, m);
49
50 free(m->path);
51 free(m);
52 }
53
54 static void mount_points_list_free(MountPoint **head) {
55 assert(head);
56
57 while (*head)
58 mount_point_free(head, *head);
59 }
60
61 static int mount_points_list_get(MountPoint **head) {
62 FILE *proc_self_mountinfo;
63 char *path, *p;
64 unsigned int i;
65 int r;
66
67 assert(head);
68
69 if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
70 return -errno;
71
72 for (i = 1;; i++) {
73 int k;
74 MountPoint *m;
75 char *root;
76 bool skip_ro;
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 "%ms " /* (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 &root,
94 &path)) != 2) {
95 if (k == EOF)
96 break;
97
98 log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
99
100 free(path);
101 continue;
102 }
103
104 /* If we encounter a bind mount, don't try to remount
105 * the source dir too early */
106 skip_ro = !streq(root, "/");
107 free(root);
108
109 p = cunescape(path);
110 free(path);
111
112 if (!p) {
113 r = -ENOMEM;
114 goto finish;
115 }
116
117 if (mount_point_is_api(p) || mount_point_ignore(p)) {
118 free(p);
119 continue;
120 }
121
122 if (!(m = new0(MountPoint, 1))) {
123 free(p);
124 r = -ENOMEM;
125 goto finish;
126 }
127
128 m->path = p;
129 m->skip_ro = skip_ro;
130 LIST_PREPEND(MountPoint, mount_point, *head, m);
131 }
132
133 r = 0;
134
135 finish:
136 fclose(proc_self_mountinfo);
137
138 return r;
139 }
140
141 static int swap_list_get(MountPoint **head) {
142 FILE *proc_swaps;
143 unsigned int i;
144 int r;
145
146 assert(head);
147
148 if (!(proc_swaps = fopen("/proc/swaps", "re")))
149 return (errno == ENOENT) ? 0 : -errno;
150
151 (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
152
153 for (i = 2;; i++) {
154 MountPoint *swap;
155 char *dev = NULL, *d;
156 int k;
157
158 if ((k = fscanf(proc_swaps,
159 "%ms " /* device/file */
160 "%*s " /* type of swap */
161 "%*s " /* swap size */
162 "%*s " /* used */
163 "%*s\n", /* priority */
164 &dev)) != 1) {
165
166 if (k == EOF)
167 break;
168
169 log_warning("Failed to parse /proc/swaps:%u.", i);
170
171 free(dev);
172 continue;
173 }
174
175 if (endswith(dev, "(deleted)")) {
176 free(dev);
177 continue;
178 }
179
180 d = cunescape(dev);
181 free(dev);
182
183 if (!d) {
184 r = -ENOMEM;
185 goto finish;
186 }
187
188 if (!(swap = new0(MountPoint, 1))) {
189 free(d);
190 r = -ENOMEM;
191 goto finish;
192 }
193
194 swap->path = d;
195 LIST_PREPEND(MountPoint, mount_point, *head, swap);
196 }
197
198 r = 0;
199
200 finish:
201 fclose(proc_swaps);
202
203 return r;
204 }
205
206 static int loopback_list_get(MountPoint **head) {
207 int r;
208 struct udev *udev;
209 struct udev_enumerate *e = NULL;
210 struct udev_list_entry *item = NULL, *first = NULL;
211
212 assert(head);
213
214 if (!(udev = udev_new())) {
215 r = -ENOMEM;
216 goto finish;
217 }
218
219 if (!(e = udev_enumerate_new(udev))) {
220 r = -ENOMEM;
221 goto finish;
222 }
223
224 if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
225 udev_enumerate_add_match_sysname(e, "loop*") < 0) {
226 r = -EIO;
227 goto finish;
228 }
229
230 if (udev_enumerate_scan_devices(e) < 0) {
231 r = -EIO;
232 goto finish;
233 }
234
235 first = udev_enumerate_get_list_entry(e);
236 udev_list_entry_foreach(item, first) {
237 MountPoint *lb;
238 struct udev_device *d;
239 char *loop;
240 const char *dn;
241
242 if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
243 r = -ENOMEM;
244 goto finish;
245 }
246
247 if (!(dn = udev_device_get_devnode(d))) {
248 udev_device_unref(d);
249 continue;
250 }
251
252 loop = strdup(dn);
253 udev_device_unref(d);
254
255 if (!loop) {
256 r = -ENOMEM;
257 goto finish;
258 }
259
260 if (!(lb = new0(MountPoint, 1))) {
261 free(loop);
262 r = -ENOMEM;
263 goto finish;
264 }
265
266 lb->path = loop;
267 LIST_PREPEND(MountPoint, mount_point, *head, lb);
268 }
269
270 r = 0;
271
272 finish:
273 if (e)
274 udev_enumerate_unref(e);
275
276 if (udev)
277 udev_unref(udev);
278
279 return r;
280 }
281
282 static int dm_list_get(MountPoint **head) {
283 int r;
284 struct udev *udev;
285 struct udev_enumerate *e = NULL;
286 struct udev_list_entry *item = NULL, *first = NULL;
287
288 assert(head);
289
290 if (!(udev = udev_new())) {
291 r = -ENOMEM;
292 goto finish;
293 }
294
295 if (!(e = udev_enumerate_new(udev))) {
296 r = -ENOMEM;
297 goto finish;
298 }
299
300 if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
301 udev_enumerate_add_match_sysname(e, "dm-*") < 0) {
302 r = -EIO;
303 goto finish;
304 }
305
306 if (udev_enumerate_scan_devices(e) < 0) {
307 r = -EIO;
308 goto finish;
309 }
310
311 first = udev_enumerate_get_list_entry(e);
312
313 udev_list_entry_foreach(item, first) {
314 MountPoint *m;
315 struct udev_device *d;
316 dev_t devnum;
317 char *node;
318 const char *dn;
319
320 if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
321 r = -ENOMEM;
322 goto finish;
323 }
324
325 devnum = udev_device_get_devnum(d);
326 dn = udev_device_get_devnode(d);
327
328 if (major(devnum) == 0 || !dn) {
329 udev_device_unref(d);
330 continue;
331 }
332
333 node = strdup(dn);
334 udev_device_unref(d);
335
336 if (!node) {
337 r = -ENOMEM;
338 goto finish;
339 }
340
341 if (!(m = new(MountPoint, 1))) {
342 free(node);
343 r = -ENOMEM;
344 goto finish;
345 }
346
347 m->path = node;
348 m->devnum = devnum;
349 LIST_PREPEND(MountPoint, mount_point, *head, m);
350 }
351
352 r = 0;
353
354 finish:
355 if (e)
356 udev_enumerate_unref(e);
357
358 if (udev)
359 udev_unref(udev);
360
361 return r;
362 }
363
364 static int delete_loopback(const char *device) {
365 int fd, r;
366
367 if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0)
368 return errno == ENOENT ? 0 : -errno;
369
370 r = ioctl(fd, LOOP_CLR_FD, 0);
371 close_nointr_nofail(fd);
372
373 if (r >= 0)
374 return 1;
375
376 /* ENXIO: not bound, so no error */
377 if (errno == ENXIO)
378 return 0;
379
380 return -errno;
381 }
382
383 static int delete_dm(dev_t devnum) {
384 int fd, r;
385 struct dm_ioctl dm;
386
387 assert(major(devnum) != 0);
388
389 if ((fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC)) < 0)
390 return -errno;
391
392 zero(dm);
393 dm.version[0] = DM_VERSION_MAJOR;
394 dm.version[1] = DM_VERSION_MINOR;
395 dm.version[2] = DM_VERSION_PATCHLEVEL;
396
397 dm.data_size = sizeof(dm);
398 dm.dev = devnum;
399
400 r = ioctl(fd, DM_DEV_REMOVE, &dm);
401 close_nointr_nofail(fd);
402
403 return r >= 0 ? 0 : -errno;
404 }
405
406 static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) {
407 MountPoint *m, *n;
408 int n_failed = 0;
409
410 assert(head);
411
412 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
413 if (path_equal(m->path, "/")
414 #ifndef HAVE_SPLIT_USR
415 || path_equal(m->path, "/usr")
416 #endif
417 ) {
418 n_failed++;
419 continue;
420 }
421
422 /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
423 if (umount2(m->path, MNT_FORCE) == 0) {
424 log_info("Unmounted %s.", m->path);
425 if (changed)
426 *changed = true;
427
428 mount_point_free(head, m);
429 } else if (log_error) {
430 log_warning("Could not unmount %s: %m", m->path);
431 n_failed++;
432 }
433 }
434
435 return n_failed;
436 }
437
438 static int mount_points_list_remount_read_only(MountPoint **head, bool *changed) {
439 MountPoint *m, *n;
440 int n_failed = 0;
441
442 assert(head);
443
444 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
445
446 if (m->skip_ro) {
447 n_failed++;
448 continue;
449 }
450
451 /* Trying to remount read-only */
452 if (mount(NULL, m->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) {
453 if (changed)
454 *changed = true;
455
456 mount_point_free(head, m);
457 } else {
458 log_warning("Could not remount as read-only %s: %m", m->path);
459 n_failed++;
460 }
461 }
462
463 return n_failed;
464 }
465
466 static int swap_points_list_off(MountPoint **head, bool *changed) {
467 MountPoint *m, *n;
468 int n_failed = 0;
469
470 assert(head);
471
472 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
473 if (swapoff(m->path) == 0) {
474 if (changed)
475 *changed = true;
476
477 mount_point_free(head, m);
478 } else {
479 log_warning("Could not deactivate swap %s: %m", m->path);
480 n_failed++;
481 }
482 }
483
484 return n_failed;
485 }
486
487 static int loopback_points_list_detach(MountPoint **head, bool *changed) {
488 MountPoint *m, *n;
489 int n_failed = 0, k;
490 struct stat root_st;
491
492 assert(head);
493
494 k = lstat("/", &root_st);
495
496 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
497 int r;
498 struct stat loopback_st;
499
500 if (k >= 0 &&
501 major(root_st.st_dev) != 0 &&
502 lstat(m->path, &loopback_st) >= 0 &&
503 root_st.st_dev == loopback_st.st_rdev) {
504 n_failed ++;
505 continue;
506 }
507
508 if ((r = delete_loopback(m->path)) >= 0) {
509
510 if (r > 0 && changed)
511 *changed = true;
512
513 mount_point_free(head, m);
514 } else {
515 log_warning("Could not delete loopback %s: %m", m->path);
516 n_failed++;
517 }
518 }
519
520 return n_failed;
521 }
522
523 static int dm_points_list_detach(MountPoint **head, bool *changed) {
524 MountPoint *m, *n;
525 int n_failed = 0, k;
526 struct stat root_st;
527
528 assert(head);
529
530 k = lstat("/", &root_st);
531
532 LIST_FOREACH_SAFE(mount_point, m, n, *head) {
533 int r;
534
535 if (k >= 0 &&
536 major(root_st.st_dev) != 0 &&
537 root_st.st_dev == m->devnum) {
538 n_failed ++;
539 continue;
540 }
541
542 if ((r = delete_dm(m->devnum)) >= 0) {
543
544 if (r > 0 && changed)
545 *changed = true;
546
547 mount_point_free(head, m);
548 } else {
549 log_warning("Could not delete dm %s: %m", m->path);
550 n_failed++;
551 }
552 }
553
554 return n_failed;
555 }
556
557 int umount_all(bool *changed) {
558 int r;
559 bool umount_changed;
560
561 LIST_HEAD(MountPoint, mp_list_head);
562
563 LIST_HEAD_INIT(MountPoint, mp_list_head);
564
565 r = mount_points_list_get(&mp_list_head);
566 if (r < 0)
567 goto end;
568
569 /* retry umount, until nothing can be umounted anymore */
570 do {
571 umount_changed = false;
572
573 mount_points_list_umount(&mp_list_head, &umount_changed, false);
574 if (umount_changed)
575 *changed = true;
576
577 } while (umount_changed);
578
579 /* umount one more time with logging enabled */
580 r = mount_points_list_umount(&mp_list_head, &umount_changed, true);
581 if (r <= 0)
582 goto end;
583
584 r = mount_points_list_remount_read_only(&mp_list_head, changed);
585
586 end:
587 mount_points_list_free(&mp_list_head);
588
589 return r;
590 }
591
592 int swapoff_all(bool *changed) {
593 int r;
594 LIST_HEAD(MountPoint, swap_list_head);
595
596 LIST_HEAD_INIT(MountPoint, swap_list_head);
597
598 r = swap_list_get(&swap_list_head);
599 if (r < 0)
600 goto end;
601
602 r = swap_points_list_off(&swap_list_head, changed);
603
604 end:
605 mount_points_list_free(&swap_list_head);
606
607 return r;
608 }
609
610 int loopback_detach_all(bool *changed) {
611 int r;
612 LIST_HEAD(MountPoint, loopback_list_head);
613
614 LIST_HEAD_INIT(MountPoint, loopback_list_head);
615
616 r = loopback_list_get(&loopback_list_head);
617 if (r < 0)
618 goto end;
619
620 r = loopback_points_list_detach(&loopback_list_head, changed);
621
622 end:
623 mount_points_list_free(&loopback_list_head);
624
625 return r;
626 }
627
628 int dm_detach_all(bool *changed) {
629 int r;
630 LIST_HEAD(MountPoint, dm_list_head);
631
632 LIST_HEAD_INIT(MountPoint, dm_list_head);
633
634 r = dm_list_get(&dm_list_head);
635 if (r < 0)
636 goto end;
637
638 r = dm_points_list_detach(&dm_list_head, changed);
639
640 end:
641 mount_points_list_free(&dm_list_head);
642
643 return r;
644 }