2f13d7
From fd8ffffced543e248847d63e9375fb95584f998d Mon Sep 17 00:00:00 2001
2f13d7
From: chantra <chantr4@gmail.com>
2f13d7
Date: Fri, 11 Feb 2022 18:09:47 -0800
2f13d7
Subject: [PATCH 22/30] [reflink] fix support for hardlinks
2f13d7
2f13d7
a `suffix` made of ";${tid}" is now used until the changes are commited. As such, when
2f13d7
linking the file, we should link to the suffixed file.
2f13d7
2f13d7
The `suffix` is currently internal only to rpmPackageFilesInstall and there is
2f13d7
no obvious way to use data from struct `filedata_s` without exposing it to
2f13d7
a bunch of other places such as rpmplugin*.
2f13d7
We already keep track of the first file index. We used to use this to fetch the
2f13d7
file name, but due to suffix, the file is not named correctly, e.g it
2f13d7
has the name we will want at the end, once the transaction is commited,
2f13d7
not the temporary name.
2f13d7
Instead of re-implementing a local suffix that may change over time as
2f13d7
the implementation evolves, we can store the name of the file we should link to
2f13d7
in the hash instead of the index, which we were then using to fetch the
2f13d7
file name.
2f13d7
When hardlinking, we can then retrieve the name of the target file
2f13d7
instead of the index, like we previously did, and hardlink to that
2f13d7
without any issues.
2f13d7
Add a test to validate that reflink can handle hardlinking.
2f13d7
2f13d7
Originally, the test added would fail with:
2f13d7
2f13d7
```
2f13d7
error: reflink: Unable to hard link /foo/hello -> /foo/hello-bar;6207219c due to No such file or directory
2f13d7
error: Plugin reflink: hook fsm_file_install failed
2f13d7
error: unpacking of archive failed on file /foo/hello-bar;6207219c:
2f13d7
cpio: (error 0x2)
2f13d7
error: hlinktest-1.0-1.noarch: install failed
2f13d7
./rpm2extents.at:114: exit code was 1, expected 0
2f13d7
499. rpm2extents.at:112: 499. reflink hardlink package
2f13d7
(rpm2extents.at:112): FAILED (rpm2extents.at:114)
2f13d7
```
2f13d7
2f13d7
After this change, the test passes.
2f13d7
---
2f13d7
 plugins/reflink.c    | 19 ++++++++-----------
2f13d7
 tests/rpm2extents.at | 15 +++++++++++++++
2f13d7
 2 files changed, 23 insertions(+), 11 deletions(-)
2f13d7
2f13d7
diff --git a/plugins/reflink.c b/plugins/reflink.c
2f13d7
index d5e6db27a..4fc1d74d1 100644
2f13d7
--- a/plugins/reflink.c
2f13d7
+++ b/plugins/reflink.c
2f13d7
@@ -29,7 +29,7 @@
2f13d7
 #undef HTDATATYPE
2f13d7
 #define HASHTYPE inodeIndexHash
2f13d7
 #define HTKEYTYPE rpm_ino_t
2f13d7
-#define HTDATATYPE int
2f13d7
+#define HTDATATYPE const char *
2f13d7
 #include "lib/rpmhash.H"
2f13d7
 #include "lib/rpmhash.C"
2f13d7
 
2f13d7
@@ -163,7 +163,7 @@ static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) {
2f13d7
 	    return RPMRC_FAIL;
2f13d7
 	}
2f13d7
 	state->inodeIndexes = inodeIndexHashCreate(
2f13d7
-	    state->keys, inodeId, inodeCmp, NULL, NULL
2f13d7
+	    state->keys, inodeId, inodeCmp, NULL, (inodeIndexHashFreeData)rfree
2f13d7
 	);
2f13d7
     }
2f13d7
 
