commit:     03666b198b6fc7d53b70803bdf29e5dcfa96495b
Author:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
AuthorDate: Thu Aug 21 09:11:43 2025 +0000
Commit:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Thu Aug 21 09:11:43 2025 +0000
URL:        https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=03666b19

libq/tree: implement support for gpkg

This allows transparent access to tbz2 and gpkg.tar files as part of the
BINPKG tree type.

Bug: https://bugs.gentoo.org/833571
Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org>

 libq/tree.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++----------
 libq/tree.h |   1 +
 2 files changed, 131 insertions(+), 24 deletions(-)

diff --git a/libq/tree.c b/libq/tree.c
index fffbf27..c8a3ad1 100644
--- a/libq/tree.c
+++ b/libq/tree.c
@@ -14,8 +14,11 @@
 #include <sys/stat.h>
 #include <ctype.h>
 #include <xalloc.h>
-#include "md5.h"
-#include "sha1.h"
+
+#ifdef HAVE_LIBARCHIVE
+# include <archive.h>
+# include <archive_entry.h>
+#endif
 
 #include "atom.h"
 #include "eat_file.h"
@@ -26,11 +29,8 @@
 #include "tree.h"
 #include "xpak.h"
 
-#include <ctype.h>
-#include <xalloc.h>
 
 static int tree_pkg_compar(const void *l, const void *r);
-static tree_pkg_ctx *tree_next_pkg_int(tree_cat_ctx *cat_ctx);
 static void tree_close_meta(tree_pkg_meta *cache);
 
 static tree_ctx *
