diff options
author | Colin Walters <walters@verbum.org> | 2017-05-11 14:54:12 -0400 |
---|---|---|
committer | Atomic Bot <atomic-devel@projectatomic.io> | 2017-05-16 16:13:05 +0000 |
commit | 30705889cb867c18cdb7fed8e55dc46477c069fa (patch) | |
tree | b6247fa4e4d51ad7a44bfef90381f3aad47c9d52 /src/libostree/ostree-impl-system-generator.c | |
parent | d815ba2a81ad14d9d4edc31dcd282dcc2a3a8fb9 (diff) | |
download | ostree-30705889cb867c18cdb7fed8e55dc46477c069fa.tar.gz |
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
Diffstat (limited to 'src/libostree/ostree-impl-system-generator.c')
-rw-r--r-- | src/libostree/ostree-impl-system-generator.c | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/src/libostree/ostree-impl-system-generator.c b/src/libostree/ostree-impl-system-generator.c new file mode 100644 index 00000000..7c4d49df --- /dev/null +++ b/src/libostree/ostree-impl-system-generator.c @@ -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 (®ex_initialized)) + { + regex = g_regex_new ("^/ostree/boot.[01]/([^/]+)/", 0, 0, NULL); + g_assert (regex); + g_once_init_leave (®ex_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 +} |