static gboolean
run_script_in_bwrap_container (int rootfs_fd,
                               const char *name,
                               const char *scriptdesc,
                               const char *script,
                               GCancellable  *cancellable,
                               GError       **error)
{
  gboolean ret = FALSE;
  int i;
  const char *usr_links[] = {"lib", "lib32", "lib64", "bin", "sbin"};
  int estatus;
  char *rofiles_mnt = strdupa ("/tmp/rofiles-fuse.XXXXXX");
  const char *rofiles_argv[] = { "rofiles-fuse", "./usr", rofiles_mnt, NULL};
  const char *pkg_script = glnx_strjoina (name, ".", scriptdesc+1);
  const char *postscript_name = glnx_strjoina ("/", pkg_script);
  const char *postscript_path_container = glnx_strjoina ("/usr/", postscript_name);
  const char *postscript_path_host;
  gboolean mntpoint_created = FALSE;
  gboolean fuse_mounted = FALSE;
  g_autoptr(GPtrArray) bwrap_argv = g_ptr_array_new ();
  g_autoptr(GPtrArray) bwrap_argv_mallocd = g_ptr_array_new_with_free_func (g_free);
  GSpawnFlags bwrap_spawnflags = G_SPAWN_SEARCH_PATH;
  gboolean created_var_tmp = FALSE;

  if (!glnx_mkdtempat (AT_FDCWD, rofiles_mnt, 0700, error))
    goto out;

  mntpoint_created = TRUE;

  if (!g_spawn_sync (NULL, (char**)rofiles_argv, NULL, G_SPAWN_SEARCH_PATH,
                     child_setup_fchdir, GINT_TO_POINTER (rootfs_fd),
                     NULL, NULL, &estatus, error))
    goto out;
  if (!g_spawn_check_exit_status (estatus, error))
    {
      g_prefix_error (error, "Executing rofiles-fuse: ");
      goto out;
    }

  fuse_mounted = TRUE;

  postscript_path_host = glnx_strjoina (rofiles_mnt, "/", postscript_name);

  /* TODO - Create a pipe and send this to bwrap so it's inside the
   * tmpfs
   */
  if (!g_file_set_contents (postscript_path_host, script, -1, error))
    {
      g_prefix_error (error, "Writing script to %s: ", postscript_path_host);
      goto out;
    }
  if (chmod (postscript_path_host, 0755) != 0)
    {
      g_prefix_error (error, "chmod %s: ", postscript_path_host);
      goto out;
    }

  /* We need to make the mount point in the case where we're doing
   * package layering, since the host `/var` tree is empty.  We
   * *could* point at the real `/var`...but that seems
   * unnecessary/dangerous to me.  Daemons that need to perform data
   * migrations should do them as part of their systemd units and not
   * in %post.
   *
   * Another alternative would be to make a tmpfs with the compat
   * symlinks.
   */
  if (mkdirat (rootfs_fd, "var/tmp", 0755) < 0)
    {
      if (errno == EEXIST)
        ;
      else
        {
          glnx_set_error_from_errno (error);
          goto out;
        }
    }
  else
    created_var_tmp = TRUE;

  add_const_args (bwrap_argv,
                  WITH_BUBBLEWRAP_PATH,
                  "--bind", rofiles_mnt, "/usr",
                  "--dev", "/dev",
                  "--proc", "/proc",
                  "--dir", "/tmp",
                  "--chdir", "/",
                  /* Scripts can see a /var with compat links like alternatives */
                  "--ro-bind", "./var", "/var",
                  /* But no need to access persistent /tmp, so make it /tmp */
                  "--bind", "/tmp", "/var/tmp",
                  /* Allow RPM scripts to change the /etc defaults */
                  "--symlink", "usr/etc", "/etc",
                  "--ro-bind", "/sys/block", "/sys/block",
                  "--ro-bind", "/sys/bus", "/sys/bus",
                  "--ro-bind", "/sys/class", "/sys/class",
                  "--ro-bind", "/sys/dev", "/sys/dev",
                  "--ro-bind", "/sys/devices", "/sys/devices",
                  NULL);

  for (i = 0; i < G_N_ELEMENTS (usr_links); i++)
    {
      const char *subdir = usr_links[i];
      struct stat stbuf;
      char *path;

      if (!(fstatat (rootfs_fd, subdir, &stbuf, AT_SYMLINK_NOFOLLOW) == 0 && S_ISLNK (stbuf.st_mode)))
        continue;

      g_ptr_array_add (bwrap_argv, "--symlink");

      path = g_strconcat ("usr/", subdir, NULL);
      g_ptr_array_add (bwrap_argv_mallocd, path);
      g_ptr_array_add (bwrap_argv, path);

      path = g_strconcat ("/", subdir, NULL);
      g_ptr_array_add (bwrap_argv_mallocd, path);
      g_ptr_array_add (bwrap_argv, path);
    }

  { const char *debugscript = getenv ("RPMOSTREE_DEBUG_SCRIPT");
    if (g_strcmp0 (debugscript, pkg_script) == 0)
      {
        g_ptr_array_add (bwrap_argv, (char*)"/bin/bash");
        bwrap_spawnflags |= G_SPAWN_CHILD_INHERITS_STDIN;
      }
    else
      g_ptr_array_add (bwrap_argv, (char*)postscript_path_container);
  }
  g_ptr_array_add (bwrap_argv, NULL);

  if (!g_spawn_sync (NULL, (char**)bwrap_argv->pdata, NULL, bwrap_spawnflags,
                     child_setup_fchdir, GINT_TO_POINTER (rootfs_fd),
                     NULL, NULL, &estatus, error))
    {
      g_prefix_error (error, "Executing bwrap: ");
      goto out;
    }
  if (!g_spawn_check_exit_status (estatus, error))
    {
      g_prefix_error (error, "Executing bwrap: ");
      goto out;
    }

  ret = TRUE;
 out:
  if (fuse_mounted)
    {
      (void) unlink (postscript_path_host);
      fusermount_cleanup (rofiles_mnt);
    }
  if (mntpoint_created)
    (void) unlinkat (AT_FDCWD, rofiles_mnt, AT_REMOVEDIR);
  if (created_var_tmp)
    (void) unlinkat (rootfs_fd, "var/tmp", AT_REMOVEDIR);
  return ret;
}
Example #2
0
static gboolean
run_script_in_bwrap_container (int rootfs_fd,
                               const char *name,
                               const char *scriptdesc,
                               const char *script,
                               GCancellable  *cancellable,
                               GError       **error)
{
  gboolean ret = FALSE;
  char *rofiles_mnt = strdupa ("/tmp/rofiles-fuse.XXXXXX");
  const char *rofiles_argv[] = { "rofiles-fuse", "./usr", rofiles_mnt, NULL};
  const char *pkg_script = glnx_strjoina (name, ".", scriptdesc+1);
  const char *postscript_name = glnx_strjoina ("/", pkg_script);
  const char *postscript_path_container = glnx_strjoina ("/usr/", postscript_name);
  const char *postscript_path_host;
  gboolean mntpoint_created = FALSE;
  gboolean fuse_mounted = FALSE;
  g_autoptr(GPtrArray) bwrap_argv = g_ptr_array_new ();
  gboolean created_var_tmp = FALSE;

  if (!glnx_mkdtempat (AT_FDCWD, rofiles_mnt, 0700, error))
    goto out;

  mntpoint_created = TRUE;

  if (!rpmostree_run_sync_fchdir_setup ((char**)rofiles_argv, G_SPAWN_SEARCH_PATH,
                                        rootfs_fd, error))
    {
      g_prefix_error (error, "Executing rofiles-fuse: ");
      goto out;
    }

  fuse_mounted = TRUE;

  postscript_path_host = glnx_strjoina (rofiles_mnt, "/", postscript_name);

  /* TODO - Create a pipe and send this to bwrap so it's inside the
   * tmpfs
   */
  if (!g_file_set_contents (postscript_path_host, script, -1, error))
    {
      g_prefix_error (error, "Writing script to %s: ", postscript_path_host);
      goto out;
    }
  if (chmod (postscript_path_host, 0755) != 0)
    {
      g_prefix_error (error, "chmod %s: ", postscript_path_host);
      goto out;
    }

  /* We need to make the mount point in the case where we're doing
   * package layering, since the host `/var` tree is empty.  We
   * *could* point at the real `/var`...but that seems
   * unnecessary/dangerous to me.  Daemons that need to perform data
   * migrations should do them as part of their systemd units and not
   * in %post.
   *
   * Another alternative would be to make a tmpfs with the compat
   * symlinks.
   */
  if (mkdirat (rootfs_fd, "var/tmp", 0755) < 0)
    {
      if (errno == EEXIST)
        ;
      else
        {
          glnx_set_error_from_errno (error);
          goto out;
        }
    }
  else
    created_var_tmp = TRUE;

  bwrap_argv = rpmostree_bwrap_base_argv_new_for_rootfs (rootfs_fd, error);
  if (!bwrap_argv)
    goto out;

  rpmostree_ptrarray_append_strdup (bwrap_argv,
                  "--bind", rofiles_mnt, "/usr",
                  /* Scripts can see a /var with compat links like alternatives */
                  "--ro-bind", "./var", "/var",
                  /* But no need to access persistent /tmp, so make it /tmp */
                  "--bind", "/tmp", "/var/tmp",
                  /* Allow RPM scripts to change the /etc defaults */
                  "--symlink", "usr/etc", "/etc",
                  NULL);

  g_ptr_array_add (bwrap_argv, g_strdup (postscript_path_container));
  /* http://www.rpm.org/max-rpm/s1-rpm-inside-scripts.html#S3-RPM-INSIDE-PRE-SCRIPT */
  g_ptr_array_add (bwrap_argv, g_strdup ("1"));
  g_ptr_array_add (bwrap_argv, NULL);

  if (!rpmostree_run_bwrap_sync ((char**)bwrap_argv->pdata, rootfs_fd, error))
    {
      g_prefix_error (error, "Executing bwrap: ");
      goto out;
    }

  ret = TRUE;
 out:
  if (fuse_mounted)
    {
      (void) unlink (postscript_path_host);
      fusermount_cleanup (rofiles_mnt);
    }
  if (mntpoint_created)
    (void) unlinkat (AT_FDCWD, rofiles_mnt, AT_REMOVEDIR);
  if (created_var_tmp)
    (void) unlinkat (rootfs_fd, "var/tmp", AT_REMOVEDIR);
  return ret;
}