chiark / gitweb /
umount: try to get rid of DM devices
[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         LIST_FIELDS (struct MountPoint, mount_point);
40 } MountPoint;
41
42 /* Takes over possession of path */
43 static MountPoint *mount_point_alloc(char *path) {
44         MountPoint *mp;
45
46         if (!(mp = new(MountPoint, 1)))
47                 return NULL;
48
49         mp->path = path;
50         return mp;
51 }
52
53 static void mount_point_remove_and_free(MountPoint *mount_point, MountPoint **mount_point_list_head) {
54         LIST_REMOVE(MountPoint, mount_point, *mount_point_list_head, mount_point);
55
56         free(mount_point->path);
57         free(mount_point);
58 }
59
60 static void mount_points_list_free(MountPoint **mount_point_list_head) {
61         while (*mount_point_list_head)
62                 mount_point_remove_and_free(*mount_point_list_head, mount_point_list_head);
63 }
64
65 static int mount_points_list_get(MountPoint **mount_point_list_head) {
66         FILE *proc_self_mountinfo;
67         char *path, *p;
68         unsigned int i;
69         int r;
70
71         if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
72                 return -errno;
73
74         for (i = 1;; i++) {
75                 int k;
76                 MountPoint *mp;
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                                 "%*s "       /* (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                                 &path)) != 1) {
94                         if (k == EOF)
95                                 break;
96
97                         log_warning("Failed to parse /proc/self/mountinfo:%u.", i);
98
99                         free(path);
100                         continue;
101                 }
102
103                 p = cunescape(path);
104                 free(path);
105
106                 if (!p) {
107                         r = -ENOMEM;
108                         goto finish;
109                 }
110
111                 if (mount_point_is_api(p)) {
112                         free(p);
113                         continue;
114                 }
115
116                 if (!(mp = mount_point_alloc(p))) {
117                         free(p);
118                         r = -ENOMEM;
119                         goto finish;
120                 }
121
122                 LIST_PREPEND(MountPoint, mount_point, *mount_point_list_head, mp);
123         }
124
125         r = 0;
126
127 finish:
128         fclose(proc_self_mountinfo);
129
130         return r;
131 }
132
133 static int swap_list_get(MountPoint **swap_list_head) {
134         FILE *proc_swaps;
135         unsigned int i;
136         int r;
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                 swap = mount_point_alloc(d);
179                 if (!swap) {
180                         free(d);
181                         r = -ENOMEM;
182                         goto finish;
183                 }
184
185                 LIST_PREPEND(MountPoint, mount_point, *swap_list_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 **loopback_list_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         if (!(udev = udev_new())) {
203                 r = -ENOMEM;
204                 goto finish;
205         }
206
207         if (!(e = udev_enumerate_new(udev))) {
208                 r = -ENOMEM;
209                 goto finish;
210         }
211
212         if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
213             udev_enumerate_add_match_sysname(e, "loop*") < 0) {
214                 r = -EIO;
215                 goto finish;
216         }
217
218         if (udev_enumerate_scan_devices(e) < 0) {
219                 r = -EIO;
220                 goto finish;
221         }
222
223         first = udev_enumerate_get_list_entry(e);
224
225         udev_list_entry_foreach(item, first) {
226                 MountPoint *lb;
227                 struct udev_device *d;
228                 char *loop;
229                 const char *dn;
230
231                 if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
232                         r = -ENOMEM;
233                         goto finish;
234                 }
235
236                 if ((dn = udev_device_get_devnode(d))) {
237                         loop = strdup(dn);
238                         udev_device_unref(d);
239
240                         if (!loop) {
241                                 r = -ENOMEM;
242                                 goto finish;
243                         }
244                 } else
245                         udev_device_unref(d);
246
247                 if (!(lb = mount_point_alloc(loop))) {
248                         free(loop);
249                         r = -ENOMEM;
250                         goto finish;
251                 }
252
253                 LIST_PREPEND(MountPoint, mount_point, *loopback_list_head, lb);
254         }
255
256         r = 0;
257
258 finish:
259         if (e)
260                 udev_enumerate_unref(e);
261
262         if (udev)
263                 udev_unref(udev);
264
265         return r;
266 }
267
268 static int dm_list_get(MountPoint **dm_list_head) {
269         int r;
270         struct udev *udev;
271         struct udev_enumerate *e = NULL;
272         struct udev_list_entry *item = NULL, *first = NULL;
273
274         if (!(udev = udev_new())) {
275                 r = -ENOMEM;
276                 goto finish;
277         }
278
279         if (!(e = udev_enumerate_new(udev))) {
280                 r = -ENOMEM;
281                 goto finish;
282         }
283
284         if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
285             udev_enumerate_add_match_sysname(e, "dm-*") < 0) {
286                 r = -EIO;
287                 goto finish;
288         }
289
290         if (udev_enumerate_scan_devices(e) < 0) {
291                 r = -EIO;
292                 goto finish;
293         }
294
295         first = udev_enumerate_get_list_entry(e);
296
297         udev_list_entry_foreach(item, first) {
298                 MountPoint *lb;
299                 struct udev_device *d;
300                 char *dm = NULL;
301                 struct udev_list_entry *dlink = NULL, *first_dlink = NULL;
302
303                 if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
304                         r = -ENOMEM;
305                         goto finish;
306                 }
307
308                 first_dlink = udev_device_get_devlinks_list_entry(d);
309                 udev_list_entry_foreach(dlink, first_dlink) {
310
311                         if (startswith(udev_list_entry_get_name(dlink), "/dev/mapper/")) {
312
313                                 if (!(dm = strdup(udev_list_entry_get_name(dlink)))) {
314                                         udev_device_unref(d);
315                                         r = -ENOMEM;
316                                         goto finish;
317                                 }
318
319                                 break;
320                         }
321                 }
322
323                 udev_device_unref(d);
324
325                 if (!dm)
326                         continue;
327
328                 if (!(lb = mount_point_alloc(dm))) {
329                         free(dm);
330                         r = -ENOMEM;
331                         goto finish;
332                 }
333
334                 LIST_PREPEND(MountPoint, mount_point, *dm_list_head, lb);
335         }
336
337         r = 0;
338
339 finish:
340         if (e)
341                 udev_enumerate_unref(e);
342
343         if (udev)
344                 udev_unref(udev);
345
346         return r;
347 }
348
349 static int delete_loopback(const char *device) {
350         int fd, r;
351
352         if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0)
353                 return -errno;
354
355         r = ioctl(fd, LOOP_CLR_FD, 0);
356         close_nointr_nofail(fd);
357
358         /* ENXIO: not bound, so no error */
359         return (r >= 0 || errno == ENXIO) ? 0 : -errno;
360 }
361
362 static int delete_dm(const char *device) {
363         int fd, r;
364         struct dm_ioctl dm;
365
366         assert(device);
367
368         if (!startswith(device, "/dev/mapper/"))
369                 return -EIO;
370
371         if ((fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC)) < 0)
372                 return -errno;
373
374         zero(dm);
375         dm.version[0] = DM_VERSION_MAJOR;
376         dm.version[1] = DM_VERSION_MINOR;
377         dm.version[2] = DM_VERSION_PATCHLEVEL;
378
379         dm.data_size = sizeof(dm);
380
381         strncpy(dm.name, device + 12, sizeof(dm.name));
382
383         r = ioctl(fd, DM_DEV_REMOVE, &dm);
384         close_nointr_nofail(fd);
385
386         return r >= 0 ? 0 : -errno;
387 }
388
389 static int mount_points_list_umount(MountPoint **mount_point_list_head) {
390         MountPoint *mp, *mp_next;
391         int failed = 0;
392
393         LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mount_point_list_head) {
394                 if (streq(mp->path, "/"))
395                         continue;
396
397                 /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
398                 if (umount2(mp->path, MNT_FORCE) == 0)
399                         mount_point_remove_and_free(mp, mount_point_list_head);
400                 else {
401                         log_warning("Could not unmount %s: %m", mp->path);
402                         failed++;
403                 }
404         }
405
406         return failed;
407 }
408
409 static int mount_points_list_remount_read_only(MountPoint **mount_point_list_head) {
410         MountPoint *mp, *mp_next;
411         int failed = 0;
412
413         LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mount_point_list_head) {
414                 /* Trying to remount read-only */
415                 if (mount(NULL, mp->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0)
416                         mount_point_remove_and_free(mp, mount_point_list_head);
417                 else {
418                         log_warning("Could not remount as read-only %s: %m", mp->path);
419                         failed++;
420                 }
421         }
422
423         return failed;
424 }
425
426 static int swap_points_list_off(MountPoint **swap_list_head) {
427         MountPoint *swap, *swap_next;
428         int failed = 0;
429
430         LIST_FOREACH_SAFE(mount_point, swap, swap_next, *swap_list_head) {
431                 if (swapoff(swap->path) == 0)
432                         mount_point_remove_and_free(swap, swap_list_head);
433                 else {
434                         log_warning("Could not deactivate swap %s: %m", swap->path);
435                         failed++;
436                 }
437         }
438
439         return failed;
440 }
441
442 static int loopback_points_list_detach(MountPoint **loopback_list_head) {
443         MountPoint *loopback, *loopback_next;
444         int failed = 0;
445
446         LIST_FOREACH_SAFE(mount_point, loopback, loopback_next, *loopback_list_head) {
447                 if (delete_loopback(loopback->path) == 0)
448                         mount_point_remove_and_free(loopback, loopback_list_head);
449                 else {
450                         log_warning("Could not delete loopback %s: %m", loopback->path);
451                         failed++;
452                 }
453         }
454
455         return failed;
456 }
457
458 static int dm_points_list_detach(MountPoint **dm_list_head) {
459         MountPoint *dm, *dm_next;
460         int failed = 0;
461
462         LIST_FOREACH_SAFE(mount_point, dm, dm_next, *dm_list_head) {
463                 if (delete_dm(dm->path) == 0)
464                         mount_point_remove_and_free(dm, dm_list_head);
465                 else {
466                         log_warning("Could not delete dm %s: %m", dm->path);
467                         failed++;
468                 }
469         }
470
471         return failed;
472 }
473
474 int umount_all(void) {
475         int r;
476         LIST_HEAD(MountPoint, mp_list_head);
477
478         LIST_HEAD_INIT(MountPoint, mp_list_head);
479
480         r = mount_points_list_get(&mp_list_head);
481         if (r < 0)
482                 goto end;
483
484         r = mount_points_list_umount(&mp_list_head);
485         if (r <= 0)
486                 goto end;
487
488         r = mount_points_list_remount_read_only(&mp_list_head);
489
490   end:
491         mount_points_list_free(&mp_list_head);
492
493         return r;
494 }
495
496 int swapoff_all(void) {
497         int r;
498         LIST_HEAD(MountPoint, swap_list_head);
499
500         LIST_HEAD_INIT(MountPoint, swap_list_head);
501
502         r = swap_list_get(&swap_list_head);
503         if (r < 0)
504                 goto end;
505
506         r = swap_points_list_off(&swap_list_head);
507
508   end:
509         mount_points_list_free(&swap_list_head);
510
511         return r;
512 }
513
514 int loopback_detach_all(void) {
515         int r;
516         LIST_HEAD(MountPoint, loopback_list_head);
517
518         LIST_HEAD_INIT(MountPoint, loopback_list_head);
519
520         r = loopback_list_get(&loopback_list_head);
521         if (r < 0)
522                 goto end;
523
524         r = loopback_points_list_detach(&loopback_list_head);
525
526   end:
527         mount_points_list_free(&loopback_list_head);
528
529         return r;
530 }
531
532 int dm_detach_all(void) {
533         int r;
534         LIST_HEAD(MountPoint, dm_list_head);
535
536         LIST_HEAD_INIT(MountPoint, dm_list_head);
537
538         r = dm_list_get(&dm_list_head);
539         if (r < 0)
540                 goto end;
541
542         r = dm_points_list_detach(&dm_list_head);
543
544   end:
545         mount_points_list_free(&dm_list_head);
546
547         return r;
548 }