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); }
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; }
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; }
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); }
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; }
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; }
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; }
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; }
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); }
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; }
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."); } }
/* 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); } }
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); } }
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; }
/* 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"); }
/* 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; }