Beispiel #1
0
static int probe_and_add_mount(
                const char *id,
                const char *what,
                const char *where,
                bool rw,
                const char *description,
                const char *post) {

        _cleanup_blkid_free_probe_ blkid_probe b = NULL;
        const char *fstype;
        int r;

        assert(id);
        assert(what);
        assert(where);
        assert(description);

        if (path_is_mount_point(where, true) <= 0 &&
            dir_is_empty(where) <= 0) {
                log_debug("%s already populated, ignoring.", where);
                return 0;
        }

        /* Let's check the partition type here, so that we know
         * whether to do LUKS magic. */

        errno = 0;
        b = blkid_new_probe_from_filename(what);
        if (!b) {
                if (errno == 0)
                        return log_oom();
                log_error_errno(errno, "Failed to allocate prober: %m");
                return -errno;
        }

        blkid_probe_enable_superblocks(b, 1);
        blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);

        errno = 0;
        r = blkid_do_safeprobe(b);
        if (r == -2 || r == 1) /* no result or uncertain */
                return 0;
        else if (r != 0) {
                if (errno == 0)
                        errno = EIO;
                log_error_errno(errno, "Failed to probe %s: %m", what);
                return -errno;
        }

        blkid_probe_lookup_value(b, "TYPE", &fstype, NULL);

        return add_mount(
                        id,
                        what,
                        where,
                        fstype,
                        rw,
                        description,
                        post);
}
Beispiel #2
0
static int condition_test_path_is_mount_point(Condition *c) {
        assert(c);
        assert(c->parameter);
        assert(c->type == CONDITION_PATH_IS_MOUNT_POINT);

        return path_is_mount_point(c->parameter, true) > 0;
}
Beispiel #3
0
static int automount_start(Unit *u) {
        Automount *a = AUTOMOUNT(u);
        Unit *trigger;
        int r;

        assert(a);
        assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED);

        if (path_is_mount_point(a->where, NULL, 0) > 0) {
                log_unit_error(u, "Path %s is already a mount point, refusing start.", a->where);
                return -EEXIST;
        }

        trigger = UNIT_TRIGGER(u);
        if (!trigger || trigger->load_state != UNIT_LOADED) {
                log_unit_error(u, "Refusing to start, unit to trigger not loaded.");
                return -ENOENT;
        }

        r = unit_start_limit_test(u);
        if (r < 0) {
                automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT);
                return r;
        }

        r = unit_acquire_invocation_id(u);
        if (r < 0)
                return r;

        a->result = AUTOMOUNT_SUCCESS;
        automount_enter_waiting(a);
        return 1;
}
Beispiel #4
0
static int mount_unified_cgroups(const char *dest) {
        const char *p;
        int r;

        assert(dest);

        p = prefix_roota(dest, "/sys/fs/cgroup");

        (void) mkdir_p(p, 0755);

        r = path_is_mount_point(p, dest, AT_SYMLINK_FOLLOW);
        if (r < 0)
                return log_error_errno(r, "Failed to determine if %s is mounted already: %m", p);
        if (r > 0) {
                p = prefix_roota(dest, "/sys/fs/cgroup/cgroup.procs");
                if (access(p, F_OK) >= 0)
                        return 0;
                if (errno != ENOENT)
                        return log_error_errno(errno, "Failed to determine if mount point %s contains the unified cgroup hierarchy: %m", p);

                log_error("%s is already mounted but not a unified cgroup hierarchy. Refusing.", p);
                return -EINVAL;
        }

        return mount_verbose(LOG_ERR, "cgroup", p, "cgroup2", MS_NOSUID|MS_NOEXEC|MS_NODEV, NULL);
}
Beispiel #5
0
static int mount_one(const MountPoint *p, bool relabel) {
        int r;

        assert(p);

        if (p->condition_fn && !p->condition_fn())
                return 0;

        /* Relabel first, just in case */
        if (relabel)
                (void) label_fix(p->where, true, true);

        r = path_is_mount_point(p->where, NULL, AT_SYMLINK_FOLLOW);
        if (r < 0 && r != -ENOENT) {
                log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, r, "Failed to determine whether %s is a mount point: %m", p->where);
                return (p->mode & MNT_FATAL) ? r : 0;
        }
        if (r > 0)
                return 0;

        /* Skip securityfs in a container */
        if (!(p->mode & MNT_IN_CONTAINER) && detect_container() > 0)
                return 0;

        /* The access mode here doesn't really matter too much, since
         * the mounted file system will take precedence anyway. */
        if (relabel)
                (void) mkdir_p_label(p->where, 0755);
        else
                (void) mkdir_p(p->where, 0755);

        log_debug("Mounting %s to %s of type %s with options %s.",
                  p->what,
                  p->where,
                  p->type,
                  strna(p->options));

        if (mount(p->what,
                  p->where,
                  p->type,
                  p->flags,
                  p->options) < 0) {
                log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, errno, "Failed to mount %s at %s: %m", p->type, p->where);
                return (p->mode & MNT_FATAL) ? -errno : 0;
        }

        /* Relabel again, since we now mounted something fresh here */
        if (relabel)
                (void) label_fix(p->where, false, false);

        return 1;
}
int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
        const char *p;
        char *t;
        static __thread bool good = false;

        assert(controller);
        assert(fs);

        if (_unlikely_(!good)) {
                int r;

                r = path_is_mount_point("/sys/fs/cgroup", false);
                if (r <= 0)
                        return r < 0 ? r : -ENOENT;

                /* Cache this to save a few stat()s */
                good = true;
        }

        if (isempty(controller))
                return -EINVAL;

        /* This is a very minimal lookup from controller names to
         * paths. Since we have mounted most hierarchies ourselves
         * should be kinda safe, but eventually we might want to
         * extend this to have a fallback to actually check
         * /proc/mounts. Might need caching then. */

        if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
                p = "systemd";
        else if (startswith(controller, "name="))
                p = controller + 5;
        else
                p = controller;

        if (path && suffix)
                t = join("/sys/fs/cgroup/", p, "/", path, "/", suffix, NULL);
        else if (path)
                t = join("/sys/fs/cgroup/", p, "/", path, NULL);
        else if (suffix)
                t = join("/sys/fs/cgroup/", p, "/", suffix, NULL);
        else
                t = join("/sys/fs/cgroup/", p, NULL);

        if (!t)
                return -ENOMEM;

        path_kill_slashes(t);

        *fs = t;
        return 0;
}
Beispiel #7
0
int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
        const char *p;
        char *mp;
        int r;
        static __thread bool good = false;

        assert(controller);
        assert(fs);

        /* This is a very minimal lookup from controller names to
         * paths. Since we have mounted most hierarchies ourselves
         * should be kinda safe, but eventually we might want to
         * extend this to have a fallback to actually check
         * /proc/mounts. Might need caching then. */

        if (streq(controller, SYSTEMD_CGROUP_CONTROLLER))
                p = "systemd";
        else if (startswith(controller, "name="))
                p = controller + 5;
        else
                p = controller;

        if (asprintf(&mp, "/sys/fs/cgroup/%s", p) < 0)
                return -ENOMEM;

        if (!good) {
                if ((r = path_is_mount_point(mp)) <= 0) {
                        free(mp);
                        return r < 0 ? r : -ENOENT;
                }

                /* Cache this to save a few stat()s */
                good = true;
        }

        if (path && suffix)
                r = asprintf(fs, "%s/%s/%s", mp, path, suffix);
        else if (path)
                r = asprintf(fs, "%s/%s", mp, path);
        else if (suffix)
                r = asprintf(fs, "%s/%s", mp, suffix);
        else {
                path_kill_slashes(mp);
                *fs = mp;
                return 0;
        }

        free(mp);
        path_kill_slashes(*fs);
        return r < 0 ? -ENOMEM : 0;
}
Beispiel #8
0
static int mount_legacy_cgroup_hierarchy(
                const char *dest,
                const char *controller,
                const char *hierarchy,
                bool read_only) {

        const char *to, *fstype, *opts;
        int r;

        to = strjoina(strempty(dest), "/sys/fs/cgroup/", hierarchy);

        r = path_is_mount_point(to, dest, 0);
        if (r < 0 && r != -ENOENT)
                return log_error_errno(r, "Failed to determine if %s is mounted already: %m", to);
        if (r > 0)
                return 0;

        mkdir_p(to, 0755);

        /* The superblock mount options of the mount point need to be
         * identical to the hosts', and hence writable... */
        if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_HYBRID)) {
                fstype = "cgroup2";
                opts = NULL;
        } else if (streq(controller, SYSTEMD_CGROUP_CONTROLLER_LEGACY)) {
                fstype = "cgroup";
                opts = "none,name=systemd,xattr";
        } else {
                fstype = "cgroup";
                opts = controller;
        }

        r = mount_verbose(LOG_ERR, "cgroup", to, fstype, MS_NOSUID|MS_NOEXEC|MS_NODEV, opts);
        if (r < 0)
                return r;

        /* ... hence let's only make the bind mount read-only, not the superblock. */
        if (read_only) {
                r = mount_verbose(LOG_ERR, NULL, to, NULL,
                                  MS_BIND|MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_RDONLY, NULL);
                if (r < 0)
                        return r;
        }

        return 1;
}
Beispiel #9
0
static int automount_start(Unit *u) {
        Automount *a = AUTOMOUNT(u);

        assert(a);
        assert(a->state == AUTOMOUNT_DEAD || a->state == AUTOMOUNT_FAILED);

        if (path_is_mount_point(a->where, 0) > 0) {
                log_unit_error(u, "Path %s is already a mount point, refusing start.", a->where);
                return -EEXIST;
        }

        if (UNIT_TRIGGER(u)->load_state != UNIT_LOADED)
                return -ENOENT;

        a->result = AUTOMOUNT_SUCCESS;
        automount_enter_waiting(a);
        return 1;
}
Beispiel #10
0
int cg_get_path(const char *controller, const char *path, const char *suffix, char **fs) {
        const char *p;
        static __thread bool good = false;

        assert(fs);

        if (_unlikely_(!good)) {
                int r;

                r = path_is_mount_point("/sys/fs/cgroup", false);
                if (r <= 0)
                        return r < 0 ? r : -ENOENT;

                /* Cache this to save a few stat()s */
                good = true;
        }

        p = controller ? normalize_controller(controller) : NULL;
        return join_path(p, path, suffix, fs);
}
Beispiel #11
0
static bool path_is_busy(const char *where) {
        int r;

        /* already a mountpoint; generators run during reload */
        r = path_is_mount_point(where, AT_SYMLINK_FOLLOW);
        if (r > 0)
                return false;

        /* the directory might not exist on a stateless system */
        if (r == -ENOENT)
                return false;

        if (r < 0)
                return true;

        /* not a mountpoint but it contains files */
        if (dir_is_empty(where) <= 0)
                return true;

        return false;
}
Beispiel #12
0
static bool condition_test(Condition *c) {
        assert(c);

        switch(c->type) {

        case CONDITION_PATH_EXISTS:
                return (access(c->parameter, F_OK) >= 0) == !c->negate;

        case CONDITION_PATH_EXISTS_GLOB:
                return (glob_exists(c->parameter) > 0) == !c->negate;

        case CONDITION_PATH_IS_DIRECTORY: {
                struct stat st;

                if (stat(c->parameter, &st) < 0)
                        return c->negate;
                return S_ISDIR(st.st_mode) == !c->negate;
        }

        case CONDITION_PATH_IS_SYMBOLIC_LINK: {
                struct stat st;

                if (lstat(c->parameter, &st) < 0)
                        return c->negate;
                return S_ISLNK(st.st_mode) == !c->negate;
        }

        case CONDITION_PATH_IS_MOUNT_POINT:
                return (path_is_mount_point(c->parameter, true) > 0) == !c->negate;

        case CONDITION_PATH_IS_READ_WRITE:
                return (path_is_read_only_fs(c->parameter) > 0) == c->negate;

        case CONDITION_DIRECTORY_NOT_EMPTY: {
                int k;

                k = dir_is_empty(c->parameter);
                return !(k == -ENOENT || k > 0) == !c->negate;
        }

        case CONDITION_FILE_NOT_EMPTY: {
                struct stat st;

                if (stat(c->parameter, &st) < 0)
                        return c->negate;

                return (S_ISREG(st.st_mode) && st.st_size > 0) == !c->negate;
        }

        case CONDITION_FILE_IS_EXECUTABLE: {
                struct stat st;

                if (stat(c->parameter, &st) < 0)
                        return c->negate;

                return (S_ISREG(st.st_mode) && (st.st_mode & 0111)) == !c->negate;
        }

        case CONDITION_KERNEL_COMMAND_LINE:
                return condition_test_kernel_command_line(c);

        case CONDITION_VIRTUALIZATION:
                return condition_test_virtualization(c);

        case CONDITION_SECURITY:
                return condition_test_security(c);

        case CONDITION_CAPABILITY:
                return condition_test_capability(c);

        case CONDITION_HOST:
                return condition_test_host(c);

        case CONDITION_AC_POWER:
                return condition_test_ac_power(c);

        case CONDITION_ARCHITECTURE:
                return condition_test_architecture(c);

        case CONDITION_NEEDS_UPDATE:
                return condition_test_needs_update(c);

        case CONDITION_FIRST_BOOT:
                return condition_test_first_boot(c);

        case CONDITION_NULL:
                return !c->negate;

        default:
                assert_not_reached("Invalid condition type.");
        }
}
Beispiel #13
0
/* Use this function only if you do not have direct access to /proc/self/mountinfo but the caller can open it
 * for you. This is the case when /proc is masked or not mounted. Otherwise, use bind_remount_recursive. */
