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; }
static int domerg(struct cfextra **extlist, int part, int nparts, int myclass, char **srcp, char **dstp, char **r_updated, char **r_skipped, char **r_anyPathLocal) { boolean_t stateFlag = B_FALSE; int i; int msg_ugid; static int maxvol = 0; static int svindx = 0; static int svpart = 0; struct cfent *ept = (struct cfent *)NULL; struct mergstat *mstat = (struct mergstat *)NULL; /* reset returned path pointers */ *dstp = (char *)NULL; *srcp = (char *)NULL; /* set to start or continue based on which part being processed */ if (part != 0) { maxvol = 0; svindx = 0; svpart = part; } else { i = svindx; part = svpart; } /* * This goes through the pkgmap entries one by one testing them * for inclusion in the package database as well as for validity * against existing files. */ for (i = svindx; extlist[i]; i++) { ept = &(extlist[i]->cf_ent); mstat = &(extlist[i]->mstat); /* * as paths are processed, if the "anyPathLocal" flag has not * been set, if the object is not of type 'i' (package script), * check to see if the object is in an area inherited from the * global zone - if not, set "anyPathLocal" to the path found, * indicating that at least one path is in an area that is not * inherited from the global zone. */ if ((r_anyPathLocal != (char **)NULL) && (*r_anyPathLocal == (char *)NULL) && (ept->ftype != 'i') && (z_path_is_inherited(ept->path, ept->ftype, get_inst_root()) == B_FALSE)) { echoDebug(DBG_INSTVOL_OBJ_LOCAL, ept->path); *r_anyPathLocal = ept->path; } /* if this isn't the class of current interest, skip it */ if (myclass != ept->pkg_class_idx) { continue; } /* if the class is invalid, announce it & exit */ if (ept->pkg_class_idx == -1) { progerr(ERR_CLIDX, ept->pkg_class_idx, (ept->path && *ept->path) ? ept->path : "unknown"); logerr(gettext("pathname=%s\n"), (ept->path && *ept->path) ? ept->path : "unknown"); logerr(gettext("class=<%s>\n"), (ept->pkg_class && *ept->pkg_class) ? ept->pkg_class : "Unknown"); logerr(gettext("CLASSES=<%s>\n"), getenv("CLASSES") ? getenv("CLASSES") : "Not Set"); quit(99); } /* * Next check to see if we are going to try to delete a * populated directory in some distressing way. */ if (mstat->dir2nondir) if (dir_is_populated(ept->path)) { logerr(WRN_INSTVOL_NOTDIR, ept->path); warnflag++; mstat->denied = 1; /* install denied! */ continue; } else { /* Replace is OK. */ /* * Remove this directory, so it won't * interfere with creation of the new object. */ if (rmdir(ept->path)) { /* * If it didn't work, there's nothing * we can do. To continue would * likely corrupt the filesystem * which is unacceptable. */ progerr(ERR_RMDIR, ept->path); quit(99); } repl_permitted = 1; /* flag it */ } /* adjust the max volume number appropriately */ if (ept->volno > maxvol) { maxvol = ept->volno; } /* if this part goes into another volume, skip it */ if (part != ept->volno) { continue; } /* * If it's a conflicting file and it's not supposed to be * installed, note it and skip. */ if (nocnflct && mstat->shared && ept->ftype != 'e') { if (mstat->contchg || mstat->attrchg) { echo(MSG_SHIGN, ept->path); } continue; } /* * If we want to set uid or gid but user says no, note it. * Remember that the actual mode bits in the structure have * already been adjusted and the mstat flag is telling us * about the original mode. */ if (nosetuid && (mstat->setuid || mstat->setgid)) { msg_ugid = 1; /* don't repeat attribute message. */ if (is_fs_writeable(ept->path, &(extlist[i]->fsys_value))) { if (!(mstat->contchg) && mstat->attrchg) { echo(MSG_UGMOD, ept->path); } else { echo(MSG_UGID, ept->path); } } } else { msg_ugid = 0; } switch (ept->ftype) { case 'l': /* hard link */ /* links treated as object "update/skip" */ stateFlag = B_TRUE; continue; /* defer to final proc */ case 's': /* for symlink, verify without fix first */ /* links treated as object "update/skip" */ stateFlag = B_TRUE; /* Do this only for default verify */ if (cl_dvfy(myclass) == DEFAULT) { if (averify(0, &ept->ftype, ept->path, &ept->ainfo)) echo(MSG_SLINK, ept->path); } /*FALLTHRU*/ case 'd': /* directory */ case 'x': /* exclusive directory */ case 'c': /* character special device */ case 'b': /* block special device */ case 'p': /* named pipe */ /* these NOT treated as object "update/skip" */ stateFlag = B_FALSE; /* * If we can't get to it for legitimate reasons, * don't try to verify it. */ if ((z_path_is_inherited(ept->path, ept->ftype, get_inst_root())) || is_remote_fs(ept->path, &(extlist[i]->fsys_value)) && !is_fs_writeable(ept->path, &(extlist[i]->fsys_value))) { mstat->attrchg = 0; mstat->contchg = 0; break; } if (averify(1, &ept->ftype, ept->path, &ept->ainfo) == 0) { mstat->contchg = mstat->attrchg = 0; } else { progerr(ERR_CREATE_PKGOBJ, ept->path); logerr(getErrbufAddr()); warnflag++; } break; case 'i': /* information file */ /* not treated as object "update/skip" */ stateFlag = B_FALSE; break; default: /* all files treated as object "update/skip" */ stateFlag = B_TRUE; break; } /* * Both contchg and shared flags have to be taken into * account. contchg is set if the file is already present * in the package database, if it does not exist or if it * exists and is modified. * The shared flag is set when 'e' or 'v' file is not * present in the package database, exists and is not * modified. It also has to be checked here. * Shared flag is also set when file is present in package * database and owned by more than one package, but for * this case contchg has already been set. */ if (mstat->contchg || (mstat->shared && ((ept->ftype == 'e') || (ept->ftype == 'v')))) { *dstp = ept->path; if ((ept->ftype == 'f') || (ept->ftype == 'e') || (ept->ftype == 'v')) { *srcp = ept->ainfo.local; if (is_partial_inst() != 0) { if (*srcp[0] == '~') { /* translate source pathname */ *srcp = srcpath(instdir, extlist[i]->map_path, part, nparts); } else { *srcp = extlist[i]->map_path; } } else { if (*srcp[0] == '~') { /* translate source pathname */ *srcp = srcpath(instdir, &(ept->ainfo.local[1]), part, nparts); } } echoDebug(DBG_DOMERG_NO_SUCH_FILE, ept->ftype, cl_nam(ept->pkg_class_idx), ept->path); } else { /* * At this point, we're returning a non-file * that couldn't be created in the standard * way. If it refers to a filesystem that is * not writeable by us, don't waste the * calling process's time. */ if (!is_fs_writeable(ept->path, &(extlist[i]->fsys_value))) { echoDebug(DBG_DOMERG_NOT_WRITABLE, ept->ftype, cl_nam(ept->pkg_class_idx), ept->path); continue; } *srcp = NULL; echoDebug(DBG_DOMERG_NOT_THERE, ept->ftype, cl_nam(ept->pkg_class_idx), ept->path); } svindx = i+1; backup(*dstp, 1); return (i); } if (mstat->attrchg) { backup(ept->path, 0); if (!msg_ugid) echo(MSG_ATTRIB, ept->path); /* fix the attributes now for robustness sake */ if (averify(1, &ept->ftype, ept->path, &ept->ainfo) == 0) { mstat->attrchg = 0; } } /* * package object exists, or does not need updating: if the path * is in an area inherited from the global zone, then treat * the object as if it were "skipped" - if the path is not in an * area inherited from the global zone, then treat the object as * if it were "updated" */ /* LINTED warning: statement has no consequent: if */ if ((stateFlag == B_FALSE) || (ept == (struct cfent *)NULL)) { /* * the object in question is a directory or special * file - the fact that this type of object already * exists or does not need updating must not trigger * the object updated/object skipped indication - * that would cause class action scripts to be run * when installing a new non-global zone - that action * must only be done when a file object that is in * an area inherited from the global zone is present. */ } else if (z_path_is_inherited(ept->path, ept->ftype, get_inst_root()) == B_TRUE) { if (r_skipped != (char **)NULL) { if (*r_skipped == (char *)NULL) { echoDebug(DBG_INSTVOL_OBJ_SKIPPED, ept->path); *r_skipped = ept->path; } } } else { if (r_updated != (char **)NULL) { if (*r_updated == (char *)NULL) { echoDebug(DBG_INSTVOL_OBJ_UPDATED, ept->path); } *r_updated = ept->path; } } } if (maxvol == part) { eocflag++; /* endofclass */ } return (DMRG_DONE); /* no remaining entries on this volume */ }