Merge pull request #2113 from cgwalters/prepare-root-sysroot-ro

Move ro /sysroot bind mount of /etc into initramfs
This commit is contained in:
OpenShift Merge Robot 2020-05-26 11:38:42 -04:00 committed by GitHub
commit 936301608a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 75 additions and 37 deletions

View File

@ -30,11 +30,13 @@
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <string.h> #include <string.h>
#include <stdbool.h>
#define INITRAMFS_MOUNT_VAR "/run/ostree/initramfs-mount-var" #define INITRAMFS_MOUNT_VAR "/run/ostree/initramfs-mount-var"
#define _OSTREE_SYSROOT_READONLY_STAMP "/run/ostree-sysroot-ro.stamp"
static inline int static inline int
path_is_on_readonly_fs (char *path) path_is_on_readonly_fs (const char *path)
{ {
struct statvfs stvfsbuf; struct statvfs stvfsbuf;

View File

@ -60,6 +60,7 @@
#include <sys/syscall.h> #include <sys/syscall.h>
#include <fcntl.h> #include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <assert.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
@ -83,6 +84,47 @@
/* Initialized early in main */ /* Initialized early in main */
static bool running_as_pid1; static bool running_as_pid1;
static inline bool
sysroot_is_configured_ro (const char *sysroot)
{
char * config_path = NULL;
assert (asprintf (&config_path, "%s/ostree/repo/config", sysroot) != -1);
FILE *f = fopen(config_path, "r");
if (!f)
{
fprintf (stderr, "Missing expected repo config: %s\n", config_path);
free (config_path);
return false;
}
free (config_path);
bool ret = false;
char *line = NULL;
size_t len = 0;
ssize_t nread;
/* Note getline() will reuse the previous buffer */
bool in_sysroot = false;
while ((nread = getline (&line, &len, f)) != -1)
{
/* This is an awful hack to avoid depending on GLib in the
* initramfs right now.
*/
if (strstr (line, "[sysroot]") == line)
in_sysroot = true;
else if (*line == '[')
in_sysroot = false;
else if (in_sysroot && strstr (line, "readonly=true") == line)
{
ret = true;
break;
}
}
fclose (f);
free (line);
return ret;
}
static char* static char*
resolve_deploy_path (const char * root_mountpoint) resolve_deploy_path (const char * root_mountpoint)
{ {
@ -192,6 +234,33 @@ main(int argc, char *argv[])
if (chdir (deploy_path) < 0) if (chdir (deploy_path) < 0)
err (EXIT_FAILURE, "failed to chdir to deploy_path"); err (EXIT_FAILURE, "failed to chdir to deploy_path");
/* Query the repository configuration - this is an operating system builder
* choice. More info: https://github.com/ostreedev/ostree/pull/1767
*/
const bool sysroot_readonly = sysroot_is_configured_ro (root_arg);
const bool sysroot_currently_writable = !path_is_on_readonly_fs (root_arg);
#ifdef USE_LIBSYSTEMD
sd_journal_send ("MESSAGE=sysroot configured read-only: %d, currently writable: %d",
(int)sysroot_readonly, (int)sysroot_currently_writable, NULL);
#endif
if (sysroot_readonly)
{
if (!sysroot_currently_writable)
errx (EXIT_FAILURE, "sysroot=readonly currently requires writable / in initramfs");
/* Now, /etc is not normally a bind mount, but if we have a readonly
* sysroot, we still need a writable /etc. And to avoid race conditions
* we ensure it's writable in the initramfs, before we switchroot at all.
*/
if (mount ("/etc", "/etc", NULL, MS_BIND, NULL) < 0)
err (EXIT_FAILURE, "failed to make /etc a bind mount");
/* Pass on the fact that we discovered a readonly sysroot to ostree-remount.service */
int fd = open (_OSTREE_SYSROOT_READONLY_STAMP, O_WRONLY | O_CREAT | O_CLOEXEC, 0644);
if (fd < 0)
err (EXIT_FAILURE, "failed to create %s", _OSTREE_SYSROOT_READONLY_STAMP);
(void) close (fd);
}
/* Default to true, but in the systemd case, default to false because it's handled by /* Default to true, but in the systemd case, default to false because it's handled by
* ostree-system-generator. */ * ostree-system-generator. */
bool mount_var = true; bool mount_var = true;

View File

@ -81,24 +81,6 @@ do_remount (const char *target,
printf ("Remounted %s: %s\n", writable ? "rw" : "ro", target); printf ("Remounted %s: %s\n", writable ? "rw" : "ro", target);
} }
static bool
sysroot_is_configured_ro (void)
{
struct stat stbuf;
static const char config_path[] = "/ostree/repo/config";
if (stat (config_path, &stbuf) != 0)
return false;
g_autoptr(GKeyFile) keyfile = g_key_file_new ();
if (!g_key_file_load_from_file (keyfile, config_path, 0, NULL))
return false;
if (g_key_file_get_boolean (keyfile, "sysroot", "readonly", NULL))
puts ("Ignoring sysroot.readonly config; see https://github.com/coreos/fedora-coreos-tracker/issues/488.");
return false;
}
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
@ -124,25 +106,10 @@ main(int argc, char *argv[])
exit (EXIT_SUCCESS); exit (EXIT_SUCCESS);
} }
/* Query the repository configuration - this is an operating system builder /* Handle remounting /sysroot read-only now */
* choice. if (unlink (_OSTREE_SYSROOT_READONLY_STAMP) == 0)
* */
const bool sysroot_readonly = sysroot_is_configured_ro ();
/* Mount the sysroot read-only if we're configured to do so.
* Note we only get here if / is already writable.
*/
do_remount ("/sysroot", !sysroot_readonly);
if (sysroot_readonly)
{ {
/* Now, /etc is not normally a bind mount, but remounting the do_remount ("/sysroot", false);
* sysroot above made it read-only since it's on the same filesystem.
* Make it a self-bind mount, so we can then mount it read-write.
*/
if (mount ("/etc", "/etc", NULL, MS_BIND, NULL) < 0)
err (EXIT_FAILURE, "failed to make /etc a bind mount");
do_remount ("/etc", true);
} }
/* If /var was created as as an OSTree default bind mount (instead of being a separate filesystem) /* If /var was created as as an OSTree default bind mount (instead of being a separate filesystem)