Switch to using a systemd generator for /var

If one wants to set up a mount for `/var` in `/etc/fstab`, it
won't be mounted since `ostree-prepare-root` set up a bind mount for
`/var` to `/sysroot/ostree/$stateroot/var`, and systemd will take
the already extant mount over what's in `/etc/fstab`.

There are a few options to fix this, but what I settled on is parsing
`/etc/fstab` in a generator (exactly like `systemd-fstab-generator` does),
except here we look for an explicit mount for `/var`, and if one *isn't* found,
synthesize the default ostree mount to the stateroot. Another nice property is
that if an admin creates a `var.mount` unit in `/etc` for example, that will
also override our mount.

Note that today ostree doesn't hard depend on systemd, so this behavior only
kicks in if we're built with systemd *and* libmount support (for parsing
`/etc/fstab`).  I didn't really test that case though.

Initially I started writing this as a "pure libc" program, but at one point
decided to use `libostree.so` to find the booted deployment. That didn't work
out because `/boot` wasn't necessarily mounted and hence we couldn't find the
bootloader config. A leftover artifact from this is that the generator code
calls into libostree via the "cmd private" infrastructure. But it's an easy way
to share code, and doesn't hurt.

Closes: #859
Approved by: jlebon
This commit is contained in:
Colin Walters authored and Atomic Bot committed 2017-05-16 16:13:05 +00:00
1 parent d815ba2a81
commit 30705889cb
10 files changed
+390 -65

No files matched your search

