umount.c 18 KB
Newer Older
1 2 3 4 5 6 7 8
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/

/***
  This file is part of systemd.

  Copyright 2010 ProFUSION embedded systems

  systemd is free software; you can redistribute it and/or modify it
9 10
  under the terms of the GNU Lesser General Public License as published by
  the Free Software Foundation; either version 2.1 of the License, or
11 12 13 14 15
  (at your option) any later version.

  systemd is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
  Lesser General Public License for more details.
17

18
  You should have received a copy of the GNU Lesser General Public License
19 20 21 22 23 24 25 26 27 28
  along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/

#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/swap.h>
#include <unistd.h>
#include <linux/loop.h>
29
#include <linux/dm-ioctl.h>
30 31 32 33 34
#include <libudev.h>

#include "list.h"
#include "mount-setup.h"
#include "umount.h"
Kay Sievers's avatar
Kay Sievers committed
35
#include "path-util.h"
36
#include "util.h"
37
#include "virt.h"
38 39 40

typedef struct MountPoint {
        char *path;
41
        dev_t devnum;
42 43 44
        LIST_FIELDS (struct MountPoint, mount_point);
} MountPoint;

45 46 47
static void mount_point_free(MountPoint **head, MountPoint *m) {
        assert(head);
        assert(m);
48

49 50 51 52
        LIST_REMOVE(MountPoint, mount_point, *head, m);

        free(m->path);
        free(m);
53 54
}

55 56 57 58 59
static void mount_points_list_free(MountPoint **head) {
        assert(head);

        while (*head)
                mount_point_free(head, *head);
60 61
}

62
static int mount_points_list_get(MountPoint **head) {
63 64 65 66 67
        FILE *proc_self_mountinfo;
        char *path, *p;
        unsigned int i;
        int r;

68 69
        assert(head);

70 71 72 73 74
        if (!(proc_self_mountinfo = fopen("/proc/self/mountinfo", "re")))
                return -errno;

        for (i = 1;; i++) {
                int k;
75
                MountPoint *m;
76 77 78 79 80 81 82

                path = p = NULL;

                if ((k = fscanf(proc_self_mountinfo,
                                "%*s "       /* (1) mount id */
                                "%*s "       /* (2) parent id */
                                "%*s "       /* (3) major:minor */
83
                                "%*s "       /* (4) root */
84 85 86 87 88 89 90 91
                                "%ms "       /* (5) mount point */
                                "%*s"        /* (6) mount options */
                                "%*[^-]"     /* (7) optional fields */
                                "- "         /* (8) separator */
                                "%*s "       /* (9) file system type */
                                "%*s"        /* (10) mount source */
                                "%*s"        /* (11) mount options 2 */
                                "%*[^\n]",   /* some rubbish at the end */
92
                                &path)) != 1) {
93 94 95 96 97 98 99 100 101
                        if (k == EOF)
                                break;

                        log_warning("Failed to parse /proc/self/mountinfo:%u.", i);

                        free(path);
                        continue;
                }

102 103
                p = cunescape(path);
                free(path);
104

105
                if (!p) {
106 107 108 109
                        r = -ENOMEM;
                        goto finish;
                }

110 111 112 113 114
                /* Ignore mount points we can't unmount because they
                 * are API or because we are keeping them open (like
                 * /dev/console) */
                if (mount_point_is_api(p) ||
                    mount_point_ignore(p) ||
Lennart Poettering's avatar
Lennart Poettering committed
115
                    path_equal(p, "/dev/console")) {
116 117 118 119
                        free(p);
                        continue;
                }

120
                if (!(m = new0(MountPoint, 1))) {
121
                        free(p);
122 123 124 125
                        r = -ENOMEM;
                        goto finish;
                }

126 127
                m->path = p;
                LIST_PREPEND(MountPoint, mount_point, *head, m);
128 129 130 131 132 133 134 135 136 137
        }

        r = 0;

finish:
        fclose(proc_self_mountinfo);

        return r;
}