2f13d7
@@ -226,7 +226,7 @@ static rpmRC reflink_fsm_file_install(rpmPlugin plugin, rpmfi fi, const char* pa
2f13d7
     struct file_clone_range fcr;
2f13d7
     rpm_loff_t size;
2f13d7
     int dst, rc;
2f13d7
-    int *hlix;
2f13d7
+    const char **hl_target = NULL;
2f13d7
 
2f13d7
     reflink_state state = rpmPluginGetData(plugin);
2f13d7
     if (state->table == NULL) {
2f13d7
@@ -243,18 +243,15 @@ static rpmRC reflink_fsm_file_install(rpmPlugin plugin, rpmfi fi, const char* pa
2f13d7
 	/* check for hard link entry in table. GetEntry overwrites hlix with
2f13d7
 	 * the address of the first match.
2f13d7
 	 */
2f13d7
-	if (inodeIndexHashGetEntry(state->inodeIndexes, inode, &hlix, NULL,
2f13d7
-	                           NULL)) {
2f13d7
+	if (inodeIndexHashGetEntry(state->inodeIndexes, inode, &hl_target,
2f13d7
+				   NULL, NULL)) {
2f13d7
 	    /* entry is in table, use hard link */
2f13d7
-	    char *fn = rpmfilesFN(state->files, hlix[0]);
2f13d7
-	    if (link(fn, path) != 0) {
2f13d7
+	    if (link(hl_target[0], path) != 0) {
2f13d7
 		rpmlog(RPMLOG_ERR,
2f13d7
 		       _("reflink: Unable to hard link %s -> %s due to %s\n"),
2f13d7
-		       fn, path, strerror(errno));
2f13d7
-		free(fn);
2f13d7
+		       hl_target[0], path, strerror(errno));
2f13d7
 		return RPMRC_FAIL;
2f13d7
 	    }
2f13d7
-	    free(fn);
2f13d7
 	    return RPMRC_PLUGIN_CONTENTS;
2f13d7
 	}
2f13d7
 	/* if we didn't hard link, then we'll track this inode as being
2f13d7
@@ -262,7 +259,7 @@ static rpmRC reflink_fsm_file_install(rpmPlugin plugin, rpmfi fi, const char* pa
2f13d7
 	 */
2f13d7
 	if (rpmfiFNlink(fi) > 1) {
2f13d7
 	    /* minor optimization: only store files with more than one link */
2f13d7
-	    inodeIndexHashAddEntry(state->inodeIndexes, inode, rpmfiFX(fi));
2f13d7
+	    inodeIndexHashAddEntry(state->inodeIndexes, inode, rstrdup(path));
2f13d7
 	}
2f13d7
 	/* derived from wfd_open in fsm.c */
2f13d7
 	mode_t old_umask = umask(0577);
2f13d7
diff --git a/tests/rpm2extents.at b/tests/rpm2extents.at
2f13d7
index fa124ac03..5c66de7f6 100644
2f13d7
--- a/tests/rpm2extents.at
2f13d7
+++ b/tests/rpm2extents.at
2f13d7
@@ -123,3 +123,18 @@ test -f ${RPMTEST}/usr/bin/hello
2f13d7
 [],
2f13d7
 [])
2f13d7
 AT_CLEANUP
2f13d7
+
2f13d7
+AT_SETUP([reflink hardlink package])
2f13d7
+AT_KEYWORDS([reflink hardlink])
2f13d7
+AT_SKIP_IF([$REFLINK_DISABLED])
2f13d7
+AT_CHECK([
2f13d7
+RPMDB_INIT
2f13d7
+
2f13d7
+PKG=hlinktest-1.0-1.noarch.rpm
2f13d7
+runroot_other cat /data/RPMS/${PKG} | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/${PKG} 2> /dev/null
2f13d7
+runroot_plugins rpm -i --nodigest --nodeps --undefine=%__transaction_dbus_announce /tmp/${PKG}
2f13d7
+],
2f13d7
+[0],
2f13d7
+[],
2f13d7
+[])
2f13d7
+AT_CLEANUP
2f13d7
-- 
2f13d7
2.35.1
2f13d7