chiark / gitweb /
util: move all to shared/ and split external dependencies in separate internal libraries
[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 General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU 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 }