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