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;
