chiark / gitweb /
Use _cleanup_ when reading config files
[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 _cleanup_close_ fd = -1;
383         int r;
384         struct dm_ioctl dm = {
385                 .version = {DM_VERSION_MAJOR,
386                             DM_VERSION_MINOR,
387                             DM_VERSION_PATCHLEVEL},
388                 .data_size = sizeof(dm),
389                 .dev = devnum,
390         };
391
392         assert(major(devnum) != 0);
393
394         fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
395         if (fd < 0)
396                 return -errno;
397
398         r = ioctl(fd, DM_DEV_REMOVE, &dm);
399         return r >= 0 ? 0 : -errno;
400 }
401
402 static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) {
403         MountPoint *m, *n;
404         int n_failed = 0;
405
406         assert(head);
407
408         LIST_FOREACH_SAFE(mount_point, m, n, *head) {
409
410                 /* If we are in a container, don't attempt to
411                    read-only mount anything as that brings no real
412                    benefits, but might confuse the host, as we remount
413                    the superblock here, not the bind mound. */
414                 if (detect_container(NULL) <= 0)  {
415                         /* We always try to remount directories
416                          * read-only first, before we go on and umount
417                          * them.
418                          *
419                          * Mount points can be stacked. If a mount
420                          * point is stacked below / or /usr, we
421                          * cannnot umount or remount it directly,
422                          * since there is no way to refer to the
423                          * underlying mount. There's nothing we can do
424                          * about it for the general case, but we can
425                          * do something about it if it is aliased
426                          * somehwere else via a bind mount. If we
427                          * explicitly remount the super block of that
428                          * alias read-only we hence should be
429                          * relatively safe regarding keeping the fs we
430                          * can otherwise not see dirty. */
431                         mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, NULL);
432                 }
433
434                 /* Skip / and /usr since we cannot unmount that
435                  * anyway, since we are running from it. They have
436                  * already been remounted ro. */
437                 if (path_equal(m->path, "/")
438 #ifndef HAVE_SPLIT_USR
439                     || path_equal(m->path, "/usr")
440 #endif
441                 )
442                         continue;
443
444                 /* Trying to umount. We don't force here since we rely
445                  * on busy NFS and FUSE file systems to return EBUSY
446                  * until we closed everything on top of them. */
447                 log_info("Unmounting %s.", m->path);
448                 if (umount2(m->path, 0) == 0) {
449                         if (changed)
450                                 *changed = true;
451
452                         mount_point_free(head, m);
453                 } else if (log_error) {
454                         log_warning("Could not unmount %s: %m", m->path);
455                         n_failed++;
456                 }
457         }
458
459         return n_failed;
460 }
461
462 static int swap_points_list_off(MountPoint **head, bool *changed) {
463         MountPoint *m, *n;
464         int n_failed = 0;
465
466         assert(head);
467
468         LIST_FOREACH_SAFE(mount_point, m, n, *head) {
469                 log_info("Deactivating swap %s.", m->path);
470                 if (swapoff(m->path) == 0) {
471                         if (changed)
472                                 *changed = true;
473
474                         mount_point_free(head, m);
475                 } else {
476                         log_warning("Could not deactivate swap %s: %m", m->path);
477                         n_failed++;
478                 }
479         }
480
481         return n_failed;
482 }
483
484 static int loopback_points_list_detach(MountPoint **head, bool *changed) {
485         MountPoint *m, *n;
486         int n_failed = 0, k;
487         struct stat root_st;
488
489         assert(head);
490
491         k = lstat("/", &root_st);
492
493         LIST_FOREACH_SAFE(mount_point, m, n, *head) {
494                 int r;
495                 struct stat loopback_st;
496
497                 if (k >= 0 &&
498                     major(root_st.st_dev) != 0 &&
499                     lstat(m->path, &loopback_st) >= 0 &&
500                     root_st.st_dev == loopback_st.st_rdev) {
501                         n_failed ++;
502                         continue;
503                 }
504
505                 log_info("Detaching loopback %s.", m->path);
506                 r = delete_loopback(m->path);
507                 if (r >= 0) {
508                         if (r > 0 && changed)
509                                 *changed = true;
510
511                         mount_point_free(head, m);
512                 } else {
513                         log_warning("Could not detach loopback %s: %m", m->path);
514                         n_failed++;
515                 }
516         }
517
518         return n_failed;
519 }
520
521 static int dm_points_list_detach(MountPoint **head, bool *changed) {
522         MountPoint *m, *n;
523         int n_failed = 0, k;
524         struct stat root_st;
525
526         assert(head);
527
528         k = lstat("/", &root_st);
529
530         LIST_FOREACH_SAFE(mount_point, m, n, *head) {
531                 int r;
532
533                 if (k >= 0 &&
534                     major(root_st.st_dev) != 0 &&
535                     root_st.st_dev == m->devnum) {
536                         n_failed ++;
537                         continue;
538                 }
539
540                 log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum));
541                 r = delete_dm(m->devnum);
542                 if (r >= 0) {
543                         if (changed)
544                                 *changed = true;
545
546                         mount_point_free(head, m);
547                 } else {
548                         log_warning("Could not detach DM %s: %m", m->path);
549                         n_failed++;
550                 }
551         }
552
553         return n_failed;
554 }
555
556 int umount_all(bool *changed) {
557         int r;
558         bool umount_changed;
559         LIST_HEAD(MountPoint, mp_list_head);
560
561         LIST_HEAD_INIT(MountPoint, mp_list_head);
562         r = mount_points_list_get(&mp_list_head);
563         if (r < 0)
564                 goto end;
565
566         /* retry umount, until nothing can be umounted anymore */
567         do {
568                 umount_changed = false;
569
570                 mount_points_list_umount(&mp_list_head, &umount_changed, false);
571                 if (umount_changed)
572                         *changed = true;
573
574         } while (umount_changed);
575
576         /* umount one more time with logging enabled */
577         r = mount_points_list_umount(&mp_list_head, &umount_changed, true);
578         if (r <= 0)
579                 goto end;
580
581   end:
582         mount_points_list_free(&mp_list_head);
583
584         return r;
585 }
586
587 int swapoff_all(bool *changed) {
588         int r;
589         LIST_HEAD(MountPoint, swap_list_head);
590
591         LIST_HEAD_INIT(MountPoint, swap_list_head);
592
593         r = swap_list_get(&swap_list_head);
594         if (r < 0)
595                 goto end;
596
597         r = swap_points_list_off(&swap_list_head, changed);
598
599   end:
600         mount_points_list_free(&swap_list_head);
601
602         return r;
603 }
604
605 int loopback_detach_all(bool *changed) {
606         int r;
607         LIST_HEAD(MountPoint, loopback_list_head);
608
609         LIST_HEAD_INIT(MountPoint, loopback_list_head);
610
611         r = loopback_list_get(&loopback_list_head);
612         if (r < 0)
613                 goto end;
614
615         r = loopback_points_list_detach(&loopback_list_head, changed);
616
617   end:
618         mount_points_list_free(&loopback_list_head);
619
620         return r;
621 }
622
623 int dm_detach_all(bool *changed) {
624         int r;
625         LIST_HEAD(MountPoint, dm_list_head);
626
627         LIST_HEAD_INIT(MountPoint, dm_list_head);
628
629         r = dm_list_get(&dm_list_head);
630         if (r < 0)
631                 goto end;
632
633         r = dm_points_list_detach(&dm_list_head, changed);
634
635   end:
636         mount_points_list_free(&dm_list_head);
637
638         return r;
639 }