int bind_remount_recursive_with_mountinfo(
                const char *prefix,
                unsigned long new_flags,
                unsigned long flags_mask,
                char **blacklist,
                FILE *proc_self_mountinfo) {

        _cleanup_set_free_free_ Set *done = NULL;
        _cleanup_free_ char *cleaned = NULL;
        int r;

        assert(proc_self_mountinfo);

        /* Recursively remount a directory (and all its submounts) read-only or read-write. If the directory is already
         * mounted, we reuse the mount and simply mark it MS_BIND|MS_RDONLY (or remove the MS_RDONLY for read-write
         * operation). If it isn't we first make it one. Afterwards we apply MS_BIND|MS_RDONLY (or remove MS_RDONLY) to
         * all submounts we can access, too. When mounts are stacked on the same mount point we only care for each
         * individual "top-level" mount on each point, as we cannot influence/access the underlying mounts anyway. We
         * do not have any effect on future submounts that might get propagated, they migt be writable. This includes
         * future submounts that have been triggered via autofs.
         *
         * If the "blacklist" parameter is specified it may contain a list of subtrees to exclude from the
         * remount operation. Note that we'll ignore the blacklist for the top-level path. */

        cleaned = strdup(prefix);
        if (!cleaned)
                return -ENOMEM;

        path_simplify(cleaned, false);

        done = set_new(&path_hash_ops);
        if (!done)
                return -ENOMEM;

        for (;;) {
                _cleanup_set_free_free_ Set *todo = NULL;
                bool top_autofs = false;
                char *x;
                unsigned long orig_flags;

                todo = set_new(&path_hash_ops);
                if (!todo)
                        return -ENOMEM;

                rewind(proc_self_mountinfo);

                for (;;) {
                        _cleanup_free_ char *path = NULL, *p = NULL, *type = NULL;
                        int k;

                        k = fscanf(proc_self_mountinfo,
                                   "%*s "       /* (1) mount id */
                                   "%*s "       /* (2) parent id */
                                   "%*s "       /* (3) major:minor */
                                   "%*s "       /* (4) root */
                                   "%ms "       /* (5) mount point */
                                   "%*s"        /* (6) mount options (superblock) */
                                   "%*[^-]"     /* (7) optional fields */
                                   "- "         /* (8) separator */
                                   "%ms "       /* (9) file system type */
                                   "%*s"        /* (10) mount source */
                                   "%*s"        /* (11) mount options (bind mount) */
                                   "%*[^\n]",   /* some rubbish at the end */
                                   &path,
                                   &type);
                        if (k != 2) {
                                if (k == EOF)
                                        break;

                                continue;
                        }

                        r = cunescape(path, UNESCAPE_RELAX, &p);
                        if (r < 0)
                                return r;

                        if (!path_startswith(p, cleaned))
                                continue;

                        /* Ignore this mount if it is blacklisted, but only if it isn't the top-level mount we shall
                         * operate on. */
                        if (!path_equal(cleaned, p)) {
                                bool blacklisted = false;
                                char **i;

                                STRV_FOREACH(i, blacklist) {

                                        if (path_equal(*i, cleaned))
                                                continue;

                                        if (!path_startswith(*i, cleaned))
                                                continue;

                                        if (path_startswith(p, *i)) {
                                                blacklisted = true;
                                                log_debug("Not remounting %s blacklisted by %s, called for %s", p, *i, cleaned);
                                                break;
                                        }
                                }
                                if (blacklisted)
                                        continue;
                        }

                        /* Let's ignore autofs mounts.  If they aren't
                         * triggered yet, we want to avoid triggering
                         * them, as we don't make any guarantees for
                         * future submounts anyway.  If they are
                         * already triggered, then we will find
                         * another entry for this. */
                        if (streq(type, "autofs")) {
                                top_autofs = top_autofs || path_equal(cleaned, p);
                                continue;
                        }

                        if (!set_contains(done, p)) {
                                r = set_consume(todo, p);
                                p = NULL;
                                if (r == -EEXIST)
                                        continue;
                                if (r < 0)
                                        return r;
                        }
                }

                /* If we have no submounts to process anymore and if
                 * the root is either already done, or an autofs, we
                 * are done */
                if (set_isempty(todo) &&
                    (top_autofs || set_contains(done, cleaned)))
                        return 0;

                if (!set_contains(done, cleaned) &&
                    !set_contains(todo, cleaned)) {
                        /* The prefix directory itself is not yet a mount, make it one. */
                        if (mount(cleaned, cleaned, NULL, MS_BIND|MS_REC, NULL) < 0)
                                return -errno;

                        orig_flags = 0;
                        (void) get_mount_flags(cleaned, &orig_flags);
                        orig_flags &= ~MS_RDONLY;

                        if (mount(NULL, cleaned, NULL, (orig_flags & ~flags_mask)|MS_BIND|MS_REMOUNT|new_flags, NULL) < 0)
                                return -errno;

                        log_debug("Made top-level directory %s a mount point.", prefix);

                        r = set_put_strdup(done, cleaned);
                        if (r < 0)
                                return r;
                }

                while ((x = set_steal_first(todo))) {

                        r = set_consume(done, x);
                        if (IN_SET(r, 0, -EEXIST))
                                continue;
                        if (r < 0)
                                return r;

                        /* Deal with mount points that are obstructed by a later mount */
                        r = path_is_mount_point(x, NULL, 0);
                        if (IN_SET(r, 0, -ENOENT))
                                continue;
                        if (IN_SET(r, -EACCES, -EPERM)) {
                                /* Even if root user invoke this, submounts under private FUSE or NFS mount points
                                 * may not be acceessed. E.g.,
                                 *
                                 * $ bindfs --no-allow-other ~/mnt/mnt ~/mnt/mnt
                                 * $ bindfs --no-allow-other ~/mnt ~/mnt
                                 *
                                 * Then, root user cannot access the mount point ~/mnt/mnt.
                                 * In such cases, the submounts are ignored, as we have no way to manage them. */
                                log_debug_errno(r, "Failed to determine '%s' is mount point or not, ignoring: %m", x);
                                continue;
                        }
                        if (r < 0)
                                return r;

                        /* Try to reuse the original flag set */
                        orig_flags = 0;
                        (void) get_mount_flags(x, &orig_flags);
                        orig_flags &= ~MS_RDONLY;

                        if (mount(NULL, x, NULL, (orig_flags & ~flags_mask)|MS_BIND|MS_REMOUNT|new_flags, NULL) < 0)
                                return -errno;

                        log_debug("Remounted %s read-only.", x);
                }
        }
Beispiel #14
0
static int run(int argc, char *argv[]) {
        VolatileMode m = _VOLATILE_MODE_INVALID;
        const char *path;
        dev_t devt;
        int r;

        log_setup_service();

        if (argc > 3)
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                       "Too many arguments. Expected directory and mode.");

        r = query_volatile_mode(&m);
        if (r < 0)
                return log_error_errno(r, "Failed to determine volatile mode from kernel command line.");
        if (r == 0 && argc >= 2) {
                /* The kernel command line always wins. However if nothing was set there, the argument passed here wins instead. */
                m = volatile_mode_from_string(argv[1]);
                if (m < 0)
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Couldn't parse volatile mode: %s", argv[1]);
        }

        if (argc < 3)
                path = "/sysroot";
        else {
                path = argv[2];

                if (isempty(path))
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                               "Directory name cannot be empty.");
                if (!path_is_absolute(path))
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                               "Directory must be specified as absolute path.");
                if (path_equal(path, "/"))
                        return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
                                               "Directory cannot be the root directory.");
        }

        if (!IN_SET(m, VOLATILE_YES, VOLATILE_OVERLAY))
                return 0;

        r = path_is_mount_point(path, NULL, AT_SYMLINK_FOLLOW);
        if (r < 0)
                return log_error_errno(r, "Couldn't determine whether %s is a mount point: %m", path);
        if (r == 0)
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "%s is not a mount point.", path);

        r = path_is_temporary_fs(path);
        if (r < 0)
                return log_error_errno(r, "Couldn't determine whether %s is a temporary file system: %m", path);
        if (r > 0) {
                log_info("%s already is a temporary file system.", path);
                return 0;
        }

        /* We are about to replace the root directory with something else. Later code might want to know what we
         * replaced here, hence let's save that information as a symlink we can later use. (This is particularly
         * relevant for the overlayfs case where we'll fully obstruct the view onto the underlying device, hence
         * querying the backing device node from the file system directly is no longer possible. */
        r = get_block_device_harder(path, &devt);
        if (r < 0)
                return log_error_errno(r, "Failed to determine device major/minor of %s: %m", path);
        else if (r > 0) {
                _cleanup_free_ char *dn = NULL;

                r = device_path_make_major_minor(S_IFBLK, devt, &dn);
                if (r < 0)
                        return log_error_errno(r, "Failed to format device node path: %m");

                if (symlink(dn, "/run/systemd/volatile-root") < 0)
                        log_warning_errno(errno, "Failed to create symlink /run/systemd/volatile-root: %m");
        }

        if (m == VOLATILE_YES)
                return make_volatile(path);
        else {
                assert(m == VOLATILE_OVERLAY);
                return make_overlay(path);
        }
}
Beispiel #15
0
int setup_machine_directory(uint64_t size, sd_bus_error *error) {
        _cleanup_release_lock_file_ LockFile lock_file = LOCK_FILE_INIT;
        struct loop_info64 info = {
                .lo_flags = LO_FLAGS_AUTOCLEAR,
        };
        _cleanup_close_ int fd = -1, control = -1, loop = -1;
        _cleanup_free_ char* loopdev = NULL;
        char tmpdir[] = "/tmp/machine-pool.XXXXXX", *mntdir = NULL;
        bool tmpdir_made = false, mntdir_made = false, mntdir_mounted = false;
        char buf[FORMAT_BYTES_MAX];
        int r, nr = -1;

        /* btrfs cannot handle file systems < 16M, hence use this as minimum */
        if (size == (uint64_t) -1)
                size = VAR_LIB_MACHINES_SIZE_START;
        else if (size < 16*1024*1024)
                size = 16*1024*1024;

        /* Make sure we only set the directory up once at a time */
        r = make_lock_file("/run/systemd/machines.lock", LOCK_EX, &lock_file);
        if (r < 0)
                return r;

        r = check_btrfs();
        if (r < 0)
                return sd_bus_error_set_errnof(error, r, "Failed to determine whether /var/lib/machines is located on btrfs: %m");
        if (r > 0) {
                (void) btrfs_subvol_make_label("/var/lib/machines");

                r = btrfs_quota_enable("/var/lib/machines", true);
                if (r < 0)
                        log_warning_errno(r, "Failed to enable quota for /var/lib/machines, ignoring: %m");

                r = btrfs_subvol_auto_qgroup("/var/lib/machines", 0, true);
                if (r < 0)
                        log_warning_errno(r, "Failed to set up default quota hierarchy for /var/lib/machines, ignoring: %m");

                return 1;
        }

        if (path_is_mount_point("/var/lib/machines", AT_SYMLINK_FOLLOW) > 0) {
                log_debug("/var/lib/machines is already a mount point, not creating loopback file for it.");
                return 0;
        }

        r = dir_is_populated("/var/lib/machines");
        if (r < 0 && r != -ENOENT)
                return r;
        if (r > 0) {
                log_debug("/var/log/machines is already populated, not creating loopback file for it.");
                return 0;
        }

        r = mkfs_exists("btrfs");
        if (r == -ENOENT) {
                log_debug("mkfs.btrfs is missing, cannot create loopback file for /var/lib/machines.");
                return 0;
        }
        if (r < 0)
                return r;

        fd = setup_machine_raw(size, error);
        if (fd < 0)
                return fd;

        control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
        if (control < 0)
                return sd_bus_error_set_errnof(error, errno, "Failed to open /dev/loop-control: %m");

        nr = ioctl(control, LOOP_CTL_GET_FREE);
        if (nr < 0)
                return sd_bus_error_set_errnof(error, errno, "Failed to allocate loop device: %m");

        if (asprintf(&loopdev, "/dev/loop%i", nr) < 0) {
                r = -ENOMEM;
                goto fail;
        }

        loop = open(loopdev, O_CLOEXEC|O_RDWR|O_NOCTTY|O_NONBLOCK);
        if (loop < 0) {
                r = sd_bus_error_set_errnof(error, errno, "Failed to open loopback device: %m");
                goto fail;
        }

        if (ioctl(loop, LOOP_SET_FD, fd) < 0) {
                r = sd_bus_error_set_errnof(error, errno, "Failed to bind loopback device: %m");
                goto fail;
        }

        if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) {
                r = sd_bus_error_set_errnof(error, errno, "Failed to enable auto-clear for loopback device: %m");
                goto fail;
        }

        /* We need to make sure the new /var/lib/machines directory
         * has an access mode of 0700 at the time it is first made
         * available. mkfs will create it with 0755 however. Hence,
         * let's mount the directory into an inaccessible directory
         * below /tmp first, fix the access mode, and move it to the
         * public place then. */

        if (!mkdtemp(tmpdir)) {
                r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount parent directory: %m");
                goto fail;
        }
        tmpdir_made = true;

        mntdir = strjoina(tmpdir, "/mnt");
        if (mkdir(mntdir, 0700) < 0) {
                r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount directory: %m");
                goto fail;
        }
        mntdir_made = true;

        if (mount(loopdev, mntdir, "btrfs", 0, NULL) < 0) {
                r = sd_bus_error_set_errnof(error, errno, "Failed to mount loopback device: %m");
                goto fail;
        }
        mntdir_mounted = true;

        r = btrfs_quota_enable(mntdir, true);
        if (r < 0)
                log_warning_errno(r, "Failed to enable quota, ignoring: %m");

        r = btrfs_subvol_auto_qgroup(mntdir, 0, true);
        if (r < 0)
                log_warning_errno(r, "Failed to set up default quota hierarchy, ignoring: %m");

        if (chmod(mntdir, 0700) < 0) {
                r = sd_bus_error_set_errnof(error, errno, "Failed to fix owner: %m");
                goto fail;
        }

        (void) mkdir_p_label("/var/lib/machines", 0700);

        if (mount(mntdir, "/var/lib/machines", NULL, MS_BIND, NULL) < 0) {
                r = sd_bus_error_set_errnof(error, errno, "Failed to mount directory into right place: %m");
                goto fail;
        }

        (void) syncfs(fd);

        log_info("Set up /var/lib/machines as btrfs loopback file system of size %s mounted on /var/lib/machines.raw.", format_bytes(buf, sizeof(buf), size));

        (void) umount2(mntdir, MNT_DETACH);
        (void) rmdir(mntdir);
        (void) rmdir(tmpdir);

        return 1;

