chiark / gitweb /
4842d403aeac8eea11b363c75eb185144f1d9c2c
[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 <libudev.h>
30
31 #include "list.h"
32 #include "mount-setup.h"
33 #include "umount.h"
34 #include "util.h"
35
36 typedef struct MountPoint {
37         char *path;
38         bool read_only;
39         LIST_FIELDS (struct MountPoint, mount_point);
40 } MountPoint;
41
42 static MountPoint *mount_point_alloc(char *path) {
43         MountPoint *mp;
44
45         if (!(mp = new(MountPoint, 1)))
46                 return NULL;
47
48         mp->path = path;
49         mp->read_only = false;
50
51         return mp;
52 }
53
54 static void mount_point_remove_and_free(MountPoint *mount_point, MountPoint **mount_point_list_head) {
55         LIST_REMOVE(MountPoint, mount_point, *mount_point_list_head, mount_point);
56
57         free(mount_point->path);
58         free(mount_point);
59 }
60
61 static void mount_points_list_free(MountPoint **mount_point_list_head) {
62         while (*mount_point_list_head)
63                 mount_point_remove_and_free(*mount_point_list_head, mount_point_list_head);
64 }
65
66 static int mount_points_list_get(MountPoint **mount_point_list_head) {
67         FILE *proc_self_mountinfo;
68         char *path, *p;
69         unsigned int i;
70         int r;
71
72         if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
73                 return -errno;
74
75         for (i = 1;; i++) {
76                 int k;
77                 MountPoint *mp;
78
79                 path = p = NULL;
80
81                 if ((k = fscanf(proc_self_mountinfo,
82                                 "%*s "       /* (1) mount id */
83                                 "%*s "       /* (2) parent id */
84                                 "%*s "       /* (3) major:minor */
85                                 "%*s "       /* (4) root */
86                                 "%ms "       /* (5) mount point */
87                                 "%*s"        /* (6) mount options */
88                                 "%*[^-]"     /* (7) optional fields */
89                                 "- "         /* (8) separator */
90                                 "%*s "       /* (9) file system type */
91                                 "%*s"        /* (10) mount source */
92                                 "%*s"        /* (11) mount options 2 */
93                                 "%*[^\n]",   /* some rubbish at the end */
94                                 &path)) != 1) {
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 (mount_point_is_api(path)) {
105                         free(path);
106                         continue;
107                 }
108
109                 if (!(p = cunescape(path))) {
110                         r = -ENOMEM;
111                         goto finish;
112                 }
113
114                 if (!(mp = mount_point_alloc(p))) {
115                         r = -ENOMEM;
116                         goto finish;
117                 }
118                 LIST_PREPEND(MountPoint, mount_point, *mount_point_list_head, mp);
119
120                 free(path);
121         }
122
123         r = 0;
124
125 finish:
126         fclose(proc_self_mountinfo);
127
128         free(path);
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                 r = -EIO;
214                 goto finish;
215         }
216
217         if (udev_enumerate_add_match_sysname(e, "loop*") < 0) {
218                 r = -EIO;
219                 goto finish;
220         }
221
222         if (udev_enumerate_scan_devices(e) < 0) {
223                 r = -EIO;
224                 goto finish;
225         }
226
227         first = udev_enumerate_get_list_entry(e);
228
229         udev_list_entry_foreach(item, first) {
230                 MountPoint *lb;
231                 char *loop;
232
233                 loop = cunescape(udev_list_entry_get_name(item));
234                 if (!loop) {
235                         r = -ENOMEM;
236                         goto finish;
237                 }
238
239                 lb = mount_point_alloc(loop);
240                 if (!lb) {
241                         free(loop);
242                         r = -ENOMEM;
243                         goto finish;
244                 }
245
246                 LIST_PREPEND(MountPoint, mount_point, *loopback_list_head, lb);
247         }
248
249         r = 0;
250
251 finish:
252         if (e)
253                 udev_enumerate_unref(e);
254
255         free(udev);
256         return r;
257 }
258
259 static int delete_loopback(const char *device) {
260         int fd, r;
261
262         if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0) {
263                 if (errno == ENOENT) {
264                         log_debug("Loop device %s does not exist.", device);
265                         errno = 0;
266                         return 0;
267                 }
268                 return -errno;
269         }
270
271         ioctl(fd, LOOP_CLR_FD, 0);
272         r = errno;
273         close_nointr(fd);
274
275         if (r == ENXIO) /* not bound, so no error */
276                 r = 0;
277         errno = r;
278         return -errno;
279 }
280
281 static int mount_points_list_umount(MountPoint **mount_point_list_head) {
282         MountPoint *mp, *mp_next;
283         int failed = 0;
284
285         LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mount_point_list_head) {
286                 if (streq(mp->path, "/"))
287                         continue;
288
289                 /* Trying to umount. Forcing to umount if busy (only for NFS mounts) */
290                 if (umount2(mp->path, MNT_FORCE) == 0)
291                         mount_point_remove_and_free(mp, mount_point_list_head);
292                 else {
293                         log_debug("Could not unmount %s: %m", mp->path);
294                         failed++;
295                 }
296         }
297
298         return failed;
299 }
300
301 static int mount_points_list_remount_read_only(MountPoint **mount_point_list_head) {
302         MountPoint *mp, *mp_next;
303         int failed = 0;
304
305         LIST_FOREACH_SAFE(mount_point, mp, mp_next, *mount_point_list_head) {
306                 if (mp->read_only)
307                         continue;
308
309                 /* Trying to remount read-only */
310                 if (mount(NULL, mp->path, NULL, MS_MGC_VAL|MS_REMOUNT|MS_RDONLY, NULL) == 0) {
311                         mp->read_only = true;
312                         mount_point_remove_and_free(mp, mount_point_list_head);
313                 } else {
314                         log_debug("Could not remount as read-only %s: %m", mp->path);
315                         failed++;
316                 }
317         }
318
319         return failed;
320 }
321
322 static int swap_points_list_off(MountPoint **swap_list_head) {
323         MountPoint *swap, *swap_next;
324         int failed = 0;
325
326         LIST_FOREACH_SAFE(mount_point, swap, swap_next, *swap_list_head) {
327                 if (swapoff(swap->path) == 0)
328                         mount_point_remove_and_free(swap, swap_list_head);
329                 else {
330                         log_debug("Could not swapoff %s: %m", swap->path);
331                         failed++;
332                 }
333         }
334
335         return failed;
336 }
337
338 static int loopback_points_list_detach(MountPoint **loopback_list_head) {
339         MountPoint *loopback, *loopback_next;
340         int failed = 0;
341
342         LIST_FOREACH_SAFE(mount_point, loopback, loopback_next, *loopback_list_head) {
343                 if (delete_loopback(loopback->path) == 0)
344                         mount_point_remove_and_free(loopback, loopback_list_head);
345                 else {
346                         log_debug("Could not delete loopback %s: %m", loopback->path);
347                         failed++;
348                 }
349         }
350
351         return failed;
352 }
353
354 int umount_all(void) {
355         int r;
356         LIST_HEAD(MountPoint, mp_list_head);
357
358         LIST_HEAD_INIT(MountPoint, mp_list_head);
359
360         r = mount_points_list_get(&mp_list_head);
361         if (r < 0)
362                 goto end;
363
364         r = mount_points_list_umount(&mp_list_head);
365         if (r <= 0)
366                 goto end;
367
368         r = mount_points_list_remount_read_only(&mp_list_head);
369
370   end:
371         mount_points_list_free(&mp_list_head);
372
373         return r;
374 }
375
376 int swapoff_all(void) {
377         int r;
378         LIST_HEAD(MountPoint, swap_list_head);
379
380         LIST_HEAD_INIT(MountPoint, swap_list_head);
381
382         r = swap_list_get(&swap_list_head);
383         if (r < 0)
384                 goto end;
385
386         r = swap_points_list_off(&swap_list_head);
387
388   end:
389         mount_points_list_free(&swap_list_head);
390
391         return r;
392 }
393
394 int loopback_detach_all(void) {
395         int r;
396         LIST_HEAD(MountPoint, loopback_list_head);
397
398         LIST_HEAD_INIT(MountPoint, loopback_list_head);
399
400         r = loopback_list_get(&loopback_list_head);
401         if (r < 0)
402                 goto end;
403
404         r = loopback_points_list_detach(&loopback_list_head);
405
406   end:
407         mount_points_list_free(&loopback_list_head);
408
409         return r;
410 }