+1
View File
@@ -45,6 +45,7 @@ const OstreeCmdPrivateVTable *
ostree_cmd__private__ (void)
{
static OstreeCmdPrivateVTable table = {
_ostree_impl_system_generator,
impl_ostree_generate_grub2_config,
_ostree_repo_static_delta_dump,
_ostree_repo_static_delta_query_exists,
+3
View File
@@ -24,7 +24,10 @@
G_BEGIN_DECLS
gboolean _ostree_impl_system_generator (const char *ostree_cmdline, const char *normal_dir, const char *early_dir, const char *late_dir, GError **error);
typedef struct {
gboolean (* ostree_system_generator) (const char *ostree_cmdline, const char *normal_dir, const char *early_dir, const char *late_dir, GError **error);
gboolean (* ostree_generate_grub2_config) (OstreeSysroot *sysroot, int bootversion, int target_fd, GCancellable *cancellable, GError **error);
gboolean (* ostree_static_delta_dump) (OstreeRepo *repo, const char *delta_id, GCancellable *cancellable, GError **error);
gboolean (* ostree_static_delta_query_exists) (OstreeRepo *repo, const char *delta_id, gboolean *out_exists, GCancellable *cancellable, GError **error);
@@ -0,0 +1,219 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2017 Colin Walters <walters@verbum.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <glib-unix.h>
#include <gio/gunixoutputstream.h>
#include <errno.h>
#include <stdio.h>
#ifdef HAVE_LIBMOUNT
#include <libmount.h>
#endif
#include <stdbool.h>
#include "otutil.h"
#include "ostree.h"
#include "ostree-core-private.h"
#include "ostree-cmdprivate.h"
#ifdef HAVE_LIBMOUNT
typedef FILE OtLibMountFile;
G_DEFINE_AUTOPTR_CLEANUP_FUNC(OtLibMountFile, endmntent);
/* Taken from systemd path-util.c */
static bool
is_path (const char *p)
{
return !!strchr (p, '/');
}
/* Taken from systemd path-util.c */
static char*
path_kill_slashes (char *path)
{
char *f, *t;
bool slash = false;
/* Removes redundant inner and trailing slashes. Modifies the
* passed string in-place.
*
* For example: ///foo///bar/ becomes /foo/bar
*/
for (f = path, t = path; *f; f++)
{
if (*f == '/')
{
slash = true;
continue;
}
if (slash)
{
slash = false;
*(t++) = '/';
}
*(t++) = *f;
}
/* Special rule, if we are talking of the root directory, a
trailing slash is good */
if (t == path && slash)
*(t++) = '/';
*t = 0;
return path;
}
/* Written by ostree-sysroot-deploy.c. We parse out the stateroot here since we
* need to know it to mount /var. Unfortunately we can't easily use the
* libostree API to find the booted deployment since /boot might not have been
* mounted yet.
*/
static char *
stateroot_from_ostree_cmdline (const char *ostree_cmdline,
GError **error)
{
static GRegex *regex;
static gsize regex_initialized;
if (g_once_init_enter (&regex_initialized))
{
regex = g_regex_new ("^/ostree/boot.[01]/([^/]+)/", 0, 0, NULL);
g_assert (regex);
g_once_init_leave (&regex_initialized, 1);
}
g_autoptr(GMatchInfo) match = NULL;
if (!g_regex_match (regex, ostree_cmdline, 0, &match))
return glnx_null_throw (error, "Failed to parse %s", ostree_cmdline);
return g_match_info_fetch (match, 1);
}
#endif
/* Implementation of ostree-system-generator */
gboolean
_ostree_impl_system_generator (const char *ostree_cmdline,
const char *normal_dir,
const char *early_dir,
const char *late_dir,
GError **error)
{
#ifdef HAVE_LIBMOUNT
/* Not currently cancellable, but define a var in case we care later */
GCancellable *cancellable = NULL;
/* Some path constants to avoid typos */
static const char fstab_path[] = "/etc/fstab";
static const char var_path[] = "/var";
/* ostree-prepare-root was patched to write the stateroot to this file */
g_autofree char *stateroot = stateroot_from_ostree_cmdline (ostree_cmdline, error);
if (!stateroot)
return FALSE;
/* Load /etc/fstab if it exists, and look for a /var mount */
g_autoptr(OtLibMountFile) fstab = setmntent (fstab_path, "re");
gboolean found_var_mnt = FALSE;
if (!fstab)
{
if (errno != ENOENT)
return glnx_throw_errno_prefix (error, "Reading %s", fstab_path);
}
else
{
/* Parse it */
struct mntent *me;
while ((me = getmntent (fstab)))
{
g_autofree char *where = g_strdup (me->mnt_dir);
if (is_path (where))
path_kill_slashes (where);
/* We're only looking for /var here */
if (strcmp (where, var_path) != 0)
continue;
found_var_mnt = TRUE;
break;
}
}
/* If we found /var, we're done */
if (found_var_mnt)
return TRUE;
/* Prepare to write to the output unit dir; we use the "normal" dir
* that overrides /usr, but not /etc.
*/
glnx_fd_close int normal_dir_dfd = -1;
if (!glnx_opendirat (AT_FDCWD, normal_dir, TRUE, &normal_dir_dfd, error))
return FALSE;
/* Generate our bind mount unit */
const char *stateroot_var_path = glnx_strjoina ("/sysroot/ostree/deploy/", stateroot, "/var");
glnx_fd_close int tmpfd = -1;
g_autofree char *tmppath = NULL;
if (!glnx_open_tmpfile_linkable_at (normal_dir_dfd, ".", O_WRONLY,
&tmpfd, &tmppath, error))
return FALSE;
g_autoptr(GOutputStream) outstream = g_unix_output_stream_new (tmpfd, FALSE);
gsize bytes_written;
/* This code is inspired by systemd's fstab-generator.c.
*
* Note that our unit doesn't run if systemd.volatile is enabled;
* see https://github.com/ostreedev/ostree/pull/856
*/
if (!g_output_stream_printf (outstream, &bytes_written, cancellable, error,
"##\n# Automatically generated by ostree-system-generator\n##\n\n"
"[Unit]\n"
"Documentation=man:ostree(1)\n"
"ConditionKernelCommandLine=!systemd.volatile\n"
/* We need /sysroot mounted writable first */
"After=ostree-remount.service\n"
"Before=local-fs.target\n"
"\n"
"[Mount]\n"
"Where=%s\n"
"What=%s\n"
"Options=bind\n",
var_path,
stateroot_var_path))
return FALSE;
if (!g_output_stream_flush (outstream, cancellable, error))
return FALSE;
g_clear_object (&outstream);
/* It should be readable */
if (fchmod (tmpfd, 0644) < 0)
return glnx_throw_errno_prefix (error, "fchmod");
/* Error out if somehow it already exists, that'll help us debug conflicts */
if (!glnx_link_tmpfile_at (normal_dir_dfd, GLNX_LINK_TMPFILE_NOREPLACE,
tmpfd, tmppath, normal_dir_dfd,
"var.mount", error))
return FALSE;
return TRUE;
#else
return glnx_throw (error, "Not implemented");
#endif
}
+1
View File
@@ -1379,6 +1379,7 @@ install_deployment_kernel (OstreeSysroot *sysroot,
val = ostree_bootconfig_parser_get (bootconfig, "options");
/* Note this is parsed in ostree-impl-system-generator.c */
g_autofree char *ostree_kernel_arg = g_strdup_printf ("ostree=/ostree/boot.%d/%s/%s/%d",
new_bootversion, osname, bootcsum,
ostree_deployment_get_bootserial (deployment));