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