@@ -440,26 +440,38 @@ tree_pkg_ctx *
 tree_open_pkg(tree_cat_ctx *cat_ctx, const char *name)
 {
        tree_pkg_ctx *pkg_ctx;
+       bool          isgpkg = false;
 
        if (cat_ctx->ctx->treetype == TREE_EBUILD &&
                cat_ctx->ctx->ebuilddir_cat_ctx == cat_ctx)
        {
-               char *p;
-               if ((p = strstr(name, ".ebuild")) == NULL)
+               size_t len = strlen(name);
+               char  *p   = (char *)&name[len - (sizeof(".ebuild") - 1)];
+               if (len <= sizeof(".ebuild") - 1 ||
+                       memcmp(p, ".ebuild", sizeof(".ebuild") - 1) != 0)
                        return NULL;  /* invalid, must be some random other 
file */
                *p = '\0';
        } else if (cat_ctx->ctx->treetype == TREE_BINPKGS) {
-               char *p;
-               if ((p = strstr(name, ".tbz2")) == NULL)
-                       return NULL;  /* invalid, no support for .gpkg yet */
+               size_t len = strlen(name);
+               char  *p   = (char *)&name[len - (sizeof(".gpkg.tar") - 1)];
+               if (len > sizeof(".gpkg.tar") - 1 &&
+                       memcmp(p, ".gpkg.tar", sizeof(".gpkg.tar") - 1) == 0)
+                       isgpkg = true;
+
+               if (!isgpkg &&
+                       (len <= sizeof(".tbz2") - 1 ||
+                        (p = (char *)&name[len - (sizeof(".tbz2") - 1)]) == 
NULL ||
+                        memcmp(p, ".tbz2", sizeof(".tbz2") - 1) != 0))
+                       return NULL;  /* invalid, like above */
                *p = '\0';
        }
 
-       pkg_ctx = xzalloc(sizeof(*pkg_ctx));
-       pkg_ctx->name = name;
-       pkg_ctx->repo = cat_ctx->ctx->repo;
-       pkg_ctx->fd = -1;
-       pkg_ctx->cat_ctx = cat_ctx;
+       pkg_ctx                = xzalloc(sizeof(*pkg_ctx));
+       pkg_ctx->name          = name;
+       pkg_ctx->repo          = cat_ctx->ctx->repo;
+       pkg_ctx->fd            = -1;
+       pkg_ctx->cat_ctx       = cat_ctx;
+       pkg_ctx->binpkg_isgpkg = isgpkg;
 
        /* see if this pkg matches the query, here we can finally check
         * version conditions like >=, etc. */
@@ -1044,10 +1056,81 @@ static tree_pkg_meta *
 tree_read_file_binpkg(tree_pkg_ctx *pkg_ctx)
 {
        tree_pkg_meta *m = xzalloc(sizeof(tree_pkg_meta));
-       int newfd = dup(pkg_ctx->fd);
+       int newfd;
+
+       if (pkg_ctx->binpkg_isgpkg) {
+#ifdef HAVE_LIBARCHIVE
+               struct archive       *a     = archive_read_new();
+               struct archive_entry *entry;
+               size_t                len   = 0;
+               char                 *buf   = NULL;
+
+               archive_read_support_format_all(a);
+               archive_read_support_filter_all(a);
+
+               if (archive_read_open_fd(a, pkg_ctx->fd, BUFSIZ) != ARCHIVE_OK)
+                       return NULL;
+               while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
+                       const char *pathname = archive_entry_pathname(entry);
+                       const char *fname    = strchr(pathname, '/');
+                       if (fname == NULL)
+                               continue;
+                       fname++;
+                       if (strncmp(fname, "metadata.tar",
+                                               sizeof("metadata.tar") - 1) == 
0)
+                       {
+                               /* read this nested tar, it contains the VDB 
entries
+                                * otherwise stored in xpak */
+                               len = archive_entry_size(entry);
+                               buf = xmalloc(len);
+                               archive_read_data(a, buf, len);
+                               break;
+                       }
+               }
+               archive_read_free(a);
+
+               if (buf != NULL)
+               {
+                       char  *data      = NULL;
+                       size_t data_size = 0;
+                       size_t data_len  = 0;
+
+                       a = archive_read_new();
+                       archive_read_support_format_all(a);
+                       archive_read_support_filter_all(a);
+                       archive_read_open_memory(a, buf, len);
+
+                       while (archive_read_next_header(a, &entry) == 
ARCHIVE_OK) {
+                               const char *pathname = 
archive_entry_pathname(entry);
+                               char       *fname    = strchr(pathname, '/');
+                               if (fname == NULL)
+                                       continue;
+                               fname++;
+
+                               data_len = archive_entry_size(entry);
+                               if (data_len > data_size) {
+                                       data_size = data_len;
+                                       data      = xrealloc(data, data_size);
+                               }
+                               if (archive_read_data(a, data, data_len) < 0)
+                                       continue;
+                               tree_read_file_binpkg_xpak_cb(m,
+                                                                               
          fname, (int)strlen(fname),
+                                                                               
          0, data_len, data);
+                       }
+                       archive_read_free(a);
+                       free(buf);
+                       free(data);
+               }
 
-       xpak_process_fd(pkg_ctx->fd, true, m, tree_read_file_binpkg_xpak_cb);
-       pkg_ctx->fd = -1;  /* closed by xpak_process_fd */
+               newfd = pkg_ctx->fd;
+#else
+               return NULL;
+#endif
+       } else {
+               xpak_process_fd(pkg_ctx->fd, true, m, 
tree_read_file_binpkg_xpak_cb);
+               pkg_ctx->fd = -1;  /* closed by xpak_process_fd */
+       }
 
        /* fill in some properties which are not available, but would be in
         * Packages, and used to verify the package ... this is somewhat
@@ -1093,7 +1176,8 @@ tree_pkg_read_openfd_int(tree_pkg_ctx *pkg_ctx)
                {
                        char buf[_Q_PATH_MAX];
                        snprintf(buf, sizeof(buf), "%s.%s", pkg_ctx->name,
-                                        ctx->treetype == TREE_EBUILD ? 
"ebuild" : "tbz2");
+                                        ctx->treetype == TREE_EBUILD ? 
"ebuild" :
+                                        pkg_ctx->binpkg_isgpkg ? "gpkg.tar" : 
"tbz2");
                        pkg_ctx->fd = openat(pkg_ctx->cat_ctx->fd, buf,
                                                                 O_RDONLY | 
O_CLOEXEC);
                } else {
@@ -1530,9 +1614,30 @@ tree_foreach_packages(tree_ctx *ctx, tree_pkg_cb 
callback, void *priv)
                                }
                                if (meta.Q_BUILDID != NULL)
                                        atom->BUILDID = atoi(meta.Q_BUILDID);
-                               pkgnamelen = snprintf(pkgname, sizeof(pkgname),
-                                               "%s.tbz2", atom->PF);
-                               pkgname[pkgnamelen - (sizeof(".tbz2") - 1)] = 
'\0';
+                               pkgnamelen = 0;
+                               if (meta.Q_PATH != NULL) {
+                                       size_t len = strlen(meta.Q_PATH);
+                                       if (len > sizeof(".tbz2") - 1 &&
+                                               memcmp(meta.Q_PATH + len - 
sizeof(".tbz2") - 1,
+                                                          ".tbz2", 
sizeof(".tbz2") - 1) == 0)
+                                       {
+                                               pkgnamelen = snprintf(pkgname, 
sizeof(pkgname),
+                                                                               
          "%s.tbz2", atom->PF);
+                                               pkgname[pkgnamelen - 
(sizeof(".tbz2") - 1)] = '\0';
+                                       }
+                                       if (len > sizeof(".gpkg.tar") - 1 &&
+                                               memcmp(meta.Q_PATH + len - 
sizeof(".gpkg.tar") - 1,
+                                                          ".gpkg.tar", 
sizeof(".gpkg.tar") - 1) == 0)
+                                       {
+                                               pkgnamelen = snprintf(pkgname, 
sizeof(pkgname),
+                                                                               
          "%s.gpkg.tar", atom->PF);
+                                               pkgname[pkgnamelen - 
(sizeof(".gpkg.tar") - 1)] = '\0';
+                                       }
+                               }
+                               if (pkgnamelen == 0) {
+                                       pkgnamelen = snprintf(pkgname, 
sizeof(pkgname),
+                                                                               
  "%s", atom->PF);
+                               }
                                pkg.name = pkgname;
                                pkg.slot = meta.Q_SLOT == NULL ? (char *)"0" : 
meta.Q_SLOT;
                                pkg.repo = ctx->repo;
@@ -1874,8 +1979,9 @@ tree_match_search_cat_int(
                                                 (char *)cat_ctx->ctx->path,
                                                 cat_ctx->name, pkg_ctx->name,
                                                 cat_ctx->ctx->treetype == 
TREE_EBUILD   ? ".ebuild" :
-                                                cat_ctx->ctx->treetype == 
TREE_BINPKGS  ? ".tbz2"   :
-                                                cat_ctx->ctx->treetype == 
TREE_PACKAGES ? ".tbz2"   :
+                                                (cat_ctx->ctx->treetype == 
TREE_BINPKGS ||
+                                                 cat_ctx->ctx->treetype == 
TREE_PACKAGES) ?
+                                                (pkg_ctx->binpkg_isgpkg   ? 
".gpkg.tar" : ".tbz2")  :
                                                                                
           "");
                        }
                        if (flags & TREE_MATCH_METADATA)

diff --git a/libq/tree.h b/libq/tree.h
index 2af425e..6f2afdb 100644
--- a/libq/tree.h
+++ b/libq/tree.h
@@ -74,6 +74,7 @@ struct tree_pkg_ctx {
        size_t slot_len;
        size_t repo_len;
        int fd;
+       int binpkg_isgpkg:1;
        tree_cat_ctx *cat_ctx;
        depend_atom *atom;
        tree_pkg_meta *meta;

Reply via email to