ret_deployment->unlocked = OSTREE_DEPLOYMENT_UNLOCKED_NONE;
g_autofree char *unlocked_development_path =
_ostree_sysroot_get_runstate_path (ret_deployment, _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_DEVELOPMENT);
+ g_autofree char *unlocked_transient_path =
+ _ostree_sysroot_get_runstate_path (ret_deployment, _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_TRANSIENT);
struct stat stbuf;
if (lstat (unlocked_development_path, &stbuf) == 0)
ret_deployment->unlocked = OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT;
+ else if (lstat (unlocked_transient_path, &stbuf) == 0)
+ ret_deployment->unlocked = OSTREE_DEPLOYMENT_UNLOCKED_TRANSIENT;
else
{
GKeyFile *origin = ostree_deployment_get_origin (ret_deployment);
const char *ovl_options = NULL;
static const char hotfix_ovl_options[] = "lowerdir=usr,upperdir=.usr-ovl-upper,workdir=.usr-ovl-work";
+ g_autofree char *unlock_ovldir = NULL;
+
switch (unlocked_state)
{
case OSTREE_DEPLOYMENT_UNLOCKED_NONE:
}
break;
case OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT:
+ case OSTREE_DEPLOYMENT_UNLOCKED_TRANSIENT:
{
+ unlock_ovldir = g_strdup ("/var/tmp/ostree-unlock-ovl.XXXXXX");
/* We're just doing transient development/hacking? Okay,
* stick the overlayfs bits in /var/tmp.
*/
- char *development_ovldir = strdupa ("/var/tmp/ostree-unlock-ovl.XXXXXX");
const char *development_ovl_upper;
const char *development_ovl_work;
"/usr", usr_mode, error))
return FALSE;
- if (g_mkdtemp_full (development_ovldir, 0755) == NULL)
+ if (g_mkdtemp_full (unlock_ovldir, 0755) == NULL)
return glnx_throw_errno_prefix (error, "mkdtemp");
}
- development_ovl_upper = glnx_strjoina (development_ovldir, "/upper");
+ development_ovl_upper = glnx_strjoina (unlock_ovldir, "/upper");
if (!mkdir_unmasked (AT_FDCWD, development_ovl_upper, usr_mode, cancellable, error))
return FALSE;
- development_ovl_work = glnx_strjoina (development_ovldir, "/work");
+ development_ovl_work = glnx_strjoina (unlock_ovldir, "/work");
if (!mkdir_unmasked (AT_FDCWD, development_ovl_work, usr_mode, cancellable, error))
return FALSE;
ovl_options = glnx_strjoina ("lowerdir=usr,upperdir=", development_ovl_upper,
return glnx_throw_errno_prefix (error, "fork");
else if (mount_child == 0)
{
+ int mountflags = 0;
+ if (unlocked_state == OSTREE_DEPLOYMENT_UNLOCKED_TRANSIENT)
+ mountflags |= MS_RDONLY;
/* Child process. Do NOT use any GLib API here; it's not generally fork() safe.
*
* TODO: report errors across a pipe (or use the journal?) rather than
*/
if (fchdir (deployment_dfd) < 0)
err (1, "fchdir");
- if (mount ("overlay", "/usr", "overlay", 0, ovl_options) < 0)
+ if (mount ("overlay", "/usr", "overlay", mountflags, ovl_options) < 0)
err (1, "mount");
exit (EXIT_SUCCESS);
}
return FALSE;
break;
case OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT:
+ case OSTREE_DEPLOYMENT_UNLOCKED_TRANSIENT:
{
g_autofree char *devpath =
- _ostree_sysroot_get_runstate_path (deployment, _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_DEVELOPMENT);
+ unlocked_state == OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT ?
+ _ostree_sysroot_get_runstate_path (deployment, _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_DEVELOPMENT)
+ :
+ _ostree_sysroot_get_runstate_path (deployment, _OSTREE_SYSROOT_DEPLOYMENT_RUNSTATE_FLAG_TRANSIENT);
g_autofree char *devpath_parent = dirname (g_strdup (devpath));
if (!glnx_shutil_mkdir_p_at (AT_FDCWD, devpath_parent, 0755, cancellable, error))
return FALSE;
- if (!g_file_set_contents (devpath, "", 0, error))
+ if (!g_file_set_contents (devpath, unlock_ovldir, -1, error))
return FALSE;
}
}
#include <err.h>
static gboolean opt_hotfix;
+static gboolean opt_transient;
static GOptionEntry options[] = {
{ "hotfix", 0, 0, G_OPTION_ARG_NONE, &opt_hotfix, "Retain changes across reboots", NULL },
+ { "transient", 0, 0, G_OPTION_ARG_NONE, &opt_transient, "Mount overlayfs read-only by default", NULL },
{ NULL }
};
goto out;
}
- target_state = opt_hotfix ? OSTREE_DEPLOYMENT_UNLOCKED_HOTFIX : OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT;
+ if (opt_hotfix && opt_transient)
+ {
+ glnx_throw (error, "Cannot specify both --hotfix and --transient");
+ goto out;
+ }
+ else if (opt_hotfix)
+ target_state = OSTREE_DEPLOYMENT_UNLOCKED_HOTFIX;
+ else if (opt_transient)
+ target_state = OSTREE_DEPLOYMENT_UNLOCKED_TRANSIENT;
+ else
+ target_state = OSTREE_DEPLOYMENT_UNLOCKED_DEVELOPMENT;
if (!ostree_sysroot_deployment_unlock (sysroot, booted_deployment,
target_state, cancellable, error))
g_print ("Development mode enabled. A writable overlayfs is now mounted on /usr.\n"
"All changes there will be discarded on reboot.\n");
break;
+ case OSTREE_DEPLOYMENT_UNLOCKED_TRANSIENT:
+ g_print ("A writable overlayfs is prepared for /usr, but is mounted read-only by default.\n"
+ "All changes there will be discarded on reboot.\n");
+ break;
}
ret = TRUE;
--- /dev/null
+#!/bin/bash
+# Test unlock --transient
+set -xeuo pipefail
+
+. ${KOLA_EXT_DATA}/libinsttest.sh
+
+testfile=/usr/share/writable-usr-test
+
+case "${AUTOPKGTEST_REBOOT_MARK:-}" in
+ "")
+ require_writable_sysroot
+ assert_not_has_file "${testfile}"
+ ostree admin unlock --transient
+ # It's still read-only
+ if touch ${testfile}; then
+ fatal "modified /usr"
+ fi
+ # But, we can affect it in a new mount namespace
+ unshare -m -- /bin/sh -c 'mount -o remount,rw /usr && echo hello from transient unlock >'"${testfile}"
+ assert_file_has_content "${testfile}" "hello from transient unlock"
+ # Still can't write to it from the outer namespace
+ if touch ${testfile} || rm -v "${testfile}" 2>/dev/null; then
+ fatal "modified ${testfile}"
+ fi
+ /tmp/autopkgtest-reboot 2
+ ;;
+ "2")
+ if test -f "${testfile}"; then
+ fatal "${testfile} persisted across reboot?"
+ fi
+ echo "ok unlock transient"
+ ;;
+ *) fatal "Unexpected boot mark ${AUTOPKGTEST_REBOOT_MARK}"
+esac