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