commit: 6449a3d248692b922620a64aa27785fafb449ab1
Author: Fabian Groffen <grobian <AT> gentoo <DOT> org>
AuthorDate: Sun Jan 4 13:08:08 2026 +0000
Commit: Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Sun Jan 4 13:08:08 2026 +0000
URL: https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=6449a3d2
qpkg: properly retain hardlinks in gpkg format
Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org>
qpkg.c | 47 ++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 44 insertions(+), 3 deletions(-)
diff --git a/qpkg.c b/qpkg.c
index eda01fb..4f78a85 100644
--- a/qpkg.c
+++ b/qpkg.c
@@ -251,6 +251,8 @@ qgpkg_make(tree_pkg_ctx *pkg, qpkg_cb_args *args)
#ifdef ENABLE_GPKG
struct archive *a;
struct archive_entry *entry;
+ struct archive_entry *sparse;
+ struct archive_entry_linkresolver *lres;
struct stat st;
struct dirent **files = NULL;
char tmpdir[BUFSIZE];
@@ -381,6 +383,8 @@ qgpkg_make(tree_pkg_ctx *pkg, qpkg_cb_args *args)
filter = qgpkg_set_compression(a);
snprintf(gpkg, sizeof(gpkg), "%s/image.tar%s", tmpdir, filter);
archive_write_open_filename(a, gpkg);
+ lres = archive_entry_linkresolver_new();
+ archive_entry_linkresolver_set_strategy(lres, archive_format(a));
for (; (line = strtok_r(line, "\n", &savep)) != NULL; line = NULL) {
contents_entry *e;
e = contents_parse_line(line);
@@ -414,11 +418,20 @@ qgpkg_make(tree_pkg_ctx *pkg, qpkg_cb_args *args)
snprintf(ename, sizeof(ename), "image/%s",
e->name);
archive_entry_set_pathname(entry, ename);
archive_entry_copy_stat(entry, &st);
+ archive_entry_linkify(lres, &entry, &sparse);
archive_write_header(a, entry);
- while ((len = read(fd, buf, sizeof(buf))) > 0)
- archive_write_data(a, buf, (size_t)len);
- close(fd);
+ if (sparse != NULL) {
+ archive_write_header(a, sparse);
+ archive_entry_free(entry);
+ entry = sparse;
+ }
+ if (archive_entry_size(entry) > 0)
+ {
+ while ((len = read(fd, buf,
sizeof(buf))) > 0)
+ archive_write_data(a, buf,
(size_t)len);
+ }
archive_entry_free(entry);
+ close(fd);
break;
case CONTENTS_SYM:
/* like for files, we take whatever is in the
filesystem */
@@ -470,6 +483,34 @@ qgpkg_make(tree_pkg_ctx *pkg, qpkg_cb_args *args)
break;
}
}
+ do {
+ const char *fname;
+
+ entry = NULL;
+ archive_entry_linkify(lres, &entry, &sparse);
+ if (entry == NULL)
+ break;
+
+ /* these are hardlinks which apparently have targets outside of
+ * the package's know filelist, e.g. the user adding a hardlink,
+ * we need to process them now depending on the archive format,
+ * which means we'll have to lookup the files to get their body
*/
+ fname = archive_entry_pathname(entry);
+ if (fname == NULL ||
+ (fname = strchr(fname, '/')) == NULL)
+ continue; /* not something we would've produced */
+ fname++;
+
+ if ((fd = openat(args->vdb->portroot_fd, fname, O_RDONLY)) < 0)
+ continue;
+
+ archive_write_header(a, entry);
+ while ((len = read(fd, buf, sizeof(buf))) > 0)
+ archive_write_data(a, buf, (size_t)len);
+ archive_entry_free(entry);
+ close(fd);
+ } while (true);
+ archive_entry_linkresolver_free(lres);
archive_write_close(a);
archive_write_free(a);
write_hashes(gpkg, "DATA", mfd);