138
static int swap_list_get(MountPoint **head) {
139 140 141 142
        FILE *proc_swaps;
        unsigned int i;
        int r;

143 144
        assert(head);

145
        if (!(proc_swaps = fopen("/proc/swaps", "re")))
146
                return (errno == ENOENT) ? 0 : -errno;
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184

        (void) fscanf(proc_swaps, "%*s %*s %*s %*s %*s\n");

        for (i = 2;; i++) {
                MountPoint *swap;
                char *dev = NULL, *d;
                int k;

                if ((k = fscanf(proc_swaps,
                                "%ms " /* device/file */
                                "%*s " /* type of swap */
                                "%*s " /* swap size */
                                "%*s " /* used */
                                "%*s\n", /* priority */
                                &dev)) != 1) {

                        if (k == EOF)
                                break;

                        log_warning("Failed to parse /proc/swaps:%u.", i);

                        free(dev);
                        continue;
                }

                if (endswith(dev, "(deleted)")) {
                        free(dev);
                        continue;
                }

                d = cunescape(dev);
                free(dev);

                if (!d) {
                        r = -ENOMEM;
                        goto finish;
                }

185
                if (!(swap = new0(MountPoint, 1))) {
186 187 188 189 190
                        free(d);
                        r = -ENOMEM;
                        goto finish;
                }

191
                swap->path = d;
192
                LIST_PREPEND(MountPoint, mount_point, *head, swap);
193 194 195 196 197 198 199 200 201 202
        }

        r = 0;

finish:
        fclose(proc_swaps);

        return r;
}

203
static int loopback_list_get(MountPoint **head) {
204 205 206 207 208
        int r;
        struct udev *udev;
        struct udev_enumerate *e = NULL;
        struct udev_list_entry *item = NULL, *first = NULL;

209 210
        assert(head);

211 212 213 214 215 216 217 218 219 220
        if (!(udev = udev_new())) {
                r = -ENOMEM;
                goto finish;
        }

        if (!(e = udev_enumerate_new(udev))) {
                r = -ENOMEM;
                goto finish;
        }

221
        if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
222 223
            udev_enumerate_add_match_sysname(e, "loop*") < 0 ||
            udev_enumerate_add_match_sysattr(e, "loop/backing_file", NULL) < 0) {
224 225 226 227 228 229 230 231 232 233 234 235
                r = -EIO;
                goto finish;
        }

        if (udev_enumerate_scan_devices(e) < 0) {
                r = -EIO;
                goto finish;
        }

        first = udev_enumerate_get_list_entry(e);
        udev_list_entry_foreach(item, first) {
                MountPoint *lb;
236
                struct udev_device *d;
237
                char *loop;
238
                const char *dn;
239

240
                if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
241 242 243 244
                        r = -ENOMEM;
                        goto finish;
                }

245
                if (!(dn = udev_device_get_devnode(d))) {
246
                        udev_device_unref(d);
247 248
                        continue;
                }
249

250 251 252 253 254 255 256
                loop = strdup(dn);
                udev_device_unref(d);

                if (!loop) {
                        r = -ENOMEM;
                        goto finish;
                }
257

258
                if (!(lb = new0(MountPoint, 1))) {
259 260 261 262 263
                        free(loop);
                        r = -ENOMEM;
                        goto finish;
                }

264
                lb->path = loop;
265
                LIST_PREPEND(MountPoint, mount_point, *head, lb);
266 267 268 269 270 271 272 273
        }

        r = 0;

finish:
        if (e)
                udev_enumerate_unref(e);

274 275 276
        if (udev)
                udev_unref(udev);

277 278 279
        return r;
}

280
static int dm_list_get(MountPoint **head) {
281 282 283 284 285
        int r;
        struct udev *udev;
        struct udev_enumerate *e = NULL;
        struct udev_list_entry *item = NULL, *first = NULL;

286 287
        assert(head);

288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
        if (!(udev = udev_new())) {
                r = -ENOMEM;
                goto finish;
        }

        if (!(e = udev_enumerate_new(udev))) {
                r = -ENOMEM;
                goto finish;
        }

        if (udev_enumerate_add_match_subsystem(e, "block") < 0 ||
            udev_enumerate_add_match_sysname(e, "dm-*") < 0) {
                r = -EIO;
                goto finish;
        }

        if (udev_enumerate_scan_devices(e) < 0) {
                r = -EIO;
                goto finish;
        }

        first = udev_enumerate_get_list_entry(e);

        udev_list_entry_foreach(item, first) {
312
                MountPoint *m;
313
                struct udev_device *d;
314 315 316
                dev_t devnum;
                char *node;
                const char *dn;
317 318 319 320 321 322

                if (!(d = udev_device_new_from_syspath(udev, udev_list_entry_get_name(item)))) {
                        r = -ENOMEM;
                        goto finish;
                }

323 324
                devnum = udev_device_get_devnum(d);
                dn = udev_device_get_devnode(d);
325

326 327 328
                if (major(devnum) == 0 || !dn) {
                        udev_device_unref(d);
                        continue;
329 330
                }

331
                node = strdup(dn);
332 333
                udev_device_unref(d);

334 335 336 337
                if (!node) {
                        r = -ENOMEM;
                        goto finish;
                }
338

339 340
                if (!(m = new(MountPoint, 1))) {
                        free(node);
341 342 343 344
                        r = -ENOMEM;
                        goto finish;
                }

345 346
                m->path = node;
                m->devnum = devnum;
347
                LIST_PREPEND(MountPoint, mount_point, *head, m);
348 349 350 351 352 353 354 355 356 357 358 359 360 361
        }

        r = 0;

finish:
        if (e)
                udev_enumerate_unref(e);

        if (udev)
                udev_unref(udev);

        return r;
}

