commit:     7740036f998166fddbf32b08042b461e8f1cd9dc
Author:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
AuthorDate: Thu Jan  1 12:34:30 2026 +0000
Commit:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Thu Jan  1 12:34:30 2026 +0000
URL:        https://gitweb.gentoo.org/proj/portage-utils.git/commit/?id=7740036f

libq/tree: support binpkg tree structure where subdirs for PN may exist

This is a hybrid of vdb/metadata and ebuild format, which we need to
determine dynamically for each and every package thanks to
bonpkg-multi-instance feature.

Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org>

 libq/tree.c | 179 ++++++++++++++++++++++++++++++++++++++++--------------------
 libq/tree.h |   3 +-
 2 files changed, 123 insertions(+), 59 deletions(-)

diff --git a/libq/tree.c b/libq/tree.c
index 1f51745..3f67d42 100644
--- a/libq/tree.c
+++ b/libq/tree.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2005-2025 Gentoo Foundation
+ * Copyright 2005-2026 Gentoo Foundation
  * Distributed under the terms of the GNU General Public License v2
  *
  * Copyright 2005-2008 Ned Ludd        - <[email protected]>
@@ -511,7 +511,8 @@ tree_pkg_ctx *
 tree_open_pkg(tree_cat_ctx *cat_ctx, const char *name)
 {
        tree_pkg_ctx *pkg_ctx;
-       bool          isgpkg = false;
+       bool          isgpkg  = false;
+       depend_atom  *patom   = NULL;
 
        if (cat_ctx->ctx->treetype == TREE_EBUILD &&
                cat_ctx->ctx->ebuilddir_cat_ctx == cat_ctx)
@@ -523,18 +524,45 @@ tree_open_pkg(tree_cat_ctx *cat_ctx, const char *name)
                        return NULL;  /* invalid, must be some random other 
file */
                *p = '\0';
        } else if (cat_ctx->ctx->treetype == TREE_BINPKGS) {
-               size_t len = strlen(name);
-               char  *p   = (char *)&name[len - (sizeof(".gpkg.tar") - 1)];
+               size_t len   = strlen(name);
+               char  *p     = (char *)&name[len - (sizeof(".gpkg.tar") - 1)];
+               bool   isxpk = false;
+
+               /* we pre-build the atom here to pick up on the build_id in the
+                * filename, if any */
+
                if (len > sizeof(".gpkg.tar") - 1 &&
                        memcmp(p, ".gpkg.tar", sizeof(".gpkg.tar") - 1) == 0)
+               {
                        isgpkg = true;
+                       patom  = atom_explode_cat(name, cat_ctx->name);
+                       *p     = '\0';
+               }
 
                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';
+                       (len > sizeof(".tbz2") - 1 &&
+                        (p = (char *)&name[len - (sizeof(".tbz2") - 1)]) != 
NULL &&
+                        memcmp(p, ".tbz2", sizeof(".tbz2") - 1) == 0))
+               {
+                       isxpk = true;
+                       patom  = atom_explode_cat(name, cat_ctx->name);
+                       *p    = '\0';
+               }
+
+               if (!isgpkg &&
+                       !isxpk)
+               {
+                       struct stat sb;
+
+                       if (cat_ctx->ctx->ebuilddir_cat_ctx == cat_ctx)
+                               return NULL;  /* invalid, like above but only 
as leaf */
+
+                       /* only accept this one if it is a directory (PN) */
+                       if (fstatat(cat_ctx->fd, name, &sb, 0) < 0)
+                               return NULL;  /* nothing? */
+                       if (!S_ISDIR(sb.st_mode))
+                               return NULL;  /* invalid random file */
+               }
        }
 
        pkg_ctx                = xzalloc(sizeof(*pkg_ctx));
@@ -543,6 +571,7 @@ tree_open_pkg(tree_cat_ctx *cat_ctx, const char *name)
        pkg_ctx->fd            = -1;
        pkg_ctx->cat_ctx       = cat_ctx;
        pkg_ctx->binpkg_isgpkg = isgpkg;
+       pkg_ctx->atom          = patom;
 
        /* see if this pkg matches the query, here we can finally check
         * version conditions like >=, etc. */
