From 4116d56809feb0c7bafe0eb6cdf759cd1abafa4b Mon Sep 17 00:00:00 2001 From: Simon McVittie <smcv@collabora.com> Date: Thu, 18 Jun 2020 11:44:00 +0100 Subject: [PATCH] tree-copy: Split into its own translation unit Signed-off-by: Simon McVittie <smcv@collabora.com> --- src/meson.build | 2 + src/runtime.c | 1 + src/tree-copy.c | 173 +++++++++++++++++++++++++++++++++++++++++++++ src/tree-copy.h | 29 ++++++++ src/utils.c | 137 ----------------------------------- src/utils.h | 4 -- tests/cheap-copy.c | 1 + 7 files changed, 206 insertions(+), 141 deletions(-) create mode 100644 src/tree-copy.c create mode 100644 src/tree-copy.h diff --git a/src/meson.build b/src/meson.build index 0ac2f0a4f..27242f101 100644 --- a/src/meson.build +++ b/src/meson.build @@ -54,6 +54,8 @@ pressure_vessel_utils = static_library( 'glib-backports.h', 'resolve-in-sysroot.c', 'resolve-in-sysroot.h', + 'tree-copy.c', + 'tree-copy.h', 'utils.c', 'utils.h', ], diff --git a/src/runtime.c b/src/runtime.c index c7dd569f7..c221a4d59 100644 --- a/src/runtime.c +++ b/src/runtime.c @@ -36,6 +36,7 @@ #include "enumtypes.h" #include "flatpak-run-private.h" #include "resolve-in-sysroot.h" +#include "tree-copy.h" #include "utils.h" /* diff --git a/src/tree-copy.c b/src/tree-copy.c new file mode 100644 index 000000000..8666668a0 --- /dev/null +++ b/src/tree-copy.c @@ -0,0 +1,173 @@ +/* + * Contains code taken from Flatpak. + * + * Copyright © 2014-2019 Red Hat, Inc + * Copyright © 2017-2020 Collabora Ltd. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This program 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.1 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, see <http://www.gnu.org/licenses/>. + */ + +#include "tree-copy.h" + +#include <ftw.h> + +#include <glib.h> +#include <glib/gstdio.h> +#include <gio/gio.h> + +#include "libglnx/libglnx.h" + +#include "glib-backports.h" +#include "flatpak-bwrap-private.h" +#include "flatpak-utils-base-private.h" +#include "flatpak-utils-private.h" + +/* nftw() doesn't have a user_data argument so we need to use a global + * variable :-( */ +static struct +{ + gchar *source_root; + gchar *dest_root; + GError *error; +} nftw_data; + +static int +copy_tree_helper (const char *fpath, + const struct stat *sb, + int typeflag, + struct FTW *ftwbuf) +{ + size_t len; + const char *suffix; + g_autofree gchar *dest = NULL; + g_autofree gchar *target = NULL; + GError **error = &nftw_data.error; + + g_return_val_if_fail (g_str_has_prefix (fpath, nftw_data.source_root), 1); + + if (strcmp (fpath, nftw_data.source_root) == 0) + { + if (typeflag != FTW_D) + { + glnx_throw (error, "\"%s\" is not a directory", fpath); + return 1; + } + + if (!glnx_shutil_mkdir_p_at (-1, nftw_data.dest_root, + sb->st_mode & 07777, NULL, error)) + return 1; + + return 0; + } + + len = strlen (nftw_data.source_root); + g_return_val_if_fail (fpath[len] == '/', 1); + suffix = &fpath[len + 1]; + dest = g_build_filename (nftw_data.dest_root, suffix, NULL); + + switch (typeflag) + { + case FTW_D: + if (!glnx_shutil_mkdir_p_at (-1, dest, sb->st_mode & 07777, + NULL, error)) + return 1; + break; + + case FTW_SL: + target = glnx_readlinkat_malloc (-1, fpath, NULL, error); + + if (target == NULL) + return 1; + + if (symlink (target, dest) != 0) + { + glnx_throw_errno_prefix (error, + "Unable to create symlink at \"%s\"", + dest); + return 1; + } + break; + + case FTW_F: + /* Fast path: try to make a hard link. */ + if (link (fpath, dest) == 0) + break; + + /* Slow path: fall back to copying. + * + * This does a FICLONE or copy_file_range to get btrfs reflinks + * if possible, making the copy as cheap as cp --reflink=auto. + * + * Rather than second-guessing which errno values would result + * in link() failing but a copy succeeding, we just try it + * unconditionally - the worst that can happen is that this + * fails too. */ + if (!glnx_file_copy_at (AT_FDCWD, fpath, sb, + AT_FDCWD, dest, + GLNX_FILE_COPY_OVERWRITE, + NULL, error)) + { + glnx_prefix_error (error, "Unable to copy \"%s\" to \"%s\"", + fpath, dest); + return 1; + } + break; + + default: + glnx_throw (&nftw_data.error, + "Don't know how to handle ftw type flag %d at %s", + typeflag, fpath); + return 1; + } + + return 0; +} + +gboolean +pv_cheap_tree_copy (const char *source_root, + const char *dest_root, + GError **error) +{ + int res; + + g_return_val_if_fail (source_root != NULL, FALSE); + g_return_val_if_fail (dest_root != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + /* Can't run concurrently */ + g_return_val_if_fail (nftw_data.source_root == NULL, FALSE); + + nftw_data.source_root = flatpak_canonicalize_filename (source_root); + nftw_data.dest_root = flatpak_canonicalize_filename (dest_root); + nftw_data.error = NULL; + + res = nftw (nftw_data.source_root, copy_tree_helper, 100, FTW_PHYS); + + if (res == -1) + { + g_assert (nftw_data.error == NULL); + glnx_throw_errno_prefix (error, "Unable to copy \"%s\" to \"%s\"", + source_root, dest_root); + } + else if (res != 0) + { + g_propagate_error (error, g_steal_pointer (&nftw_data.error)); + } + + g_clear_pointer (&nftw_data.source_root, g_free); + g_clear_pointer (&nftw_data.dest_root, g_free); + g_assert (nftw_data.error == NULL); + return (res == 0); +} diff --git a/src/tree-copy.h b/src/tree-copy.h new file mode 100644 index 000000000..b15e13fad --- /dev/null +++ b/src/tree-copy.h @@ -0,0 +1,29 @@ +/* + * Contains code taken from Flatpak. + * + * Copyright © 2014-2019 Red Hat, Inc + * Copyright © 2017-2020 Collabora Ltd. + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * 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.1 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, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <glib.h> + +gboolean pv_cheap_tree_copy (const char *source_root, + const char *dest_root, + GError **error); diff --git a/src/utils.c b/src/utils.c index 95f3bf803..e646399bd 100644 --- a/src/utils.c +++ b/src/utils.c @@ -286,143 +286,6 @@ pv_hash_table_get_arbitrary_key (GHashTable *table) return NULL; } -/* nftw() doesn't have a user_data argument so we need to use a global - * variable :-( */ -static struct -{ - gchar *source_root; - gchar *dest_root; - GError *error; -} nftw_data; - -static int -copy_tree_helper (const char *fpath, - const struct stat *sb, - int typeflag, - struct FTW *ftwbuf) -{ - size_t len; - const char *suffix; - g_autofree gchar *dest = NULL; - g_autofree gchar *target = NULL; - GError **error = &nftw_data.error; - - g_return_val_if_fail (g_str_has_prefix (fpath, nftw_data.source_root), 1); - - if (strcmp (fpath, nftw_data.source_root) == 0) - { - if (typeflag != FTW_D) - { - glnx_throw (error, "\"%s\" is not a directory", fpath); - return 1; - } - - if (!glnx_shutil_mkdir_p_at (-1, nftw_data.dest_root, - sb->st_mode & 07777, NULL, error)) - return 1; - - return 0; - } - - len = strlen (nftw_data.source_root); - g_return_val_if_fail (fpath[len] == '/', 1); - suffix = &fpath[len + 1]; - dest = g_build_filename (nftw_data.dest_root, suffix, NULL); - - switch (typeflag) - { - case FTW_D: - if (!glnx_shutil_mkdir_p_at (-1, dest, sb->st_mode & 07777, - NULL, error)) - return 1; - break; - - case FTW_SL: - target = glnx_readlinkat_malloc (-1, fpath, NULL, error); - - if (target == NULL) - return 1; - - if (symlink (target, dest) != 0) - { - glnx_throw_errno_prefix (error, - "Unable to create symlink at \"%s\"", - dest); - return 1; - } - break; - - case FTW_F: - /* Fast path: try to make a hard link. */ - if (link (fpath, dest) == 0) - break; - - /* Slow path: fall back to copying. - * - * This does a FICLONE or copy_file_range to get btrfs reflinks - * if possible, making the copy as cheap as cp --reflink=auto. - * - * Rather than second-guessing which errno values would result - * in link() failing but a copy succeeding, we just try it - * unconditionally - the worst that can happen is that this - * fails too. */ - if (!glnx_file_copy_at (AT_FDCWD, fpath, sb, - AT_FDCWD, dest, - GLNX_FILE_COPY_OVERWRITE, - NULL, error)) - { - glnx_prefix_error (error, "Unable to copy \"%s\" to \"%s\"", - fpath, dest); - return 1; - } - break; - - default: - glnx_throw (&nftw_data.error, - "Don't know how to handle ftw type flag %d at %s", - typeflag, fpath); - return 1; - } - - return 0; -} - -gboolean -pv_cheap_tree_copy (const char *source_root, - const char *dest_root, - GError **error) -{ - int res; - - g_return_val_if_fail (source_root != NULL, FALSE); - g_return_val_if_fail (dest_root != NULL, FALSE); - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - /* Can't run concurrently */ - g_return_val_if_fail (nftw_data.source_root == NULL, FALSE); - - nftw_data.source_root = flatpak_canonicalize_filename (source_root); - nftw_data.dest_root = flatpak_canonicalize_filename (dest_root); - nftw_data.error = NULL; - - res = nftw (nftw_data.source_root, copy_tree_helper, 100, FTW_PHYS); - - if (res == -1) - { - g_assert (nftw_data.error == NULL); - glnx_throw_errno_prefix (error, "Unable to copy \"%s\" to \"%s\"", - source_root, dest_root); - } - else if (res != 0) - { - g_propagate_error (error, g_steal_pointer (&nftw_data.error)); - } - - g_clear_pointer (&nftw_data.source_root, g_free); - g_clear_pointer (&nftw_data.dest_root, g_free); - g_assert (nftw_data.error == NULL); - return (res == 0); -} - static gint ftw_remove (const gchar *path, const struct stat *sb, diff --git a/src/utils.h b/src/utils.h index 3d89b7ced..0966efedb 100644 --- a/src/utils.h +++ b/src/utils.h @@ -54,10 +54,6 @@ gchar *pv_capture_output (const char * const * argv, gpointer pv_hash_table_get_arbitrary_key (GHashTable *table); -gboolean pv_cheap_tree_copy (const char *source_root, - const char *dest_root, - GError **error); - gboolean pv_rm_rf (const char *directory); gboolean pv_boolean_environment (const gchar *name, diff --git a/tests/cheap-copy.c b/tests/cheap-copy.c index 9a5bf686c..912da297e 100644 --- a/tests/cheap-copy.c +++ b/tests/cheap-copy.c @@ -26,6 +26,7 @@ #include "libglnx/libglnx.h" #include "glib-backports.h" +#include "tree-copy.h" #include "utils.h" static GOptionEntry options[] = -- GitLab