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