362 363 364
static int delete_loopback(const char *device) {
        int fd, r;

365
        if ((fd = open(device, O_RDONLY|O_CLOEXEC)) < 0)
366
                return errno == ENOENT ? 0 : -errno;
367

368
        r = ioctl(fd, LOOP_CLR_FD, 0);
369
        close_nointr_nofail(fd);
370

371 372 373
        if (r >= 0)
                return 1;

374
        /* ENXIO: not bound, so no error */
375 376 377 378
        if (errno == ENXIO)
                return 0;

        return -errno;
379 380
}

381
static int delete_dm(dev_t devnum) {
Zbigniew Jędrzejewski-Szmek's avatar
Zbigniew Jędrzejewski-Szmek committed
382 383
        int _cleanup_close_ fd = -1;
        int r;
384 385 386 387 388 389 390
        struct dm_ioctl dm = {
                .version = {DM_VERSION_MAJOR,
                            DM_VERSION_MINOR,
                            DM_VERSION_PATCHLEVEL},
                .data_size = sizeof(dm),
                .dev = devnum,
        };
391

392
        assert(major(devnum) != 0);
393

Zbigniew Jędrzejewski-Szmek's avatar
Zbigniew Jędrzejewski-Szmek committed
394 395
        fd = open("/dev/mapper/control", O_RDWR|O_CLOEXEC);
        if (fd < 0)
396 397 398 399 400 401
                return -errno;

        r = ioctl(fd, DM_DEV_REMOVE, &dm);
        return r >= 0 ? 0 : -errno;
}

402
static int mount_points_list_umount(MountPoint **head, bool *changed, bool log_error) {
403 404
        MountPoint *m, *n;
        int n_failed = 0;
405

406 407 408
        assert(head);

        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
409 410 411 412 413 414 415 416 417 418 419 420

                /* If we are in a container, don't attempt to
                   read-only mount anything as that brings no real
                   benefits, but might confuse the host, as we remount
                   the superblock here, not the bind mound. */
                if (detect_container(NULL) <= 0)  {
                        /* We always try to remount directories
                         * read-only first, before we go on and umount
                         * them.
                         *
                         * Mount points can be stacked. If a mount
                         * point is stacked below / or /usr, we
421
                         * cannot umount or remount it directly,
422 423 424 425 426 427 428 429 430 431 432 433 434
                         * since there is no way to refer to the
                         * underlying mount. There's nothing we can do
                         * about it for the general case, but we can
                         * do something about it if it is aliased
                         * somehwere else via a bind mount. If we
                         * explicitly remount the super block of that
                         * alias read-only we hence should be
                         * relatively safe regarding keeping the fs we
                         * can otherwise not see dirty. */
                        mount(NULL, m->path, NULL, MS_REMOUNT|MS_RDONLY, NULL);
                }

                /* Skip / and /usr since we cannot unmount that
Hermann Gausterer's avatar
Hermann Gausterer committed
435 436
                 * anyway, since we are running from it. They have
                 * already been remounted ro. */
437 438 439 440
                if (path_equal(m->path, "/")
#ifndef HAVE_SPLIT_USR
                    || path_equal(m->path, "/usr")
#endif
441
                )
442 443
                        continue;

444 445 446
                /* Trying to umount. We don't force here since we rely
                 * on busy NFS and FUSE file systems to return EBUSY
                 * until we closed everything on top of them. */
447
                log_info("Unmounting %s.", m->path);
448
                if (umount2(m->path, 0) == 0) {
449 450 451 452
                        if (changed)
                                *changed = true;

                        mount_point_free(head, m);
453
                } else if (log_error) {
454 455
                        log_warning("Could not unmount %s: %m", m->path);
                        n_failed++;
456 457 458
                }
        }

459
        return n_failed;
460 461
}

462 463 464 465 466
static int swap_points_list_off(MountPoint **head, bool *changed) {
        MountPoint *m, *n;
        int n_failed = 0;

        assert(head);
467

468
        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
469
                log_info("Deactivating swap %s.", m->path);
470 471 472 473 474 475 476 477
                if (swapoff(m->path) == 0) {
                        if (changed)
                                *changed = true;

                        mount_point_free(head, m);
                } else {
                        log_warning("Could not deactivate swap %s: %m", m->path);
                        n_failed++;
478 479 480
                }
        }