fail:
        if (mntdir_mounted)
                (void) umount2(mntdir, MNT_DETACH);

        if (mntdir_made)
                (void) rmdir(mntdir);
        if (tmpdir_made)
                (void) rmdir(tmpdir);

        if (loop >= 0) {
                (void) ioctl(loop, LOOP_CLR_FD);
                loop = safe_close(loop);
        }

        if (control >= 0 && nr >= 0)
                (void) ioctl(control, LOOP_CTL_REMOVE, nr);

        return r;
}
int main(int argc, char *argv[]) {
    _cleanup_free_ char *what = NULL;
    _cleanup_fclose_ FILE *f = NULL;
    int r = EXIT_SUCCESS;
    sd_id128_t id;
    char *name;

    if (argc > 1 && argc != 4) {
        log_error("This program takes three or no arguments.");
        return EXIT_FAILURE;
    }

    if (argc > 1)
        arg_dest = argv[3];

    log_set_target(LOG_TARGET_SAFE);
    log_parse_environment();
    log_open();

    umask(0022);

    if (in_initrd()) {
        log_debug("In initrd, exiting.");
        return EXIT_SUCCESS;
    }

    if (detect_container(NULL) > 0) {
        log_debug("In a container, exiting.");
        return EXIT_SUCCESS;
    }

    if (!is_efi_boot()) {
        log_debug("Not an EFI boot, exiting.");
        return EXIT_SUCCESS;
    }

    if (path_is_mount_point("/boot", true) <= 0 &&
            dir_is_empty("/boot") <= 0) {
        log_debug("/boot already populated, exiting.");
        return EXIT_SUCCESS;
    }

    r = efi_loader_get_device_part_uuid(&id);
    if (r == -ENOENT) {
        log_debug("EFI loader partition unknown, exiting.");
        return EXIT_SUCCESS;
    } else if (r < 0) {
        log_error_errno(r, "Failed to read ESP partition UUID: %m");
        return EXIT_FAILURE;
    }

    name = strjoina(arg_dest, "/boot.mount");
    f = fopen(name, "wxe");
    if (!f) {
        log_error_errno(errno, "Failed to create mount unit file %s: %m", name);
        return EXIT_FAILURE;
    }

    r = asprintf(&what,
                 "/dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
                 SD_ID128_FORMAT_VAL(id));
    if (r < 0) {
        log_oom();
        return EXIT_FAILURE;
    }

    fprintf(f,
            "# Automatially generated by systemd-efi-boot-generator\n\n"
            "[Unit]\n"
            "Description=EFI System Partition\n"
            "Documentation=man:systemd-efi-boot-generator(8)\n");

    r = generator_write_fsck_deps(f, arg_dest, what, "/boot", "vfat");
    if (r < 0)
        return EXIT_FAILURE;

    fprintf(f,
            "\n"
            "[Mount]\n"
            "What=%s\n"
            "Where=/boot\n"
            "Type=vfat\n"
            "Options=umask=0077,noauto\n",
            what);

    fflush(f);
    if (ferror(f)) {
        log_error_errno(errno, "Failed to write mount unit file: %m");
        return EXIT_FAILURE;
    }

    name = strjoina(arg_dest, "/boot.automount");
    fclose(f);
    f = fopen(name, "wxe");
    if (!f) {
        log_error_errno(errno, "Failed to create automount unit file %s: %m", name);
        return EXIT_FAILURE;
    }

    fputs("# Automatially generated by systemd-efi-boot-generator\n\n"
          "[Unit]\n"
          "Description=EFI System Partition Automount\n\n"
          "[Automount]\n"
          "Where=/boot\n", f);

    fflush(f);
    if (ferror(f)) {
        log_error_errno(errno, "Failed to write automount unit file: %m");
        return EXIT_FAILURE;
    }

    name = strjoina(arg_dest, "/" SPECIAL_LOCAL_FS_TARGET ".wants/boot.automount");
    mkdir_parents(name, 0755);

    if (symlink("../boot.automount", name) < 0) {
        log_error_errno(errno, "Failed to create symlink %s: %m", name);
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}
Beispiel #17
0
/* Mount legacy cgroup hierarchy when cgroup namespaces are unsupported. */
static int mount_legacy_cgns_unsupported(
                const char *dest,
                CGroupUnified unified_requested,
                bool userns,
                uid_t uid_shift,
                uid_t uid_range,
                const char *selinux_apifs_context) {

        _cleanup_set_free_free_ Set *controllers = NULL;
        const char *cgroup_root;
        int r;

        cgroup_root = prefix_roota(dest, "/sys/fs/cgroup");

        (void) mkdir_p(cgroup_root, 0755);

        /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
        r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
        if (r < 0)
                return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
        if (r == 0) {
                _cleanup_free_ char *options = NULL;

                r = tmpfs_patch_options("mode=755", uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &options);
                if (r < 0)
                        return log_oom();

                r = mount_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs",
                                  MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options);
                if (r < 0)
                        return r;
        }

        r = cg_all_unified();
        if (r < 0)
                return r;
        if (r > 0)
                goto skip_controllers;

        r = cg_kernel_controllers(&controllers);
        if (r < 0)
                return log_error_errno(r, "Failed to determine cgroup controllers: %m");

        for (;;) {
                _cleanup_free_ char *controller = NULL, *origin = NULL, *combined = NULL;

                controller = set_steal_first(controllers);
                if (!controller)
                        break;

                origin = prefix_root("/sys/fs/cgroup/", controller);
                if (!origin)
                        return log_oom();

                r = readlink_malloc(origin, &combined);
                if (r == -EINVAL) {
                        /* Not a symbolic link, but directly a single cgroup hierarchy */

                        r = mount_legacy_cgroup_hierarchy(dest, controller, controller, true);
                        if (r < 0)
                                return r;

                } else if (r < 0)
                        return log_error_errno(r, "Failed to read link %s: %m", origin);
                else {
                        _cleanup_free_ char *target = NULL;

                        target = prefix_root(dest, origin);
                        if (!target)
                                return log_oom();

                        /* A symbolic link, a combination of controllers in one hierarchy */

                        if (!filename_is_valid(combined)) {
                                log_warning("Ignoring invalid combined hierarchy %s.", combined);
                                continue;
                        }

                        r = mount_legacy_cgroup_hierarchy(dest, combined, combined, true);
                        if (r < 0)
                                return r;

                        r = symlink_idempotent(combined, target, false);
                        if (r == -EINVAL)
                                return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m");
                        if (r < 0)
                                return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
                }
        }

skip_controllers:
        if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
                r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
                if (r < 0)
                        return r;
        }

        r = mount_legacy_cgroup_hierarchy(dest, SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
        if (r < 0)
                return r;

        return mount_verbose(LOG_ERR, NULL, cgroup_root, NULL,
                             MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");
}
Beispiel #18
0
/* Mount a legacy cgroup hierarchy when cgroup namespaces are supported. */
static int mount_legacy_cgns_supported(
                const char *dest,
                CGroupUnified unified_requested,
                bool userns,
                uid_t uid_shift,
                uid_t uid_range,
                const char *selinux_apifs_context) {

        _cleanup_set_free_free_ Set *controllers = NULL;
        const char *cgroup_root = "/sys/fs/cgroup", *c;
        int r;

        (void) mkdir_p(cgroup_root, 0755);

        /* Mount a tmpfs to /sys/fs/cgroup if it's not mounted there yet. */
        r = path_is_mount_point(cgroup_root, dest, AT_SYMLINK_FOLLOW);
        if (r < 0)
                return log_error_errno(r, "Failed to determine if /sys/fs/cgroup is already mounted: %m");
        if (r == 0) {
                _cleanup_free_ char *options = NULL;

                /* When cgroup namespaces are enabled and user namespaces are
                 * used then the mount of the cgroupfs is done *inside* the new
                 * user namespace. We're root in the new user namespace and the
                 * kernel will happily translate our uid/gid to the correct
                 * uid/gid as seen from e.g. /proc/1/mountinfo. So we simply
                 * pass uid 0 and not uid_shift to tmpfs_patch_options().
                 */
                r = tmpfs_patch_options("mode=755", 0, selinux_apifs_context, &options);
                if (r < 0)
                        return log_oom();

                r = mount_verbose(LOG_ERR, "tmpfs", cgroup_root, "tmpfs",
                                  MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME, options);
                if (r < 0)
                        return r;
        }

        r = cg_all_unified();
        if (r < 0)
                return r;
        if (r > 0)
                goto skip_controllers;

        r = get_process_controllers(&controllers);
        if (r < 0)
                return log_error_errno(r, "Failed to determine cgroup controllers: %m");

        for (;;) {
                _cleanup_free_ const char *controller = NULL;

                controller = set_steal_first(controllers);
                if (!controller)
                        break;

                r = mount_legacy_cgroup_hierarchy("", controller, controller, !userns);
                if (r < 0)
                        return r;

                /* When multiple hierarchies are co-mounted, make their
                 * constituting individual hierarchies a symlink to the
                 * co-mount.
                 */
                c = controller;
                for (;;) {
                        _cleanup_free_ char *target = NULL, *tok = NULL;

                        r = extract_first_word(&c, &tok, ",", 0);
                        if (r < 0)
                                return log_error_errno(r, "Failed to extract co-mounted cgroup controller: %m");
                        if (r == 0)
                                break;

                        if (streq(controller, tok))
                                break;

                        target = prefix_root("/sys/fs/cgroup/", tok);
                        if (!target)
                                return log_oom();

                        r = symlink_idempotent(controller, target, false);
                        if (r == -EINVAL)
                                return log_error_errno(r, "Invalid existing symlink for combined hierarchy: %m");
                        if (r < 0)
                                return log_error_errno(r, "Failed to create symlink for combined hierarchy: %m");
                }
        }

skip_controllers:
        if (unified_requested >= CGROUP_UNIFIED_SYSTEMD) {
                r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_HYBRID, "unified", false);
                if (r < 0)
                        return r;
        }

        r = mount_legacy_cgroup_hierarchy("", SYSTEMD_CGROUP_CONTROLLER_LEGACY, "systemd", false);
        if (r < 0)
                return r;

        if (!userns)
                return mount_verbose(LOG_ERR, NULL, cgroup_root, NULL,
                                     MS_REMOUNT|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_STRICTATIME|MS_RDONLY, "mode=755");

        return 0;
}