Пример #1
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;
}
Пример #2
0
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 */
}