481
        return n_failed;
482 483
}

484 485
static int loopback_points_list_detach(MountPoint **head, bool *changed) {
        MountPoint *m, *n;
486 487
        int n_failed = 0, k;
        struct stat root_st;
488 489 490

        assert(head);

491 492
        k = lstat("/", &root_st);

493 494
        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
                int r;
495 496 497 498 499 500 501 502 503
                struct stat loopback_st;

                if (k >= 0 &&
                    major(root_st.st_dev) != 0 &&
                    lstat(m->path, &loopback_st) >= 0 &&
                    root_st.st_dev == loopback_st.st_rdev) {
                        n_failed ++;
                        continue;
                }
504

505
                log_info("Detaching loopback %s.", m->path);
506 507
                r = delete_loopback(m->path);
                if (r >= 0) {
508 509 510 511 512
                        if (r > 0 && changed)
                                *changed = true;

                        mount_point_free(head, m);
                } else {
513
                        log_warning("Could not detach loopback %s: %m", m->path);
514
                        n_failed++;
515 516 517
                }
        }

518
        return n_failed;
519 520
}

521 522
static int dm_points_list_detach(MountPoint **head, bool *changed) {
        MountPoint *m, *n;
523 524
        int n_failed = 0, k;
        struct stat root_st;
525 526 527

        assert(head);

528 529
        k = lstat("/", &root_st);

530 531 532
        LIST_FOREACH_SAFE(mount_point, m, n, *head) {
                int r;

533 534 535 536 537 538 539
                if (k >= 0 &&
                    major(root_st.st_dev) != 0 &&
                    root_st.st_dev == m->devnum) {
                        n_failed ++;
                        continue;
                }

540
                log_info("Detaching DM %u:%u.", major(m->devnum), minor(m->devnum));
541 542
                r = delete_dm(m->devnum);
                if (r >= 0) {
543
                        if (changed)
544
                                *changed = true;
545

546 547
                        mount_point_free(head, m);
                } else {
548
                        log_warning("Could not detach DM %s: %m", m->path);
549
                        n_failed++;
550 551 552
                }
        }

553
        return n_failed;
554 555
}

556
int umount_all(bool *changed) {
557
        int r;
558
        bool umount_changed;
559 560 561 562 563 564 565
        LIST_HEAD(MountPoint, mp_list_head);

        LIST_HEAD_INIT(MountPoint, mp_list_head);
        r = mount_points_list_get(&mp_list_head);
        if (r < 0)
                goto end;

566 567 568
        /* retry umount, until nothing can be umounted anymore */
        do {
                umount_changed = false;
569 570

                mount_points_list_umount(&mp_list_head, &umount_changed, false);
571 572
                if (umount_changed)
                        *changed = true;
573 574 575

        } while (umount_changed);

576 577
        /* umount one more time with logging enabled */
        r = mount_points_list_umount(&mp_list_head, &umount_changed, true);
578 579 580 581 582 583 584 585 586
        if (r <= 0)
                goto end;

  end:
        mount_points_list_free(&mp_list_head);

        return r;
}

587
int swapoff_all(bool *changed) {
588 589 590 591 592 593 594 595 596
        int r;
        LIST_HEAD(MountPoint, swap_list_head);

        LIST_HEAD_INIT(MountPoint, swap_list_head);

        r = swap_list_get(&swap_list_head);
        if (r < 0)
                goto end;

597
        r = swap_points_list_off(&swap_list_head, changed);
598 599 600 601 602 603 604

  end:
        mount_points_list_free(&swap_list_head);

        return r;
}

605
int loopback_detach_all(bool *changed) {
606 607 608 609 610 611 612 613 614
        int r;
        LIST_HEAD(MountPoint, loopback_list_head);

        LIST_HEAD_INIT(MountPoint, loopback_list_head);

        r = loopback_list_get(&loopback_list_head);
        if (r < 0)
                goto end;

615
        r = loopback_points_list_detach(&loopback_list_head, changed);
616 617 618 619 620 621

  end:
        mount_points_list_free(&loopback_list_head);

        return r;
}
622

623
int dm_detach_all(bool *changed) {
624 625 626 627 628 629 630 631 632
        int r;
        LIST_HEAD(MountPoint, dm_list_head);

        LIST_HEAD_INIT(MountPoint, dm_list_head);

        r = dm_list_get(&dm_list_head);
        if (r < 0)
                goto end;

633
        r = dm_points_list_detach(&dm_list_head, changed);
634 635 636 637 638 639

  end:
        mount_points_list_free(&dm_list_head);

        return r;
}