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