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