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; }
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; }