chiark / gitweb /
shutdown: loop - convert to enumerate match
[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             udev_enumerate_add_match_sysattr(e, "loop/backing_file", NULL) < 0) {
224                 r = -EIO;
225                 goto finish;
226         }
227
228         if (udev_enumerate_scan_devices(e) < 0) {
229                 r = -EIO;
230                 goto finish;
231         }
232
233         first = udev_enumerate_get_list_entry(e);
234         udev_list_entry_foreach(item, first) {
235                 MountPoint *lb;
236                 struct udev_device *d;
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                 if (!(dn = udev_device_get_devnode(d))) {
246                         udev_device_unref(d);
247                         continue;
248                 }
249
250                 loop = strdup(dn);
251                 udev_device_unref(d);
252
253                 if (!loop) {
254                         r = -ENOMEM;
255                         goto finish;
256                 }
257
258                 if (!(lb = new0(MountPoint, 1))) {
259                         free(loop);
260                         r = -ENOMEM;
261                         goto finish;
262                 }
263
264                 lb->path = loop;
265                 LIST_PREPEND(MountPoint, mount_point, *head, lb);
266         }
267
268         r = 0;
269
270 finish:
271         if (e)
272                 udev_enumerate_unref(e);
273
274         if (udev)
275                 udev_unref(udev);
276
277         return r;
278 }
279
280 static int dm_list_get(MountPoint **head) {
281         int r;
282         struct udev *udev;
283         struct udev_enumerate *e = NULL;
284         struct udev_list_entry *item = NULL, *first = NULL;
285
286         assert(head);
287
288         if (!(udev = udev_new())) {
289                 r = -ENOMEM;
290                 goto finish;
291         }
292
293         if (!(e = udev_enumerate_new(udev))) {
294                 r = -ENOMEM;
295                 goto finish;
296         }
297
298         if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
299             udev_enumerate_add_match_sysname(e, "dm-*") < 0) {
300                 r = -EIO;
301                 goto finish;
302         }
303
304         if (udev_enumerate_scan_devices(e) < 0) {
305                 r = -EIO;
306                 goto finish;
307         }
308
309         first = udev_enumerate_get_list_entry(e);
310
311         udev_list_entry_foreach(item, first) {
312                 MountPoint *m;
313                 struct udev_device *d;
314                 dev_t devnum;
315                 char *node;
316                 const char *dn;
317
318                 if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
319                         r = -ENOMEM;
320                         goto finish;
321                 }
322
323                 devnum = udev_device_get_devnum(d);
324                 dn = udev_device_get_devnode(d);
325
326                 if (major(devnum) == 0 || !dn) {
327                         udev_device_unref(d);
328                         continue;
329                 }
330
331                 node = strdup(dn);
332                 udev_device_unref(d);
333
334                 if (!node) {
335                         r = -ENOMEM;
336                         goto finish;
337                 }
338
339                 if (!(m = new(MountPoint, 1))) {
340                         free(node);
341                         r = -ENOMEM;
342                         goto finish;
343                 }
344
345                 m->path = node;
346                 m->devnum = devnum;
347                 LIST_PREPEND(MountPoint, mount_point, *head, m);
348         }
349
350         r = 0;
351
352 finish:
353         if (e)
354                 udev_enumerate_unref(e);
355
356         if (udev)
357                 udev_unref(udev);
358
359         return r;
360 }
361
362 static int delete_loopback(const char *device) {
363         int fd, r;
364
365         if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0)
366                 return errno == ENOENT ? 0 : -errno;
367
368         r = ioctl(fd, LOOP_CLR_FD, 0);
369         close_nointr_nofail(fd);
370
371         if (r >= 0)
372                 return 1;
373
374         /* ENXIO: not bound, so no error */
375         if (errno == ENXIO)
376                 return 0;
377
378         return -errno;
379 }
380
381 static int delete_dm(dev_t devnum) {
382         int fd, r;
383         struct dm_ioctl dm;
384
385         assert(major(devnum) != 0);
386
387         if ((fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC)) < 0)
388                 return -errno;
389
390         zero(dm);
391         dm.version[0] = DM_VERSION_MAJOR;
392         dm.version[1] = DM_VERSION_MINOR;
393         dm.version[2] = DM_VERSION_PATCHLEVEL;
394
395         dm.data_size = sizeof(dm);
396         dm.dev = devnum;
397
398         r = ioctl(fd, DM_DEV_REMOVE, &dm);
399         close_nointr_nofail(fd);
400
401         return r >= 0 ? 0 : -errno;
402 }
403
404 static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) {
405         MountPoint *m, *n;
406         int n_failed = 0;
407
408         assert(head);
409
410         LIST_FOREACH_SAFE(mount_point, m, n, *head) {
411
412                 /* If we are in a container, don't attempt to
413                    read-only mount anything as that brings no real
414                    benefits, but might confuse the host, as we remount
415                    the superblock here, not the bind mound. */
416                 if (detect_container(NULL) <= 0)  {
417                         /* We always try to remount directories
418                          * read-only first, before we go on and umount
419                          * them.
420                          *
421                          * Mount points can be stacked. If a mount
422                          * point is stacked below / or /usr, we
423                          * cannnot umount or remount it directly,
424                          * since there is no way to refer to the
425                          * underlying mount. There's nothing we can do
426                          * about it for the general case, but we can
427                          * do something about it if it is aliased
428                          * somehwere else via a bind mount. If we
429                          * explicitly remount the super block of that
430                          * alias read-only we hence should be
431                          * relatively safe regarding keeping the fs we
432                          * can otherwise not see dirty. */
433                         mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, NULL);
434                 }
435
436                 /* Skip / and /usr since we cannot unmount that
437                  * anyway, since we are running from it. They have
438                  * already been remounted ro. */
439                 if (path_equal(m->path, "/")
440 #ifndef HAVE_SPLIT_USR
441                     || path_equal(m->path, "/usr")
442 #endif
443                 )
444                         continue;
445
446                 /* Trying to umount. We don't force here since we rely
447                  * on busy NFS and FUSE file systems to return EBUSY
448                  * until we closed everything on top of them. */
449                 log_info("Unmounting %s.", m->path);
450                 if (umount2(m->path, 0) == 0) {
451                         if (changed)
452                                 *changed = true;
453
454                         mount_point_free(head, m);
455                 } else if (log_error) {
456                         log_warning("Could not unmount %s: %m", m->path);
457                         n_failed++;
458                 }
459         }
460
461         return n_failed;
462 }
463
464 static int swap_points_list_off(MountPoint **head, bool *changed) {
465         MountPoint *m, *n;
466         int n_failed = 0;
467
468         assert(head);
469
470         LIST_FOREACH_SAFE(mount_point, m, n, *head) {
471                 log_info("Deactivating swap %s.", m->path);
472                 if (swapoff(m->path) == 0) {
473                         if (changed)
474                                 *changed = true;
475
476                         mount_point_free(head, m);
477                 } else {
478                         log_warning("Could not deactivate swap %s: %m", m->path);
479                         n_failed++;
480                 }
481         }
482
483         return n_failed;
484 }
485
486 static int loopback_points_list_detach(MountPoint **head, bool *changed) {
487         MountPoint *m, *n;
488         int n_failed = 0, k;
489         struct stat root_st;
490
491         assert(head);
492
493         k = lstat("/", &root_st);
494
495         LIST_FOREACH_SAFE(mount_point, m, n, *head) {
496                 int r;
497                 struct stat loopback_st;
498
499                 if (k >= 0 &&
500                     major(root_st.st_dev) != 0 &&
501                     lstat(m->path, &loopback_st) >= 0 &&
502                     root_st.st_dev == loopback_st.st_rdev) {
503                         n_failed ++;
504                         continue;
505                 }
506
507                 log_info("Detaching loopback %s.", m->path);
508                 r = delete_loopback(m->path);
509                 if (r >= 0) {
510                         if (r > 0 && changed)
511                                 *changed = true;
512
513                         mount_point_free(head, m);
514                 } else {
515                         log_warning("Could not detach 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                 log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum));
543                 r = delete_dm(m->devnum);
544                 if (r >= 0) {
545                         if (changed)
546                                 *changed = true;
547
548                         mount_point_free(head, m);
549                 } else {
550                         log_warning("Could not detach DM %s: %m", m->path);
551                         n_failed++;
552                 }
553         }
554
555         return n_failed;
556 }
557
558 int umount_all(bool *changed) {
559         int r;
560         bool umount_changed;
561         LIST_HEAD(MountPoint, mp_list_head);
562
563         LIST_HEAD_INIT(MountPoint, mp_list_head);
564         r = mount_points_list_get(&mp_list_head);
565         if (r < 0)
566                 goto end;
567
568         /* retry umount, until nothing can be umounted anymore */
569         do {
570                 umount_changed = false;
571
572                 mount_points_list_umount(&mp_list_head, &umount_changed, false);
573                 if (umount_changed)
574                         *changed = true;
575
576         } while (umount_changed);
577
578         /* umount one more time with logging enabled */
579         r = mount_points_list_umount(&mp_list_head, &umount_changed, true);
580         if (r <= 0)
581                 goto end;
582
583   end:
584         mount_points_list_free(&mp_list_head);
585
586         return r;
587 }
588
589 int swapoff_all(bool *changed) {
590         int r;
591         LIST_HEAD(MountPoint, swap_list_head);
592
593         LIST_HEAD_INIT(MountPoint, swap_list_head);
594
595         r = swap_list_get(&swap_list_head);
596         if (r < 0)
597                 goto end;
598
599         r = swap_points_list_off(&swap_list_head, changed);
600
601   end:
602         mount_points_list_free(&swap_list_head);
603
604         return r;
605 }
606
607 int loopback_detach_all(bool *changed) {
608         int r;
609         LIST_HEAD(MountPoint, loopback_list_head);
610
611         LIST_HEAD_INIT(MountPoint, loopback_list_head);
612
613         r = loopback_list_get(&loopback_list_head);
614         if (r < 0)
615                 goto end;
616
617         r = loopback_points_list_detach(&loopback_list_head, changed);
618
619   end:
620         mount_points_list_free(&loopback_list_head);
621
622         return r;
623 }
624
625 int dm_detach_all(bool *changed) {
626         int r;
627         LIST_HEAD(MountPoint, dm_list_head);
628
629         LIST_HEAD_INIT(MountPoint, dm_list_head);
630
631         r = dm_list_get(&dm_list_head);
632         if (r < 0)
633                 goto end;
634
635         r = dm_points_list_detach(&dm_list_head, changed);
636
637   end:
638         mount_points_list_free(&dm_list_head);
639
640         return r;
641 }