@@ -649,58 +678,80 @@ tree_next_pkg_int(tree_cat_ctx *cat_ctx)
 tree_pkg_ctx *
 tree_next_pkg(tree_cat_ctx *cat_ctx)
 {
-       tree_ctx *ctx = cat_ctx->ctx;
-       tree_pkg_ctx *ret = NULL;
+       tree_ctx     *ctx     = cat_ctx->ctx;
+       tree_pkg_ctx *ret     = NULL;
+       bool          recurse = false;
+       bool          dofile  = false;
+
+       /* first resume an on-going recursed listing */
+       if (ctx->ebuilddir_pkg_ctx != NULL) {
+               ret = tree_next_pkg_int(ctx->ebuilddir_cat_ctx);
+               if (ret == NULL) {
+                       tree_close_cat(ctx->ebuilddir_cat_ctx);
+                       if (!cat_ctx->ctx->do_sort ||
+                               cat_ctx->pkg_ctxs == NULL)
+                               tree_close_pkg(ctx->ebuilddir_pkg_ctx);
+                       ctx->ebuilddir_pkg_ctx = NULL;
+               } else {
+                       ret->binpkg_ismulti = true;  /* no matter if this is 
ebuild */
+                       return ret;
+               }
+       }
+
+       /* load next entry */
+       ret = tree_next_pkg_int(cat_ctx);
+       if (ret == NULL)
+               return ret;
 
        if (ctx->treetype == TREE_EBUILD) {
                /* serve *.ebuild files each as separate pkg_ctx with name set
-                * to CAT/P like in VDB and metadata */
-               do {
-                       if (ctx->ebuilddir_pkg_ctx == NULL) {
-                               tree_ctx *pkgdir = ctx->ebuilddir_ctx;
-
-                               if (pkgdir == NULL)
-                                       pkgdir = ctx->ebuilddir_ctx = 
xmalloc(sizeof(*pkgdir));
-
-                               ctx->ebuilddir_cat_ctx = NULL;
-                               ctx->ebuilddir_pkg_ctx = 
tree_next_pkg_int(cat_ctx);
-                               if (ctx->ebuilddir_pkg_ctx == NULL)
-                                       return NULL;
-
-                               memset(pkgdir, 0, sizeof(*pkgdir));
-                               pkgdir->portroot_fd = -1;
-                               pkgdir->tree_fd = cat_ctx->fd;
-                               pkgdir->do_sort = ctx->do_sort;
-                               pkgdir->repo = ctx->repo;
-                               pkgdir->treetype = ctx->treetype;
-                               pkgdir->subtree = ctx->subtree;
-
-                               ctx->ebuilddir_cat_ctx =
-                                       tree_open_cat(pkgdir, 
ctx->ebuilddir_pkg_ctx->name);
-
-                               /* opening might fail if what we found wasn't a
-                                * directory or something */
-                               if (ctx->ebuilddir_cat_ctx == NULL) {
-                                       tree_close_pkg(ctx->ebuilddir_pkg_ctx);
-                                       ctx->ebuilddir_pkg_ctx = NULL;
-                                       continue;
-                               }
+                * to CAT/P like in VDB and metadata, this is the only supported
+                * format, so if there's a non-directory in here, we just ignore
+                * it, which is fine */
+               recurse = true;
+       } else if (ctx->treetype == TREE_BINPKGS) {
+               /* with FEATURES=binpkg-multi-instance we can have at worst a
+                * mix here of files and directories, so we must handle both */
+               recurse = true;
+               dofile  = true;
+       }
 
-                               /* "zap" the pkg such that it looks like CAT/P 
*/
-                               ctx->ebuilddir_cat_ctx->name = cat_ctx->name;
-                               ctx->ebuilddir_cat_ctx->ctx = ctx;
-                       }
+       if (recurse) {
+               tree_ctx *pkgdir = ctx->ebuilddir_ctx;
 
-                       ret = tree_next_pkg_int(ctx->ebuilddir_cat_ctx);
-                       if (ret == NULL) {
-                               tree_close_cat(ctx->ebuilddir_cat_ctx);
-                               if (!cat_ctx->ctx->do_sort || cat_ctx->pkg_ctxs 
== NULL)
-                                       tree_close_pkg(ctx->ebuilddir_pkg_ctx);
-                               ctx->ebuilddir_pkg_ctx = NULL;
-                       }
-               } while (ret == NULL);
-       } else {
-               ret = tree_next_pkg_int(cat_ctx);
+               if (pkgdir == NULL)
+                       pkgdir = ctx->ebuilddir_ctx = xmalloc(sizeof(*pkgdir));
+
+               ctx->ebuilddir_cat_ctx = NULL;
+               ctx->ebuilddir_pkg_ctx = ret;
+
+               memset(pkgdir, 0, sizeof(*pkgdir));
+               pkgdir->portroot_fd = -1;
+               pkgdir->tree_fd     = cat_ctx->fd;
+               pkgdir->do_sort     = ctx->do_sort;
+               pkgdir->repo        = ctx->repo;
+               pkgdir->treetype    = ctx->treetype;
+               pkgdir->subtree     = ctx->subtree;
+
+               ctx->ebuilddir_cat_ctx =
+                       tree_open_cat(pkgdir, ctx->ebuilddir_pkg_ctx->name);
+
+               /* opening might fail if what we found wasn't a
+                * directory or something */
+               if (ctx->ebuilddir_cat_ctx == NULL) {
+                       ctx->ebuilddir_pkg_ctx = NULL;
+                       if (dofile)
+                               return ret;
+                       else
+                               tree_close_pkg(ret);
+               } else {
+                       /* "zap" the pkg such that it looks like CAT/P */
+                       ctx->ebuilddir_cat_ctx->name = cat_ctx->name;
+                       ctx->ebuilddir_cat_ctx->ctx  = ctx;
+               }
+
+               /* recurse to get the next step */
+               ret = tree_next_pkg(cat_ctx);
        }
 
        return ret;
@@ -2302,13 +2353,25 @@ tree_match_search_cat_int(
                        if (cat_ctx->ctx->treetype == TREE_PACKAGES &&
                                pkg_ctx->meta->Q_PATH != NULL)
                        {
-                               /* binpkg-multi-instance has a PATH ready for 
us */
+                               /* Packages file has a PATH ready for us */
                                snprintf(n->path, sizeof(n->path), "%s/%s",
                                                 (char *)cat_ctx->ctx->path, 
pkg_ctx->meta->Q_PATH);
+                       } else if (pkg_ctx->binpkg_ismulti) {
+                               /* FEATURES=binpkg-multi-instance complicates 
things as
+                                * it creates an intermediate PN directory like 
ebuilds,
+                                * without multi-instance, it is more flat */
+                               snprintf(n->path, sizeof(n->path), 
"%s/%s/%s/%s%s",
+                                                (char *)cat_ctx->ctx->path,
+                                                atom->CATEGORY, atom->PN, 
pkg_ctx->name,
+                                                cat_ctx->ctx->treetype == 
TREE_EBUILD   ? ".ebuild" :
+                                                (cat_ctx->ctx->treetype == 
TREE_BINPKGS ||
+                                                 cat_ctx->ctx->treetype == 
TREE_PACKAGES) ?
+                                                (pkg_ctx->binpkg_isgpkg   ? 
".gpkg.tar" : ".tbz2")  :
+                                                                               
           "");
                        } else {
                                snprintf(n->path, sizeof(n->path), "%s/%s/%s%s",
                                                 (char *)cat_ctx->ctx->path,
-                                                cat_ctx->name, pkg_ctx->name,
+                                                atom->CATEGORY, pkg_ctx->name,
                                                 cat_ctx->ctx->treetype == 
TREE_EBUILD   ? ".ebuild" :
                                                 (cat_ctx->ctx->treetype == 
TREE_BINPKGS ||
                                                  cat_ctx->ctx->treetype == 
TREE_PACKAGES) ?

diff --git a/libq/tree.h b/libq/tree.h
index 1a83c6c..32dc6f5 100644
--- a/libq/tree.h
+++ b/libq/tree.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2005-2025 Gentoo Foundation
+ * Copyright 2005-2026 Gentoo Foundation
  * Distributed under the terms of the GNU General Public License v2
  */
 
@@ -76,6 +76,7 @@ struct tree_pkg_ctx {
        size_t repo_len;
        int fd;
        int binpkg_isgpkg:1;
+       int binpkg_ismulti:1;  /* for path reconstruction */
        tree_cat_ctx *cat_ctx;
        depend_atom *atom;
        tree_pkg_meta *meta;

Reply via email to