deploy: Fix removing /var/.updated with separate /var mount
authorColin Walters <walters@verbum.org>
Fri, 21 Sep 2018 13:47:36 +0000 (09:47 -0400)
committerAtomic Bot <atomic-devel@projectatomic.io>
Fri, 21 Sep 2018 15:47:43 +0000 (15:47 +0000)
There's some subtlety to this, we don't handle all cases.
But the 99% cases are using `--sysroot deploy` to create an
initial deployment, and then doing upgrades from inside
a booted deployment.

It was only the latter case that didn't work with a separate `/var`.
Fixing all of them would probably require libostree to learn
how to e.g. look at `/etc/fstab` (or worse, systemd mount units?)
and handle the mounting.  I don't think we want to do anything
like that right now, since there are no active drivers for the
use case.

Closes: https://github.com/ostreedev/ostree/issues/1729
Closes: #1730
Approved by: akiernan

src/libostree/ostree-sysroot-deploy.c

index 8198e191eaa5e7eaf22b1cfc996b6cbc36551be5..246f111405500bac0e9260ec8c6b3526e1414cf9 100644 (file)
@@ -2509,6 +2509,47 @@ sysroot_initialize_deployment (OstreeSysroot     *self,
   return TRUE;
 }
 
+/* Get a directory fd for the /var of @deployment.
+ * Before we supported having /var be a separate mount point,
+ * this was easy. However, as https://github.com/ostreedev/ostree/issues/1729
+ * raised, in the primary case where we're
+ * doing a new deployment for the booted stateroot,
+ * we need to use /var/.  This code doesn't correctly
+ * handle the case of `ostree admin --sysroot upgrade`,
+ * nor (relatedly) the case of upgrading a separate stateroot.
+ */
+static gboolean
+get_var_dfd (OstreeSysroot      *self,
+             int                 osdeploy_dfd,
+             OstreeDeployment   *deployment,
+             int                *ret_fd,
+             GError            **error)
+{
+  const char *booted_stateroot =
+    self->booted_deployment ? ostree_deployment_get_osname (self->booted_deployment) : NULL;
+
+  int base_dfd;
+  const char *base_path;
+  /* The common case is when we're doing a new deployment for the same stateroot (osname).
+   * If we have a separate mounted /var, then we need to use it - the /var in the
+   * stateroot will probably just be an empty directory.
+   *
+   * If the stateroot doesn't match, just fall back to /var in the target's stateroot.
+   */
+  if (g_strcmp0 (booted_stateroot, ostree_deployment_get_osname (deployment)) == 0)
+    {
+      base_dfd = AT_FDCWD;
+      base_path = "/var";
+    }
+  else
+    {
+      base_dfd = osdeploy_dfd;
+      base_path = "var";
+    }
+
+  return glnx_opendirat (base_dfd, base_path, TRUE, ret_fd, error);
+}
+
 static gboolean
 sysroot_finalize_deployment (OstreeSysroot     *self,
                              OstreeDeployment  *deployment,
@@ -2547,6 +2588,9 @@ sysroot_finalize_deployment (OstreeSysroot     *self,
   glnx_autofd int os_deploy_dfd = -1;
   if (!glnx_opendirat (self->sysroot_fd, osdeploypath, TRUE, &os_deploy_dfd, error))
     return FALSE;
+  glnx_autofd int var_dfd = -1;
+  if (!get_var_dfd (self, os_deploy_dfd, deployment, &var_dfd, error))
+    return FALSE;
 
   /* Ensure that the new deployment does not have /etc/.updated or
    * /var/.updated so that systemd ConditionNeedsUpdate=/etc|/var services run
@@ -2554,7 +2598,7 @@ sysroot_finalize_deployment (OstreeSysroot     *self,
    */
   if (!ot_ensure_unlinked_at (deployment_dfd, "etc/.updated", error))
     return FALSE;
-  if (!ot_ensure_unlinked_at (os_deploy_dfd, "var/.updated", error))
+  if (!ot_ensure_unlinked_at (var_dfd, ".updated", error))
     return FALSE;
 
   g_autoptr(OstreeSePolicy) sepolicy = ostree_sepolicy_new_at (deployment_dfd, cancellable, error);