From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by finch.gentoo.org (Postfix) with ESMTPS id F1945158003 for ; Sun, 26 Dec 2021 14:00:00 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 887392BC02A; Sun, 26 Dec 2021 13:59:58 +0000 (UTC) Received: from smtp.gentoo.org (dev.gentoo.org [IPv6:2001:470:ea4a:1:5054:ff:fec7:86e4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id 80E1F2BC02A for ; Sun, 26 Dec 2021 13:59:57 +0000 (UTC) Received: from oystercatcher.gentoo.org (oystercatcher.gentoo.org [148.251.78.52]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id 7545C342EA5 for ; Sun, 26 Dec 2021 13:59:55 +0000 (UTC) Received: from localhost.localdomain (localhost [IPv6:::1]) by oystercatcher.gentoo.org (Postfix) with ESMTP id D0C8B264 for ; Sun, 26 Dec 2021 13:59:53 +0000 (UTC) From: "Fabian Groffen" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "Fabian Groffen" Message-ID: <1640526924.ed8a0a8694de1a83a29041104de162ff63e5f8c6.grobian@gentoo> Subject: [gentoo-commits] proj/portage-utils:master commit in: / X-VCS-Repository: proj/portage-utils X-VCS-Files: qmerge.c X-VCS-Directories: / X-VCS-Committer: grobian X-VCS-Committer-Name: Fabian Groffen X-VCS-Revision: ed8a0a8694de1a83a29041104de162ff63e5f8c6 X-VCS-Branch: master Date: Sun, 26 Dec 2021 13:59:53 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply X-Archives-Salt: 467005b5-ab53-4c06-89c4-676a65a59b6c X-Archives-Hash: 1e60f791ccd98f849b5a861961196250 commit: ed8a0a8694de1a83a29041104de162ff63e5f8c6 Author: Fabian Groffen gentoo org> AuthorDate: Sun Dec 26 13:55:24 2021 +0000 Commit: Fabian Groffen gentoo org> CommitDate: Sun Dec 26 13:55:24 2021 +0000 URL: https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=ed8a0a86 qmerge: support cross-device merges also for VDB #816141 As originally suggested by Barnabás Virágh, but in this implementation using move_file, which is now shared code between pkg merge and the putting the vdb in place. Bug: https://bugs.gentoo.org/816141 Signed-off-by: Fabian Groffen gentoo.org> qmerge.c | 115 ++++++++++++++++++++------------------------------------------- 1 file changed, 37 insertions(+), 78 deletions(-) diff --git a/qmerge.c b/qmerge.c index 091b6e1..1ad7179 100644 --- a/qmerge.c +++ b/qmerge.c @@ -22,6 +22,7 @@ #include "atom.h" #include "copy_file.h" +#include "move_file.h" #include "contents.h" #include "eat_file.h" #include "hash.h" @@ -320,16 +321,6 @@ crossmount_rm(const char *fname, const struct stat * const st, rm_rf_at(fd, fname); } -static int -q_merge_filter_self_parent(const struct dirent *de) -{ - if (de->d_name[0] == '.' && (de->d_name[1] == '\0' || - (de->d_name[1] == '.' && de->d_name[2] == '\0'))) - return 0; - - return 1; -} - enum inc_exc { INCLUDE = 1, EXCLUDE = 2 }; static void @@ -353,7 +344,7 @@ install_mask_check_dir( #endif char *npth = qpth + strlen(qpth); - cnt = scandirat(fd, ".", &files, q_merge_filter_self_parent, alphasort); + cnt = scandirat(fd, ".", &files, filter_self_parent, alphasort); for (j = 0; j < cnt; j++) { mode = child_mode = parent_mode; for (i = 0; i < maskc; i++) { @@ -820,7 +811,7 @@ merge_tree_at(int fd_src, const char *src, int fd_dst, const char *dst, while ((de = readdir(dir)) != NULL) { const char *name = de->d_name; - if (!strcmp(name, ".") || !strcmp(name, "..")) + if (filter_self_parent(de) == 0) continue; /* Build up the full path for this entry */ @@ -866,10 +857,8 @@ merge_tree_at(int fd_src, const char *src, int fd_dst, const char *dst, unlinkat(subfd_dst, name, AT_REMOVEDIR); } else if (S_ISREG(st.st_mode)) { /* Migrate a file */ - struct timespec times[2]; - int fd_srcf, fd_dstf; char *hash; - const char *tmpname, *dname; + const char *dname; char buf[_Q_PATH_MAX * 2]; struct stat ignore; @@ -905,66 +894,8 @@ merge_tree_at(int fd_src, const char *src, int fd_dst, const char *dst, if (pretend) continue; - /* First try fast path -- src/dst are same device */ - if (renameat(subfd_src, dname, subfd_dst, name) == 0) - continue; - - /* Fall back to slow path -- manual read/write */ - fd_srcf = openat(subfd_src, name, O_RDONLY|O_CLOEXEC); - if (fd_srcf < 0) { - warnp("could not read %s", cpath); - continue; - } - - /* Do not write the file in place ... - * will fail with files that are in use. - * XXX: Should we make this random ? - */ - tmpname = ".qmerge.update"; - fd_dstf = openat(subfd_dst, tmpname, - O_WRONLY|O_CLOEXEC|O_CREAT|O_TRUNC, st.st_mode); - if (fd_dstf < 0) { - warnp("could not write %s", cpath); - close(fd_srcf); - continue; - } - - /* Make sure owner/mode is sane before we write out data */ - if (fchown(fd_dstf, st.st_uid, st.st_gid)) { - warnp("could not set ownership (%zu/%zu) for %s", - (size_t)st.st_uid, (size_t)st.st_gid, cpath); - continue; - } - if (fchmod(fd_dstf, st.st_mode)) { - warnp("could not set permission (%u) for %s", - (int)st.st_mode, cpath); - continue; - } - - /* Do the actual data copy */ - if (copy_file_fd(fd_srcf, fd_dstf)) { - warnp("could not write %s", cpath); - if (ftruncate(fd_dstf, 0)) { - /* don't care */; - } - close(fd_srcf); - close(fd_dstf); - continue; - } - - /* Preserve the file times */ - times[0] = get_stat_mtime(&st); - times[1] = get_stat_mtime(&st); - futimens(fd_dstf, times); - - close(fd_srcf); - close(fd_dstf); - - /* Move the new tmp dst file to the right place */ - if (renameat(subfd_dst, tmpname, subfd_dst, dname)) { - warnp("could not rename %s to %s", tmpname, cpath); - continue; - } + if (move_file(subfd_src, name, subfd_dst, dname, &st) != 0) + warnp("failed to move file from %s", cpath); } else if (S_ISLNK(st.st_mode)) { /* Migrate a symlink */ size_t len = st.st_size; @@ -1000,7 +931,7 @@ merge_tree_at(int fd_src, const char *src, int fd_dst, const char *dst, } struct timespec times[2]; - times[0] = get_stat_mtime(&st); + times[0] = get_stat_atime(&st); times[1] = get_stat_mtime(&st); utimensat(subfd_dst, name, times, AT_SYMLINK_NOFOLLOW); } else { @@ -1520,8 +1451,36 @@ pkg_merge(int level, const depend_atom *qatom, const tree_match_ctx *mpkg) mkdir_p(buf, 0755); strcat(buf, mpkg->atom->PF); rm_rf(buf); /* get rid of existing dir, empty dir is fine */ - if (rename("vdb", buf) != 0) - warn("failed to move 'vdb' to '%s': %s", buf, strerror(errno)); + if (rename("vdb", buf) != 0) { + struct stat vst; + int src_fd; + int dst_fd; + int cnt; + int vi; + struct dirent **files; + + /* e.g. in case of cross-device rename, try copy+delete */ + if ((src_fd = open("vdb", O_RDONLY|O_CLOEXEC|O_PATH)) < 0 || + fstat(src_fd, &vst) != 0 || + mkdir_p(buf, vst.st_mode) != 0 || + (dst_fd = open(buf, O_RDONLY|O_CLOEXEC|O_PATH)) < 0 || + (cnt = scandirat(src_fd, ".", + &files, filter_self_parent, NULL)) < 0) + { + warn("cannot stat 'vdb' or create '%s', huh?", buf); + } else { + /* for now we assume the VDB is a flat directory, e.g. + * there are no subdirs */ + for (vi = 0; vi < cnt; vi++) { + if (move_file(src_fd, files[vi]->d_name, + dst_fd, files[vi]->d_name, + NULL) != 0) + warn("failed to move 'vdb/%s' to '%s': %s", + files[vi]->d_name, buf, strerror(errno)); + } + scandir_free(files, cnt); + } + } } /* clean up our local temp dir */