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