chiark / gitweb /
shutdown: ignore loop devices without a backing file
[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 "path-util.h"
36 #include "util.h"
37 #include "virt.h"
38
39 typedef struct MountPoint {
40         char *path;
41         dev_t devnum;
42         LIST_FIELDS (struct MountPoint, mount_point);
43 } MountPoint;
44
45 static void mount_point_free(MountPoint **head, MountPoint *m) {
46         assert(head);
47         assert(m);
48
49         LIST_REMOVE(MountPoint, mount_point, *head, m);
50
51         free(m->path);
52         free(m);
53 }
54
55 static void mount_points_list_free(MountPoint **head) {
56         assert(head);
57
58         while (*head)
59                 mount_point_free(head, *head);
60 }
61
62 static int mount_points_list_get(MountPoint **head) {
63         FILE *proc_self_mountinfo;
64         char *path, *p;
65         unsigned int i;
66         int r;
67
68         assert(head);
69
70         if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
71                 return -errno;
72
73         for (i = 1;; i++) {
74                 int k;
75                 MountPoint *m;
76
77                 path = p = NULL;
78
79                 if ((k = fscanf(proc_self_mountinfo,
80                                 "%*s "       /* (1) mount id */
81                                 "%*s "       /* (2) parent id */
82                                 "%*s "       /* (3) major:minor */
83                                 "%*s "       /* (4) root */
84                                 "%ms "       /* (5) mount point */
85                                 "%*s"        /* (6) mount options */
86                                 "%*[^-]"     /* (7) optional fields */
87                                 "- "         /* (8) separator */
88                                 "%*s "       /* (9) file system type */
89                                 "%*s"        /* (10) mount source */
90                                 "%*s"        /* (11) mount options 2 */
91                                 "%*[^\n]",   /* some rubbish at the end */
92                                 &path)) != 1) {
93                         if (k == EOF)
94                                 break;
95
96                         log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
97
98                         free(path);
99                         continue;
100                 }
101
102                 p = cunescape(path);
103                 free(path);
104
105                 if (!p) {
106                         r = -ENOMEM;
107                         goto finish;
108                 }
109
110                 /* Ignore mount points we can't unmount because they
111                  * are API or because we are keeping them open (like
112                  * /dev/console) */
113                 if (mount_point_is_api(p) ||
114                     mount_point_ignore(p) ||
115                     path_equal(p, "/dev/console")) {
116                         free(p);
117                         continue;
118                 }
119
120                 if (!(m = new0(MountPoint, 1))) {
121                         free(p);
122                         r = -ENOMEM;
123                         goto finish;
124                 }
125
126                 m->path = p;
127                 LIST_PREPEND(MountPoint, mount_point, *head, m);
128         }
129
130         r = 0;
131
132 finish:
133         fclose(proc_self_mountinfo);
134
135         return r;
136 }
137
138 static int swap_list_get(MountPoint **head) {
139         FILE *proc_swaps;
140         unsigned int i;
141         int r;
142
143         assert(head);
144
145         if (!(proc_swaps = fopen("/proc/swaps", "re")))
146                 return (errno == ENOENT) ? 0 : -errno;
147
148         (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");
149
150         for (i = 2;; i++) {
151                 MountPoint *swap;
152                 char *dev = NULL, *d;
153                 int k;
154
155                 if ((k = fscanf(proc_swaps,
156                                 "%ms " /* device/file */
157                                 "%*s " /* type of swap */
158                                 "%*s " /* swap size */
159                                 "%*s " /* used */
160                                 "%*s\n", /* priority */
161                                 &dev)) != 1) {
162
163                         if (k == EOF)
164                                 break;
165
166                         log_warning("Failed to parse /proc/swaps:%u.", i);
167
168                         free(dev);
169                         continue;
170                 }
171
172                 if (endswith(dev, "(deleted)")) {
173                         free(dev);
174                         continue;
175                 }
176
177                 d = cunescape(dev);
178                 free(dev);
179
180                 if (!d) {
181                         r = -ENOMEM;
182                         goto finish;
183                 }
184
185                 if (!(swap = new0(MountPoint, 1))) {
186                         free(d);
187                         r = -ENOMEM;
188                         goto finish;
189                 }
190
191                 swap->path = d;
192                 LIST_PREPEND(MountPoint, mount_point, *head, swap);
193         }
194
195         r = 0;
196
197 finish:
198         fclose(proc_swaps);
199
200         return r;
201 }
202
203 static int loopback_list_get(MountPoint **head) {
204         int r;
205         struct udev *udev;
206         struct udev_enumerate *e = NULL;
207         struct udev_list_entry *item = NULL, *first = NULL;
208
209         assert(head);
210
211         if (!(udev = udev_new())) {
212                 r = -ENOMEM;
213                 goto finish;
214         }
215
216         if (!(e = udev_enumerate_new(udev))) {
217                 r = -ENOMEM;
218                 goto finish;
219         }
220
221         if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
222             udev_enumerate_add_match_sysname(e, "loop*") < 0) {
223                 r = -EIO;
224                 goto finish;
225         }
226
227         if (udev_enumerate_scan_devices(e) < 0) {
228                 r = -EIO;
229                 goto finish;
230         }
231
232         first = udev_enumerate_get_list_entry(e);
233         udev_list_entry_foreach(item, first) {
234                 MountPoint *lb;
235                 struct udev_device *d;
236                 const char *backing;
237                 char *loop;
238                 const char *dn;
239
240                 if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
241                         r = -ENOMEM;
242                         goto finish;
243                 }
244
245                 backing = udev_device_get_sysattr_value(d, "loop/backing_file");
246                 if (!backing) {
247                         udev_device_unref(d);
248                         continue;
249                 }
250
251                 if (!(dn = udev_device_get_devnode(d))) {
252                         udev_device_unref(d);
253                         continue;
254                 }
255
256                 loop = strdup(dn);
257                 udev_device_unref(d);
258
259                 if (!loop) {
260                         r = -ENOMEM;
261                         goto finish;
262                 }
263
264                 if (!(lb = new0(MountPoint, 1))) {
265                         free(loop);
266                         r = -ENOMEM;
267                         goto finish;
268                 }
269
270                 lb->path = loop;
271                 LIST_PREPEND(MountPoint, mount_point, *head, lb);
272         }
273
274         r = 0;
275
276 finish:
277         if (e)
278                 udev_enumerate_unref(e);
279
280         if (udev)
281                 udev_unref(udev);
282
283         return r;
284 }
285
286 static int dm_list_get(MountPoint **head) {
287         int r;
288         struct udev *udev;
289         struct udev_enumerate *e = NULL;
290         struct udev_list_entry *item = NULL, *first = NULL;
291
292         assert(head);
293
294         if (!(udev = udev_new())) {
295                 r = -ENOMEM;
296                 goto finish;
297         }
298
299         if (!(e = udev_enumerate_new(udev))) {
300                 r = -ENOMEM;
301                 goto finish;
302         }
303
304         if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
305             udev_enumerate_add_match_sysname(e, "dm-*") < 0) {
306                 r = -EIO;
307                 goto finish;
308         }
309
310         if (udev_enumerate_scan_devices(e) < 0) {
311                 r = -EIO;
312                 goto finish;
313         }
314
315         first = udev_enumerate_get_list_entry(e);
316
317         udev_list_entry_foreach(item, first) {
318                 MountPoint *m;
319                 struct udev_device *d;
320                 dev_t devnum;
321                 char *node;
322                 const char *dn;
323
324                 if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
325                         r = -ENOMEM;
326                         goto finish;
327                 }
328
329                 devnum = udev_device_get_devnum(d);
330                 dn = udev_device_get_devnode(d);
331
332                 if (major(devnum) == 0 || !dn) {
333                         udev_device_unref(d);
334                         continue;
335                 }
336
337                 node = strdup(dn);
338                 udev_device_unref(d);
339
340                 if (!node) {
341                         r = -ENOMEM;
342                         goto finish;
343                 }
344
345                 if (!(m = new(MountPoint, 1))) {
346                         free(node);
347                         r = -ENOMEM;
348                         goto finish;
349                 }
350
351                 m->path = node;
352                 m->devnum = devnum;
353                 LIST_PREPEND(MountPoint, mount_point, *head, m);
354         }
355
356         r = 0;
357
358 finish:
359         if (e)
360                 udev_enumerate_unref(e);
361
362         if (udev)
363                 udev_unref(udev);
364
365         return r;
366 }
367
368 static int delete_loopback(const char *device) {
369         int fd, r;
370
371         if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0)
372                 return errno == ENOENT ? 0 : -errno;
373
374         r = ioctl(fd, LOOP_CLR_FD, 0);
375         close_nointr_nofail(fd);
376
377         if (r >= 0)
378                 return 1;
379
380         /* ENXIO: not bound, so no error */
381         if (errno == ENXIO)
382                 return 0;
383
384         return -errno;
385 }
386
387 static int delete_dm(dev_t devnum) {
388         int fd, r;
389         struct dm_ioctl dm;
390
391         assert(major(devnum) != 0);
392
393         if ((fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC)) < 0)
394                 return -errno;
395
396         zero(dm);
397         dm.version[0] = DM_VERSION_MAJOR;
398         dm.version[1] = DM_VERSION_MINOR;
399         dm.version[2] = DM_VERSION_PATCHLEVEL;
400
401         dm.data_size = sizeof(dm);
402         dm.dev = devnum;
403
404         r = ioctl(fd, DM_DEV_REMOVE, &dm);
405         close_nointr_nofail(fd);
406
407         return r >= 0 ? 0 : -errno;
408 }
409
410 static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) {
411         MountPoint *m, *n;
412         int n_failed = 0;
413
414         assert(head);
415
416         LIST_FOREACH_SAFE(mount_point, m, n, *head) {
417
418                 /* If we are in a container, don't attempt to
419                    read-only mount anything as that brings no real
420                    benefits, but might confuse the host, as we remount
421                    the superblock here, not the bind mound. */
422                 if (detect_container(NULL) <= 0)  {
423                         /* We always try to remount directories
424                          * read-only first, before we go on and umount
425                          * them.
426                          *
427                          * Mount points can be stacked. If a mount
428                          * point is stacked below / or /usr, we
429                          * cannnot umount or remount it directly,
430                          * since there is no way to refer to the
431                          * underlying mount. There's nothing we can do
432                          * about it for the general case, but we can
433                          * do something about it if it is aliased
434                          * somehwere else via a bind mount. If we
435                          * explicitly remount the super block of that
436                          * alias read-only we hence should be
437                          * relatively safe regarding keeping the fs we
438                          * can otherwise not see dirty. */
439                         mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, NULL);
440                 }
441
442                 /* Skip / and /usr since we cannot unmount that
443                  * anyway, since we are running from it. They have
444                  * already been remounted ro. */
445                 if (path_equal(m->path, "/")
446 #ifndef HAVE_SPLIT_USR
447                     || path_equal(m->path, "/usr")
448 #endif
449                 )
450                         continue;
451
452                 /* Trying to umount. We don't force here since we rely
453                  * on busy NFS and FUSE file systems to return EBUSY
454                  * until we closed everything on top of them. */
455                 log_info("Unmounting %s.", m->path);
456                 if (umount2(m->path, 0) == 0) {
457                         if (changed)
458                                 *changed = true;
459
460                         mount_point_free(head, m);
461                 } else if (log_error) {
462                         log_warning("Could not unmount %s: %m", m->path);
463                         n_failed++;
464                 }
465         }
466
467         return n_failed;
468 }
469
470 static int swap_points_list_off(MountPoint **head, bool *changed) {
471         MountPoint *m, *n;
472         int n_failed = 0;
473
474         assert(head);
475
476         LIST_FOREACH_SAFE(mount_point, m, n, *head) {
477                 log_info("Deactivating swap %s.", m->path);
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                 log_info("Detaching loopback %s.", m->path);
514                 r = delete_loopback(m->path);
515                 if (r >= 0) {
516                         if (r > 0 && changed)
517                                 *changed = true;
518
519                         mount_point_free(head, m);
520                 } else {
521                         log_warning("Could not detach loopback %s: %m", m->path);
522                         n_failed++;
523                 }
524         }
525
526         return n_failed;
527 }
528
529 static int dm_points_list_detach(MountPoint **head, bool *changed) {
530         MountPoint *m, *n;
531         int n_failed = 0, k;
532         struct stat root_st;
533
534         assert(head);
535
536         k = lstat("/", &root_st);
537
538         LIST_FOREACH_SAFE(mount_point, m, n, *head) {
539                 int r;
540
541                 if (k >= 0 &&
542                     major(root_st.st_dev) != 0 &&
543                     root_st.st_dev == m->devnum) {
544                         n_failed ++;
545                         continue;
546                 }
547
548                 log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum));
549                 r = delete_dm(m->devnum);
550                 if (r >= 0) {
551                         if (changed)
552                                 *changed = true;
553
554                         mount_point_free(head, m);
555                 } else {
556                         log_warning("Could not detach DM %s: %m", m->path);
557                         n_failed++;
558                 }
559         }
560
561         return n_failed;
562 }
563
564 int umount_all(bool *changed) {
565         int r;
566         bool umount_changed;
567         LIST_HEAD(MountPoint, mp_list_head);
568
569         LIST_HEAD_INIT(MountPoint, mp_list_head);
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   end:
590         mount_points_list_free(&mp_list_head);
591
592         return r;
593 }
594
595 int swapoff_all(bool *changed) {
596         int r;
597         LIST_HEAD(MountPoint, swap_list_head);
598
599         LIST_HEAD_INIT(MountPoint, swap_list_head);
600
601         r = swap_list_get(&swap_list_head);
602         if (r < 0)
603                 goto end;
604
605         r = swap_points_list_off(&swap_list_head, changed);
606
607   end:
608         mount_points_list_free(&swap_list_head);
609
610         return r;
611 }
612
613 int loopback_detach_all(bool *changed) {
614         int r;
615         LIST_HEAD(MountPoint, loopback_list_head);
616
617         LIST_HEAD_INIT(MountPoint, loopback_list_head);
618
619         r = loopback_list_get(&loopback_list_head);
620         if (r < 0)
621                 goto end;
622
623         r = loopback_points_list_detach(&loopback_list_head, changed);
624
625   end:
626         mount_points_list_free(&loopback_list_head);
627
628         return r;
629 }
630
631 int dm_detach_all(bool *changed) {
632         int r;
633         LIST_HEAD(MountPoint, dm_list_head);
634
635         LIST_HEAD_INIT(MountPoint, dm_list_head);
636
637         r = dm_list_get(&dm_list_head);
638         if (r < 0)
639                 goto end;
640
641         r = dm_points_list_detach(&dm_list_head, changed);
642
643   end:
644         mount_points_list_free(&dm_list_head);
645
646         return r;
647 }