From c5b1921279850f94012437d454070ae4403ad8be Mon Sep 17 00:00:00 2001 From: bbhtt Date: Sun, 30 Aug 2026 09:48:41 +0530 Subject: [PATCH] builder-flatpak-utils: Switch flatpak_cp_a to f-based operations This additionally drops the MOVE and NO_CHOWN cp flags since they were either not used anywhere or were the default being used. --- src/builder-flatpak-utils.c | 264 +++++++++++++++++++----------------- src/builder-flatpak-utils.h | 16 +-- src/builder-source-dir.c | 4 +- 3 files changed, 148 insertions(+), 136 deletions(-) diff --git a/src/builder-flatpak-utils.c b/src/builder-flatpak-utils.c index 2829e7eb..ca2732dc 100644 --- a/src/builder-flatpak-utils.c +++ b/src/builder-flatpak-utils.c @@ -36,12 +36,18 @@ #include #include #include +#include #include #include "libglnx.h" #include #include +typedef struct { + dev_t dev; + ino_t ino; +} CpSkipFilesInode; + GFile * flatpak_file_new_tmp_in (GFile *dir, @@ -760,158 +766,166 @@ flatpak_file_is_in (GFile *file, g_file_has_prefix (canonical_file, canonical_toplevel); } -gboolean -flatpak_cp_a (GFile *src, - GFile *dest, - GFile *keep_in_toplevel, - FlatpakCpFlags flags, - GPtrArray *skip_files, - GCancellable *cancellable, - GError **error) -{ - gboolean ret = FALSE; - GFileEnumerator *enumerator = NULL; - GFileInfo *src_info = NULL; - GFile *dest_child = NULL; - int dest_dfd = -1; - gboolean merge = (flags & FLATPAK_CP_FLAGS_MERGE) != 0; - gboolean no_chown = (flags & FLATPAK_CP_FLAGS_NO_CHOWN) != 0; - gboolean move = (flags & FLATPAK_CP_FLAGS_MOVE) != 0; - g_autoptr(GFileInfo) child_info = NULL; - GError *temp_error = NULL; - int r; - - enumerator = g_file_enumerate_children (src, "standard::type,standard::name,unix::uid,unix::gid,unix::mode", - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable, error); - if (!enumerator) - goto out; - - src_info = g_file_query_info (src, "standard::name,unix::mode,unix::uid,unix::gid," \ - "time::modified,time::modified-usec,time::access,time::access-usec", - G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, - cancellable, error); - if (!src_info) - goto out; - - do - r = mkdir (flatpak_file_get_path_cached (dest), 0755); - while (G_UNLIKELY (r == -1 && errno == EINTR)); - if (r == -1) - { - if (!merge || errno != EEXIST) - { - glnx_set_error_from_errno (error); - goto out; - } +static gboolean +flatpak_cp_a_at (int src_dir_fd, + int dst_parent_fd, + const char *dst_name, + const GArray *skip_inodes, + FlatpakCpFlags flags, + GCancellable *cancellable, + GError **error) +{ + const gboolean merge = (flags & FLATPAK_CP_FLAGS_MERGE) != 0; - /* When merging, ensure the new dir is inside the toplevel instead of a symlink outside */ - if (keep_in_toplevel != NULL && !flatpak_file_is_in (dest, keep_in_toplevel)) - { - flatpak_fail (error, "Recursive copy outside destination bounds"); - goto out; - } - } + struct stat src_stat; + glnx_autofd int dst_dir_fd = -1; + g_auto(GLnxDirFdIterator) dir_iter = { 0, }; - if (!glnx_opendirat (AT_FDCWD, flatpak_file_get_path_cached (dest), TRUE, - &dest_dfd, error)) - goto out; + if (fstat (src_dir_fd, &src_stat) != 0) + return glnx_throw_errno_prefix (error, "fstat"); - if (!no_chown) + if (TEMP_FAILURE_RETRY (mkdirat (dst_parent_fd, dst_name, 0755)) < 0) { - do - r = fchown (dest_dfd, - g_file_info_get_attribute_uint32 (src_info, "unix::uid"), - g_file_info_get_attribute_uint32 (src_info, "unix::gid")); - while (G_UNLIKELY (r == -1 && errno == EINTR)); - if (r == -1) - { - glnx_set_error_from_errno (error); - goto out; - } + if (!(merge && errno == EEXIST)) + return glnx_throw_errno_prefix (error, "mkdirat(%s)", dst_name); } - do - r = fchmod (dest_dfd, g_file_info_get_attribute_uint32 (src_info, "unix::mode")); - while (G_UNLIKELY (r == -1 && errno == EINTR)); + if (!glnx_opendirat (dst_parent_fd, dst_name, FALSE, &dst_dir_fd, error)) + return FALSE; - if (dest_dfd != -1) - { - (void) close (dest_dfd); - dest_dfd = -1; - } + if (TEMP_FAILURE_RETRY (fchmod (dst_dir_fd, src_stat.st_mode & 07777)) != 0) + return glnx_throw_errno_prefix (error, "fchmod"); - while ((child_info = g_file_enumerator_next_file (enumerator, cancellable, &temp_error))) + if (!glnx_dirfd_iterator_init_at (src_dir_fd, ".", FALSE, &dir_iter, error)) + return FALSE; + + while (TRUE) { - const char *name = g_file_info_get_name (child_info); - g_autoptr(GFile) src_child = g_file_get_child (src, name); - gboolean skip = FALSE; - int i; + struct dirent *dir_entry; + const char *entry_name; + gboolean should_skip = FALSE; + glnx_autofd int chase_fd = -1; + struct glnx_statx stx; + + if (!glnx_dirfd_iterator_next_dent (&dir_iter, &dir_entry, + cancellable, error)) + return FALSE; + + if (dir_entry == NULL) + break; - for (i = 0; skip_files != NULL && i < skip_files->len; i++) + entry_name = dir_entry->d_name; + + chase_fd = glnx_chase_and_statxat (dir_iter.fd, entry_name, + GLNX_CHASE_NOFOLLOW, + GLNX_STATX_TYPE | GLNX_STATX_INO, + &stx, error); + if (chase_fd < 0) + return FALSE; + + if (skip_inodes != NULL && skip_inodes->len > 0) { - if (g_file_equal (src_child, g_ptr_array_index (skip_files, i))) + for (size_t i = 0; i < skip_inodes->len; i++) { - skip = TRUE; - break; + const CpSkipFilesInode *skip = &g_array_index (skip_inodes, CpSkipFilesInode, i); + + if (stx.stx_ino == skip->ino && + stx.stx_dev_major == major (skip->dev) && + stx.stx_dev_minor == minor (skip->dev)) + { + should_skip = TRUE; + break; + } } } - if (dest_child) - g_object_unref (dest_child); - dest_child = g_file_get_child (dest, name); + if (should_skip) + continue; - if (skip) - { - /* skip src */ - } - else if (g_file_info_get_file_type (child_info) == G_FILE_TYPE_DIRECTORY) + if (S_ISDIR (stx.stx_mode)) { - if (!flatpak_cp_a (src_child, dest_child, keep_in_toplevel, flags, skip_files, - cancellable, error)) - goto out; + glnx_autofd int src_child_fd = -1; + + src_child_fd = glnx_fd_reopen (chase_fd, O_RDONLY | O_DIRECTORY, error); + if (src_child_fd < 0) + return glnx_prefix_error (error, "Failed to reopen directory %s", entry_name); + + if (!flatpak_cp_a_at (src_child_fd, dst_dir_fd, entry_name, skip_inodes, + flags, cancellable, error)) + return FALSE; } else { - (void) unlink (flatpak_file_get_path_cached (dest_child)); - GFileCopyFlags copyflags = G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS; - if (!no_chown) - copyflags |= G_FILE_COPY_ALL_METADATA; - if (move) - { - if (!g_file_move (src_child, dest_child, copyflags, - cancellable, NULL, NULL, error)) - goto out; - } - else - { - if (!g_file_copy (src_child, dest_child, copyflags, - cancellable, NULL, NULL, error)) - goto out; - } + GLnxFileCopyFlags cp_flags = GLNX_FILE_COPY_OVERWRITE | GLNX_FILE_COPY_NOCHOWN; + + (void) unlinkat (dst_dir_fd, entry_name, 0); + + if (!glnx_file_copy_at (dir_iter.fd, entry_name, NULL, + dst_dir_fd, entry_name, cp_flags, + cancellable, error)) + return FALSE; } + } - g_clear_object (&child_info); + return TRUE; +} + +gboolean +flatpak_cp_a (GFile *src, + GFile *dest, + GFile *keep_in_toplevel, + FlatpakCpFlags flags, + GPtrArray *skip_files, + GCancellable *cancellable, + GError **error) +{ + glnx_autofd int src_dfd = -1; + glnx_autofd int dest_parent_fd = -1; + const char *src_path; + const char *dest_path; + g_autofree char *dest_dirname = NULL; + g_autofree char *dest_basename = NULL; + g_autoptr(GArray) skip_inodes = NULL; + + if (keep_in_toplevel != NULL && !flatpak_file_is_in (dest, keep_in_toplevel)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED, + "Recursive copy outside destination"); + return FALSE; } - if (temp_error != NULL) + src_path = flatpak_file_get_path_cached (src); + dest_path = flatpak_file_get_path_cached (dest); + + if (!glnx_opendirat (AT_FDCWD, src_path, TRUE, &src_dfd, error)) + return FALSE; + + dest_dirname = g_path_get_dirname (dest_path); + dest_basename = g_path_get_basename (dest_path); + + dest_parent_fd = glnx_chaseat (AT_FDCWD, dest_dirname, GLNX_CHASE_MUST_BE_DIRECTORY, error); + if (dest_parent_fd < 0) + return FALSE; + + if (skip_files != NULL && skip_files->len > 0) { - g_propagate_error (error, temp_error); - goto out; + skip_inodes = g_array_sized_new (FALSE, FALSE, sizeof (CpSkipFilesInode), skip_files->len); + for (size_t i = 0; i < skip_files->len; i++) + { + GFile *skip_file = g_ptr_array_index (skip_files, i); + const char *skip_path = flatpak_file_get_path_cached (skip_file); + struct stat st; + + if (skip_path != NULL && stat (skip_path, &st) == 0) + { + CpSkipFilesInode si = { .dev = st.st_dev, .ino = st.st_ino }; + g_array_append_val (skip_inodes, si); + } + } } - if (move && - !g_file_delete (src, NULL, error)) - goto out; - - ret = TRUE; -out: - if (dest_dfd != -1) - (void) close (dest_dfd); - g_clear_object (&src_info); - g_clear_object (&enumerator); - g_clear_object (&dest_child); - return ret; + return flatpak_cp_a_at (src_dfd, dest_parent_fd, dest_basename, + skip_inodes, flags, cancellable, error); } gboolean diff --git a/src/builder-flatpak-utils.h b/src/builder-flatpak-utils.h index 5f44f114..97d1af31 100644 --- a/src/builder-flatpak-utils.h +++ b/src/builder-flatpak-utils.h @@ -195,17 +195,15 @@ gboolean flatpak_file_is_in (GFile *file, typedef enum { FLATPAK_CP_FLAGS_NONE = 0, FLATPAK_CP_FLAGS_MERGE = 1<<0, - FLATPAK_CP_FLAGS_NO_CHOWN = 1<<1, - FLATPAK_CP_FLAGS_MOVE = 1<<2, } FlatpakCpFlags; -gboolean flatpak_cp_a (GFile *src, - GFile *dest, - GFile *keep_in_toplevel, - FlatpakCpFlags flags, - GPtrArray *skip_files, - GCancellable *cancellable, - GError **error); +gboolean flatpak_cp_a (GFile *src, + GFile *dest, + GFile *keep_in_toplevel, + FlatpakCpFlags flags, + GPtrArray *skip_files, + GCancellable *cancellable, + GError **error); gboolean flatpak_zero_mtime (int parent_dfd, const char *rel_path, diff --git a/src/builder-source-dir.c b/src/builder-source-dir.c index 671c7be6..0a858c43 100644 --- a/src/builder-source-dir.c +++ b/src/builder-source-dir.c @@ -215,7 +215,7 @@ builder_source_dir_extract (BuilderSource *source, skip = builder_source_dir_get_skip (source, context); if (!flatpak_cp_a (src, dest, source_dir, - FLATPAK_CP_FLAGS_MERGE|FLATPAK_CP_FLAGS_NO_CHOWN, + FLATPAK_CP_FLAGS_MERGE, skip, NULL, error)) return FALSE; @@ -251,7 +251,7 @@ builder_source_dir_bundle (BuilderSource *source, skip = builder_source_dir_get_skip (source, context); g_mkdir_with_parents (flatpak_file_get_path_cached (dest), 0755); if (!flatpak_cp_a (src, dest, NULL, - FLATPAK_CP_FLAGS_MERGE|FLATPAK_CP_FLAGS_NO_CHOWN, + FLATPAK_CP_FLAGS_MERGE, skip, NULL, error)) return FALSE;