From f4c9a9f3b902e90dacf908ace2d1d974b1b24557 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Sat, 28 Oct 2023 23:57:09 +0200 Subject: [PATCH 1/8] lib: move the repository stage into the repodata file By combining repodata and stage data into a single file its easier to use remote stagedata when enabled. This also avoids having to come up with a new mechanism to fetch -stagedata and having to keep -repodata and -stagedata in sync. --- include/xbps.h.in | 73 ++--- include/xbps_api_impl.h | 6 + lib/archive.c | 109 +++++++- lib/plist_fetch.c | 163 ++--------- lib/repo.c | 607 +++++++++++++++++++++++++--------------- 5 files changed, 540 insertions(+), 418 deletions(-) diff --git a/include/xbps.h.in b/include/xbps.h.in index 780673b94..783dfe62d 100644 --- a/include/xbps.h.in +++ b/include/xbps.h.in @@ -112,16 +112,22 @@ #define XBPS_PKGFILES "files.plist" /** - * @def XBPS_REPOIDX + * @def XBPS_REPODATA_INDEX * Filename for the repository index property list. */ -#define XBPS_REPOIDX "index.plist" +#define XBPS_REPODATA_INDEX "index.plist" /** - * @def XBPS_REPOIDX_META + * @def XBPS_REPODATA_STAGE + * Filename for the repository stage property list. + */ +#define XBPS_REPODATA_STAGE "stage.plist" + +/** + * @def XBPS_REPODATA_META * Filename for the repository index metadata property list. */ -#define XBPS_REPOIDX_META "index-meta.plist" +#define XBPS_REPODATA_META "index-meta.plist" /** * @def XBPS_FLAG_VERBOSE @@ -1415,19 +1421,36 @@ struct xbps_repo { struct { struct xbps_repo *sqe_next; /* next element */ } entries; - struct archive *ar; /** * @var xhp * * Pointer to our xbps_handle struct passed to xbps_rpool_foreach. */ struct xbps_handle *xhp; + /** + * @var arch + * + * The architecture of the repository. + */ + const char *arch; /** * @var idx * * Proplib dictionary associated with the repository index. */ xbps_dictionary_t idx; + /** + * @var index + * + * Proplib dictionary associated with the repository index. + */ + xbps_dictionary_t index; + /** + * @var idx + * + * Proplib dictionary associated with the repository index. + */ + xbps_dictionary_t stage; /** * @var idxmeta * @@ -1440,10 +1463,6 @@ struct xbps_repo { * URI string associated with repository. */ const char *uri; - /** - * @private - */ - int fd; /** * var is_remote * @@ -1612,7 +1631,7 @@ bool xbps_repo_remove(struct xbps_handle *xhp, const char *url); * @return True on success and lockfd/lockfname are assigned appropiately. * otherwise false and lockfd/lockfname aren't set. */ -bool xbps_repo_lock(struct xbps_handle *xhp, const char *uri, int *lockfd, char **lockfname); +int xbps_repo_lock(const char *repodir, const char *arch); /** * Unlocks a local repository and removes its lock file. @@ -1620,7 +1639,7 @@ bool xbps_repo_lock(struct xbps_handle *xhp, const char *uri, int *lockfd, char * @param[in] lockfd Lock file descriptor. * @param[in] lockfname Lock filename. */ -void xbps_repo_unlock(int lockfd, char *lockfname); +void xbps_repo_unlock(const char *repodir, const char *arch, int fd); /** * Opens a repository and returns a xbps_repo object. @@ -1632,16 +1651,6 @@ void xbps_repo_unlock(int lockfd, char *lockfname); */ struct xbps_repo *xbps_repo_open(struct xbps_handle *xhp, const char *uri); -/** - * Opens a staging repository and returns a xbps_repo object. - * - * @param[in] xhp Pointer to the xbps_handle struct. - * @param[in] uri Repository URI to match. - * - * @return The matching repository object, NULL otherwise. - */ -struct xbps_repo *xbps_repo_stage_open(struct xbps_handle *xhp, const char *uri); - /** * Opens a repository and returns a xbps_repo object. * @@ -1679,28 +1688,6 @@ void xbps_repo_release(struct xbps_repo *repo); */ char *xbps_repo_path(struct xbps_handle *xhp, const char *url); -/** - * - * Returns a heap-allocated string with the repository local path. - * - * @param[in] xhp The xbps_handle object. - * @param[in] url The repository URL to match. - * @param[in] name The repository name (stage or repodata) - * - * @return A heap allocated string that must be free(3)d when it's unneeded. - */ -char *xbps_repo_path_with_name(struct xbps_handle *xhp, const char *url, const char *name); - -/** - * Remotely fetch repository data and keep it in memory. - * - * @param[in] repo A struct xbps_repo pointer to be filled in. - * @param[in] url Full url to the target remote repository data archive. - * - * @return True on success, false otherwise and errno is set appropiately. - */ -bool xbps_repo_fetch_remote(struct xbps_repo *repo, const char *url); - /** * Returns a pkg dictionary from a repository \a repo matching * the expression \a pkg. diff --git a/include/xbps_api_impl.h b/include/xbps_api_impl.h index 66ffc3c1a..1f5389489 100644 --- a/include/xbps_api_impl.h +++ b/include/xbps_api_impl.h @@ -116,9 +116,15 @@ int HIDDEN xbps_set_cb_state(struct xbps_handle *, xbps_state_t, int, int HIDDEN xbps_unpack_binary_pkg(struct xbps_handle *, xbps_dictionary_t); int HIDDEN xbps_remove_pkg(struct xbps_handle *, const char *, bool); int HIDDEN xbps_register_pkg(struct xbps_handle *, xbps_dictionary_t); + char HIDDEN *xbps_archive_get_file(struct archive *, struct archive_entry *); xbps_dictionary_t HIDDEN xbps_archive_get_dictionary(struct archive *, struct archive_entry *); + +struct archive HIDDEN *xbps_archive_read_new(void); +int HIDDEN xbps_archive_read_open(struct archive *ar, const char *path); +int HIDDEN xbps_archive_read_open_remote(struct archive *ar, const char *url); + const char HIDDEN *vpkg_user_conf(struct xbps_handle *, const char *, bool); xbps_array_t HIDDEN xbps_get_pkg_fulldeptree(struct xbps_handle *, const char *, bool); diff --git a/lib/archive.c b/lib/archive.c index 94cba292d..e8ff836c3 100644 --- a/lib/archive.c +++ b/lib/archive.c @@ -23,12 +23,14 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include #include #include #include #include +#include "fetch.h" #include "xbps_api_impl.h" char HIDDEN * @@ -83,8 +85,8 @@ xbps_archive_append_buf(struct archive *ar, const void *buf, const size_t buflen assert(gname); entry = archive_entry_new(); - if (entry == NULL) - return archive_errno(ar); + if (!entry) + return -archive_errno(ar); archive_entry_set_filetype(entry, AE_IFREG); archive_entry_set_perm(entry, mode); @@ -95,17 +97,114 @@ xbps_archive_append_buf(struct archive *ar, const void *buf, const size_t buflen if (archive_write_header(ar, entry) != ARCHIVE_OK) { archive_entry_free(entry); - return archive_errno(ar); + return -archive_errno(ar); } if (archive_write_data(ar, buf, buflen) != ARCHIVE_OK) { archive_entry_free(entry); - return archive_errno(ar); + return -archive_errno(ar); } if (archive_write_finish_entry(ar) != ARCHIVE_OK) { archive_entry_free(entry); - return archive_errno(ar); + return -archive_errno(ar); } archive_entry_free(entry); return 0; } + +struct fetch_archive { + struct url *url; + struct fetchIO *fetch; + char buffer[32768]; +}; + +static int +fetch_archive_open(struct archive *a UNUSED, void *client_data) +{ + struct fetch_archive *f = client_data; + + f->fetch = fetchGet(f->url, NULL); + + if (f->fetch == NULL) + return ENOENT; + + return 0; +} + +static ssize_t +fetch_archive_read(struct archive *a UNUSED, void *client_data, const void **buf) +{ + struct fetch_archive *f = client_data; + + *buf = f->buffer; + return fetchIO_read(f->fetch, f->buffer, sizeof(f->buffer)); +} + +static int +fetch_archive_close(struct archive *a UNUSED, void *client_data) +{ + struct fetch_archive *f = client_data; + + if (f->fetch != NULL) + fetchIO_close(f->fetch); + fetchFreeURL(f->url); + free(f); + + return 0; +} + +struct archive HIDDEN * +xbps_archive_read_new(void) +{ + struct archive *ar = archive_read_new(); + if (!ar) + return NULL; + archive_read_support_filter_gzip(ar); + archive_read_support_filter_bzip2(ar); + archive_read_support_filter_xz(ar); + archive_read_support_filter_lz4(ar); + archive_read_support_filter_zstd(ar); + archive_read_support_format_tar(ar); + return ar; +} + +int HIDDEN +xbps_archive_read_open(struct archive *ar, const char *filename) +{ + int r = archive_read_open_filename(ar, filename, 4096); + if (r == ARCHIVE_FATAL) + return -archive_errno(ar); + return 0; +} + +int HIDDEN +xbps_archive_read_open_remote(struct archive *ar, const char *url) +{ + struct url *furl; + struct fetch_archive *f; + int r; + + furl = fetchParseURL(url); + if (!furl) + return -EINVAL; + + f = calloc(1, sizeof(*f)); + if (!f) { + r = -errno; + archive_read_free(ar); + fetchFreeURL(furl); + return r; + } + f->url = furl; + + r = archive_read_open(ar, f, fetch_archive_open, fetch_archive_read, + fetch_archive_close); + if (r == ARCHIVE_FATAL) { + r = -archive_errno(ar); + fetchFreeURL(f->url); + free(f); + return r; + } + + return 0; +} diff --git a/lib/plist_fetch.c b/lib/plist_fetch.c index efeb0de73..bc6bbb743 100644 --- a/lib/plist_fetch.c +++ b/lib/plist_fetch.c @@ -40,110 +40,33 @@ * @defgroup plist_fetch Package URL metadata files handling */ -struct fetch_archive { - struct url *url; - struct fetchIO *fetch; - char buffer[32768]; -}; - -static int -fetch_archive_open(struct archive *a UNUSED, void *client_data) -{ - struct fetch_archive *f = client_data; - - f->fetch = fetchGet(f->url, NULL); - - if (f->fetch == NULL) - return ENOENT; - - return 0; -} - -static ssize_t -fetch_archive_read(struct archive *a UNUSED, void *client_data, const void **buf) -{ - struct fetch_archive *f = client_data; - - *buf = f->buffer; - return fetchIO_read(f->fetch, f->buffer, sizeof(f->buffer)); -} - -static int -fetch_archive_close(struct archive *a UNUSED, void *client_data) -{ - struct fetch_archive *f = client_data; - - if (f->fetch != NULL) - fetchIO_close(f->fetch); - free(f); - - return 0; -} - static struct archive * -open_archive_by_url(struct url *url) +open_archive(const char *url) { - struct fetch_archive *f; - struct archive *a; - - f = malloc(sizeof(struct fetch_archive)); - if (f == NULL) - return NULL; - - f->url = url; - - if ((a = archive_read_new()) == NULL) { - free(f); + struct archive *ar; + int r; + + ar = xbps_archive_read_new(); + if (!ar) { + r = -errno; + xbps_error_printf("failed to open archive: %s: %s\n", url, strerror(-r)); + errno = -r; return NULL; } - archive_read_support_filter_gzip(a); - archive_read_support_filter_bzip2(a); - archive_read_support_filter_xz(a); - archive_read_support_filter_lz4(a); - archive_read_support_filter_zstd(a); - archive_read_support_format_tar(a); - if (archive_read_open(a, f, fetch_archive_open, fetch_archive_read, - fetch_archive_close) != ARCHIVE_OK) { - archive_read_free(a); - return NULL; + if (xbps_repository_is_remote(url)) { + r = xbps_archive_read_open_remote(ar, url); + } else { + r = xbps_archive_read_open(ar, url); } - - return a; -} - -static struct archive * -open_archive(const char *url) -{ - struct url *u; - struct archive *a; - - if (!xbps_repository_is_remote(url)) { - if ((a = archive_read_new()) == NULL) - return NULL; - - archive_read_support_filter_gzip(a); - archive_read_support_filter_bzip2(a); - archive_read_support_filter_xz(a); - archive_read_support_filter_lz4(a); - archive_read_support_filter_zstd(a); - archive_read_support_format_tar(a); - - /* XXX: block size? */ - if (archive_read_open_filename(a, url, 32768) != ARCHIVE_OK) { - archive_read_free(a); - return NULL; - } - return a; + if (r < 0) { + xbps_error_printf("failed to open archive: %s: %s\n", url, strerror(-r)); + archive_read_free(ar); + errno = -r; + return NULL; } - if ((u = fetchParseURL(url)) == NULL) - return NULL; - - a = open_archive_by_url(u); - fetchFreeURL(u); - - return a; + return ar; } char * @@ -177,54 +100,6 @@ xbps_archive_fetch_file(const char *url, const char *fname) return buf; } -bool -xbps_repo_fetch_remote(struct xbps_repo *repo, const char *url) -{ - struct archive *a; - struct archive_entry *entry; - uint8_t i = 0; - - assert(url); - assert(repo); - - if ((a = open_archive(url)) == NULL) - return false; - - while ((archive_read_next_header(a, &entry)) == ARCHIVE_OK) { - const char *bfile; - char *buf; - - bfile = archive_entry_pathname(entry); - if (bfile[0] == '.') - bfile++; /* skip first dot */ - - if (strcmp(bfile, XBPS_REPOIDX_META) == 0) { - buf = xbps_archive_get_file(a, entry); - repo->idxmeta = xbps_dictionary_internalize(buf); - free(buf); - i++; - } else if (strcmp(bfile, XBPS_REPOIDX) == 0) { - buf = xbps_archive_get_file(a, entry); - repo->idx = xbps_dictionary_internalize(buf); - free(buf); - i++; - } else { - archive_read_data_skip(a); - } - if (i == 2) - break; - } - archive_read_free(a); - - if (xbps_object_type(repo->idxmeta) == XBPS_TYPE_DICTIONARY) - repo->is_signed = true; - - if (xbps_object_type(repo->idx) == XBPS_TYPE_DICTIONARY) - return true; - - return false; -} - int xbps_archive_fetch_file_into_fd(const char *url, const char *fname, int fd) { diff --git a/lib/repo.c b/lib/repo.c index 95acd906c..76decbfd2 100644 --- a/lib/repo.c +++ b/lib/repo.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2012-2020 Juan Romero Pardines. + * Copyright (c) 2023 Duncan Overbruck . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,19 +24,17 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include + +#include +#include +#include +#include #include -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include "xbps_api_impl.h" @@ -44,220 +43,344 @@ * @brief Repository functions * @defgroup repo Repository functions */ -char * -xbps_repo_path(struct xbps_handle *xhp, const char *url) -{ - return xbps_repo_path_with_name(xhp, url, "repodata"); -} -char * -xbps_repo_path_with_name(struct xbps_handle *xhp, const char *url, const char *name) +int +xbps_repo_lock(const char *repodir, const char *arch) { - assert(xhp); - assert(url); - assert(strcmp(name, "repodata") == 0 || strcmp(name, "stagedata") == 0); + char path[PATH_MAX]; + int fd; + int r; - return xbps_xasprintf("%s/%s-%s", - url, xhp->target_arch ? xhp->target_arch : xhp->native_arch, name); -} + if (xbps_repository_is_remote(repodir)) + return -EINVAL; -static xbps_dictionary_t -repo_get_dict(struct xbps_repo *repo) -{ - struct archive_entry *entry; - int rv; + r = snprintf(path, sizeof(path), "%s/%s-repodata.lock", repodir, arch); + if (r < 0 || (size_t)r > sizeof(path)) { + return -ENAMETOOLONG; + } - if (repo->ar == NULL) - return NULL; + fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC, 0660); + if (fd == -1) + return -errno; - rv = archive_read_next_header(repo->ar, &entry); - if (rv != ARCHIVE_OK) { - xbps_dbg_printf("%s: read_next_header %s\n", repo->uri, - archive_error_string(repo->ar)); - return NULL; + if (flock(fd, LOCK_EX|LOCK_NB) == 0) + return fd; + if (errno != EWOULDBLOCK) { + r = -errno; + close(fd); + return r; } - return xbps_archive_get_dictionary(repo->ar, entry); -} -bool -xbps_repo_lock(struct xbps_handle *xhp, const char *repodir, - int *lockfd, char **lockfname) -{ - char *repofile, *lockfile; - int fd, rv; + xbps_warn_printf("repository locked: %s: waiting...", repodir); + if (flock(fd, LOCK_EX) == -1) { + r = -errno; + close(fd); + return r; + } - assert(repodir); - assert(lockfd); - assert(lockfname); + return fd; +} - repofile = xbps_repo_path(xhp, repodir); - assert(repofile); +void +xbps_repo_unlock(const char *repodir, const char *arch, int fd) +{ + char path[PATH_MAX]; + int r; - lockfile = xbps_xasprintf("%s.lock", repofile); - free(repofile); + if (fd != -1) + close(0); - for (;;) { - fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC, 0660); - rv = errno; - if (fd != -1) - break; - if (rv != EEXIST) { - xbps_dbg_printf("[repo] `%s' failed to " - "create lock file %s\n", lockfile, strerror(rv)); - free(lockfile); - return false; - } else { - xbps_dbg_printf("[repo] `%s' lock file exists," - "waiting for 1s...\n", lockfile); - sleep(1); - } - } - *lockfname = lockfile; - *lockfd = fd; - return true; + r = snprintf(path, sizeof(path), "%s/%s-repodata.lock", repodir, arch); + if (r < 0 || (size_t)r > sizeof(path)) + return; + unlink(path); } -void -xbps_repo_unlock(int lockfd, char *lockfname) +static int +repo_read_next(struct xbps_repo *repo, struct archive *ar, struct archive_entry **entry) { - if (lockfd != -1) { - close(lockfd); - } - if (lockfname) { - unlink(lockfname); - free(lockfname); + int r; + + r = archive_read_next_header(ar, entry); + if (r == ARCHIVE_FATAL) { + xbps_error_printf("failed to read repository: %s: %s\n", + repo->uri, archive_error_string(ar)); + return -archive_errno(ar); + } else if (r == ARCHIVE_WARN) { + xbps_warn_printf("reading repository: %s: %s\n", + repo->uri, archive_error_string(ar)); + return 0; + } else if (r == ARCHIVE_EOF) { + return -EIO; } + return 0; } -static bool -repo_open_local(struct xbps_repo *repo, const char *repofile) + +static int +repo_read_index(struct xbps_repo *repo, struct archive *ar) { - struct stat st; + struct archive_entry *entry; + char *buf; + int r; + + r = repo_read_next(repo, ar, &entry); + if (r < 0) + return r; + + /* index.plist */ + if (strcmp(archive_entry_pathname(entry), XBPS_REPODATA_INDEX) != 0) { + xbps_error_printf("failed to read repository index: %s: unexpected archive entry\n", + repo->uri); + r = -EINVAL; + return r; + } + + if (archive_entry_size(entry) == 0) { + r = archive_read_data_skip(ar); + if (r == ARCHIVE_FATAL) { + xbps_error_printf("failed to read repository: %s: archive error: %s\n", + repo->uri, archive_error_string(ar)); + return -archive_errno(ar); + } + repo->index = xbps_dictionary_create(); + return 0; + } - if (fstat(repo->fd, &st) == -1) { - xbps_dbg_printf("[repo] `%s' fstat repodata %s\n", - repofile, strerror(errno)); - return false; + buf = xbps_archive_get_file(ar, entry); + if (!buf) + return -errno; + repo->index = xbps_dictionary_internalize(buf); + r = -errno; + free(buf); + if (!repo->index) { + xbps_error_printf("failed to open repository: %s: reading index: %s\n", + repo->uri, strerror(-r)); + return -EIO; } - repo->ar = archive_read_new(); - assert(repo->ar); - archive_read_support_filter_gzip(repo->ar); - archive_read_support_filter_bzip2(repo->ar); - archive_read_support_filter_xz(repo->ar); - archive_read_support_filter_lz4(repo->ar); - archive_read_support_filter_zstd(repo->ar); - archive_read_support_format_tar(repo->ar); + xbps_dictionary_make_immutable(repo->index); + return 0; +} - if (archive_read_open_fd(repo->ar, repo->fd, st.st_blksize) == ARCHIVE_FATAL) { - xbps_dbg_printf("[repo] `%s' failed to open repodata archive %s\n", - repofile, archive_error_string(repo->ar)); - return false; +static int +repo_read_meta(struct xbps_repo *repo, struct archive *ar) +{ + struct archive_entry *entry; + char *buf; + int r; + + r = repo_read_next(repo, ar, &entry); + if (r < 0) + return r; + + if (strcmp(archive_entry_pathname(entry), XBPS_REPODATA_META) != 0) { + xbps_error_printf("failed to read repository metadata: %s: unexpected archive entry\n", + repo->uri); + r = -EINVAL; + return r; + } + if (archive_entry_size(entry) == 0) { + r = archive_read_data_skip(ar); + if (r == ARCHIVE_FATAL) { + xbps_error_printf("failed to read repository: %s: archive error: %s\n", + repo->uri, archive_error_string(ar)); + return -archive_errno(ar); + } + repo->idxmeta = xbps_dictionary_create(); + return 0; } - if ((repo->idx = repo_get_dict(repo)) == NULL) { - xbps_dbg_printf("[repo] `%s' failed to internalize " - " index on archive, removing file.\n", repofile); - /* broken archive, remove it */ - (void)unlink(repofile); - return false; + + buf = xbps_archive_get_file(ar, entry); + if (!buf) { + r = -errno; + xbps_error_printf("failed to read repository metadata: %s: %s\n", + repo->uri, strerror(-r)); + return r; } - xbps_dictionary_make_immutable(repo->idx); - repo->idxmeta = repo_get_dict(repo); - if (repo->idxmeta != NULL) { - repo->is_signed = true; - xbps_dictionary_make_immutable(repo->idxmeta); + /* for backwards compatibility check if the content is DEADBEEF. */ + if (strcmp(buf, "DEADBEEF") == 0) { + free(buf); + return 0; } - /* - * We don't need the archive anymore, we are only - * interested in the proplib dictionaries. - */ - xbps_repo_close(repo); - return true; -} + errno = 0; + repo->idxmeta = xbps_dictionary_internalize(buf); + r = -errno; -static bool -repo_open_remote(struct xbps_repo *repo) -{ - char *rpath; - bool rv; + free(buf); - rpath = xbps_repo_path(repo->xhp, repo->uri); - rv = xbps_repo_fetch_remote(repo, rpath); - free(rpath); - if (rv) { - xbps_dbg_printf("[repo] `%s' used remotely (kept in memory).\n", repo->uri); - if (repo->xhp->state_cb && xbps_repo_key_import(repo) != 0) - rv = false; + if (!repo->idxmeta) { + if (!r) { + xbps_error_printf("failed to read repository metadata: %s: invalid dictionary\n", + repo->uri); + return -EINVAL; + } + xbps_error_printf("failed to read repository metadata: %s: %s\n", + repo->uri, strerror(-r)); + return r; } - return rv; + + xbps_dictionary_make_immutable(repo->idxmeta); + return 0; } -static struct xbps_repo * -repo_open_with_type(struct xbps_handle *xhp, const char *url, const char *name) +static int +repo_read_stage(struct xbps_repo *repo, struct archive *ar) { - struct xbps_repo *repo = NULL; - const char *arch; - char *repofile = NULL; - - assert(xhp); - assert(url); + struct archive_entry *entry; + int r; + + r = repo_read_next(repo, ar, &entry); + if (r < 0) { + /* XXX: backwards compatibility missing */ + if (r == -EIO) { + repo->stage = xbps_dictionary_create(); + return 0; + } + return r; + } - if (xhp->target_arch) - arch = xhp->target_arch; - else - arch = xhp->native_arch; + if (strcmp(archive_entry_pathname(entry), XBPS_REPODATA_STAGE) != 0) { + xbps_error_printf("failed to read repository stage: %s: unexpected archive entry\n", + repo->uri); + r = -EINVAL; + return r; + } + if (archive_entry_size(entry) == 0) { + repo->stage = xbps_dictionary_create(); + return 0; + } - repo = calloc(1, sizeof(struct xbps_repo)); - assert(repo); - repo->fd = -1; - repo->xhp = xhp; - repo->uri = url; + repo->stage = xbps_archive_get_dictionary(ar, entry); + if (!repo->stage) { + xbps_error_printf("failed to open repository: %s: reading stage: %s\n", + repo->uri, archive_error_string(ar)); + return -EIO; + } + xbps_dictionary_make_immutable(repo->stage); + return 0; +} - if (xbps_repository_is_remote(url)) { - /* remote repository */ - char *rpath; +static int +repo_read(struct xbps_repo *repo, struct archive *ar) +{ + int r; + + r = repo_read_index(repo, ar); + if (r < 0) + return r; + r = repo_read_meta(repo, ar); + if (r < 0) + return r; + r = repo_read_stage(repo, ar); + if (r < 0) + return r; + + return r; +} - if ((rpath = xbps_get_remote_repo_string(url)) == NULL) { - goto out; +static int +repo_open_local(struct xbps_repo *repo, struct archive *ar) +{ + char path[PATH_MAX]; + int r; + + if (repo->is_remote) { + char *cachedir; + cachedir = xbps_get_remote_repo_string(repo->uri); + if (!cachedir) { + r = -EINVAL; + xbps_error_printf("failed to open repository: %s: invalid repository url\n", + repo->uri); + goto err; } - repofile = xbps_xasprintf("%s/%s/%s-%s", xhp->metadir, rpath, arch, name); - free(rpath); - repo->is_remote = true; + r = snprintf(path, sizeof(path), "%s/%s/%s-repodata", + repo->xhp->metadir, cachedir, repo->arch); + free(cachedir); } else { - /* local repository */ - repofile = xbps_repo_path_with_name(xhp, url, name); + r = snprintf(path, sizeof(path), "%s/%s-repodata", repo->uri, repo->arch); } - /* - * In memory repo sync. - */ - if (repo->is_remote && (xhp->flags & XBPS_FLAG_REPOS_MEMSYNC)) { - if (repo_open_remote(repo)) { - free(repofile); - return repo; + if (r < 0 || (size_t)r >= sizeof(path)) { + r = -ENAMETOOLONG; + xbps_error_printf("failed to open repository: %s: repository path too long\n", + repo->uri); + goto err; + } + + r = xbps_archive_read_open(ar, path); + if (r < 0) { + if (r != -ENOENT) { + xbps_error_printf("failed to open repodata: %s: %s\n", + path, strerror(-r)); } - goto out; + goto err; } - /* - * Open the repository archive. - */ - repo->fd = open(repofile, O_RDONLY|O_CLOEXEC); - if (repo->fd == -1) { - int rv = errno; - xbps_dbg_printf("[repo] `%s' open %s %s\n", - repofile, name, strerror(rv)); - xbps_error_printf("%s: %s\n", strerror(rv), repofile); - goto out; + + return 0; +err: + return r; +} + +static int +repo_open_remote(struct xbps_repo *repo, struct archive *ar) +{ + char url[PATH_MAX]; + int r; + + r = snprintf(url, sizeof(url), "%s/%s-repodata", repo->uri, repo->arch); + if (r < 0 || (size_t)r >= sizeof(url)) { + xbps_error_printf("failed to open repository: %s: repository url too long\n", + repo->uri); + return -ENAMETOOLONG; } - if (repo_open_local(repo, repofile)) { - free(repofile); - return repo; + + r = xbps_archive_read_open_remote(ar, url); + if (r < 0) { + xbps_error_printf("failed to open repository: %s: %s\n", repo->uri, strerror(-r)); + archive_read_free(ar); + return r; } -out: - free(repofile); - xbps_repo_release(repo); - return NULL; + return 0; +} + +static int +repo_open(struct xbps_handle *xhp, struct xbps_repo *repo) +{ + struct archive *ar; + int r; + + ar = xbps_archive_read_new(); + if (!ar) { + r = -errno; + xbps_error_printf("failed to open repo: %s\n", strerror(-r)); + return r; + } + + if (repo->is_remote && (xhp->flags & XBPS_FLAG_REPOS_MEMSYNC)) + r = repo_open_remote(repo, ar); + else + r = repo_open_local(repo, ar); + if (r < 0) + goto err; + + r = repo_read(repo, ar); + if (r < 0) + goto err; + + r = archive_read_close(ar); + if (r < 0) { + xbps_error_printf("failed to open repository: %s: closing archive: %s\n", + repo->uri, archive_error_string(ar)); + goto err; + } + + archive_read_free(ar); + return 0; +err: + archive_read_free(ar); + return r; } bool @@ -322,64 +445,90 @@ xbps_repo_remove(struct xbps_handle *xhp, const char *repo) return rv; } -struct xbps_repo * -xbps_repo_stage_open(struct xbps_handle *xhp, const char *url) +static int +repo_merge_stage(struct xbps_repo *repo) { - return repo_open_with_type(xhp, url, "stagedata"); -} + xbps_dictionary_t idx; + xbps_object_t keysym; + xbps_object_iterator_t iter; + int r = 0; -struct xbps_repo * -xbps_repo_public_open(struct xbps_handle *xhp, const char *url) { - return repo_open_with_type(xhp, url, "repodata"); + idx = xbps_dictionary_copy_mutable(repo->index); + if (!idx) + return -errno; + + iter = xbps_dictionary_iterator(repo->stage); + if (!iter) { + r = -errno; + goto err1; + } + + while ((keysym = xbps_object_iterator_next(iter))) { + const char *pkgname = xbps_dictionary_keysym_cstring_nocopy(keysym); + xbps_dictionary_t pkgd = xbps_dictionary_get_keysym(repo->stage, keysym); + if (!xbps_dictionary_set(idx, pkgname, pkgd)) { + r = -errno; + goto err2; + } + } + + xbps_object_iterator_release(iter); + repo->idx = idx; + return 0; +err2: + xbps_object_iterator_release(iter); +err1: + xbps_object_release(idx); + return r; } struct xbps_repo * xbps_repo_open(struct xbps_handle *xhp, const char *url) { - struct xbps_repo *repo = xbps_repo_public_open(xhp, url); - struct xbps_repo *stage = NULL; - xbps_dictionary_t idx; - const char *pkgname; - xbps_object_t keysym; - xbps_object_iterator_t iter; + struct xbps_repo *repo; + int r; + + repo = calloc(1, sizeof(*repo)); + if (!repo) { + r = -errno; + xbps_error_printf("failed to open repository: %s\n", strerror(-r)); + errno = -r; + return NULL; + } + repo->xhp = xhp; + repo->uri = url; + repo->arch = xhp->target_arch ? xhp->target_arch : xhp->native_arch; + repo->is_remote = xbps_repository_is_remote(url); + + r = repo_open(xhp, repo); + if (r < 0) { + free(repo); + errno = -r; + return NULL; + } + + if (repo->is_remote || xbps_dictionary_count(repo->stage) == 0) { + repo->idx = repo->index; + xbps_object_retain(repo->idx); + return repo; + } + /* * Load and merge staging repository if the repository is local. */ - if (repo && !repo->is_remote) { - stage = xbps_repo_stage_open(xhp, url); - if (stage == NULL) - return repo; - idx = xbps_dictionary_copy_mutable(repo->idx); - iter = xbps_dictionary_iterator(stage->idx); - while ((keysym = xbps_object_iterator_next(iter))) { - pkgname = xbps_dictionary_keysym_cstring_nocopy(keysym); - xbps_dictionary_set(idx, pkgname, - xbps_dictionary_get_keysym(stage->idx, keysym)); - } - xbps_object_iterator_release(iter); - xbps_object_release(repo->idx); - xbps_repo_release(stage); - repo->idx = idx; - return repo; + r = repo_merge_stage(repo); + if (r < 0) { + xbps_error_printf( + "failed to open repository: %s: could not merge stage: %s\n", + url, strerror(-r)); + xbps_repo_release(repo); + errno = -r; + return NULL; } + return repo; } -void -xbps_repo_close(struct xbps_repo *repo) -{ - if (!repo) - return; - - if (repo->ar != NULL) { - archive_read_free(repo->ar); - repo->ar = NULL; - } - if (repo->fd != -1) { - close(repo->fd); - repo->fd = -1; - } -} void xbps_repo_release(struct xbps_repo *repo) @@ -387,13 +536,19 @@ xbps_repo_release(struct xbps_repo *repo) if (!repo) return; - xbps_repo_close(repo); - - if (repo->idx != NULL) { + if (repo->idx) { xbps_object_release(repo->idx); repo->idx = NULL; } - if (repo->idxmeta != NULL) { + if (repo->index) { + xbps_object_release(repo->index); + repo->idx = NULL; + } + if (repo->stage) { + xbps_object_release(repo->stage); + repo->idx = NULL; + } + if (repo->idxmeta) { xbps_object_release(repo->idxmeta); repo->idxmeta = NULL; } From cf8dc0dd72dee485ca07a8a31d9ba09545453137 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Sun, 29 Oct 2023 00:02:54 +0200 Subject: [PATCH 2/8] bin/xbps-rindex: move the repository stage into the repodata file --- bin/xbps-rindex/defs.h | 5 +- bin/xbps-rindex/index-add.c | 346 +++++++++++++---------------- bin/xbps-rindex/index-clean.c | 176 +++++++-------- bin/xbps-rindex/main.c | 2 +- bin/xbps-rindex/remove-obsoletes.c | 215 ++++++++++-------- bin/xbps-rindex/repoflush.c | 234 ++++++++++++------- bin/xbps-rindex/sign.c | 36 +-- 7 files changed, 543 insertions(+), 471 deletions(-) diff --git a/bin/xbps-rindex/defs.h b/bin/xbps-rindex/defs.h index d99b59f38..0f3063bc7 100644 --- a/bin/xbps-rindex/defs.h +++ b/bin/xbps-rindex/defs.h @@ -45,7 +45,8 @@ int sign_repo(struct xbps_handle *, const char *, const char *, int sign_pkgs(struct xbps_handle *, int, int, char **, const char *, bool); /* From repoflush.c */ -bool repodata_flush(struct xbps_handle *, const char *, const char *, - xbps_dictionary_t, xbps_dictionary_t, const char *); +int repodata_flush(const char *repodir, const char *arch, + xbps_dictionary_t index, xbps_dictionary_t stage, xbps_dictionary_t meta, + const char *compression); #endif /* !_XBPS_RINDEX_DEFS_H_ */ diff --git a/bin/xbps-rindex/index-add.c b/bin/xbps-rindex/index-add.c index 0c3d27823..f6ec6adf1 100644 --- a/bin/xbps-rindex/index-add.c +++ b/bin/xbps-rindex/index-add.c @@ -37,20 +37,18 @@ #include #include "defs.h" -static bool -repodata_commit(struct xbps_handle *xhp, const char *repodir, - xbps_dictionary_t idx, xbps_dictionary_t meta, xbps_dictionary_t stage, +static int +repodata_commit(const char *repodir, const char *repoarch, + xbps_dictionary_t index, xbps_dictionary_t stage, xbps_dictionary_t meta, const char *compression) { xbps_object_iterator_t iter; xbps_object_t keysym; - int rv; + int r; xbps_dictionary_t oldshlibs, usedshlibs; - if (xbps_dictionary_count(stage) == 0) { - // Nothing to do. - return true; - } + if (xbps_dictionary_count(stage) == 0) + return 0; /* * Find old shlibs-provides @@ -61,7 +59,7 @@ repodata_commit(struct xbps_handle *xhp, const char *repodir, iter = xbps_dictionary_iterator(stage); while ((keysym = xbps_object_iterator_next(iter))) { const char *pkgname = xbps_dictionary_keysym_cstring_nocopy(keysym); - xbps_dictionary_t pkg = xbps_dictionary_get(idx, pkgname); + xbps_dictionary_t pkg = xbps_dictionary_get(index, pkgname); xbps_array_t pkgshlibs; pkgshlibs = xbps_dictionary_get(pkg, "shlib-provides"); @@ -76,13 +74,13 @@ repodata_commit(struct xbps_handle *xhp, const char *repodir, /* * throw away all unused shlibs */ - iter = xbps_dictionary_iterator(idx); + iter = xbps_dictionary_iterator(index); while ((keysym = xbps_object_iterator_next(iter))) { const char *pkgname = xbps_dictionary_keysym_cstring_nocopy(keysym); xbps_dictionary_t pkg = xbps_dictionary_get(stage, pkgname); xbps_array_t pkgshlibs; if (!pkg) - pkg = xbps_dictionary_get_keysym(idx, keysym); + pkg = xbps_dictionary_get_keysym(index, keysym); pkgshlibs = xbps_dictionary_get(pkg, "shlib-requires"); for (unsigned int i = 0; i < xbps_array_count(pkgshlibs); i++) { @@ -105,9 +103,9 @@ repodata_commit(struct xbps_handle *xhp, const char *repodir, * purge all packages that are fullfilled by the index and * not in the stage. */ - iter = xbps_dictionary_iterator(idx); + iter = xbps_dictionary_iterator(index); while ((keysym = xbps_object_iterator_next(iter))) { - xbps_dictionary_t pkg = xbps_dictionary_get_keysym(idx, keysym); + xbps_dictionary_t pkg = xbps_dictionary_get_keysym(index, keysym); xbps_array_t pkgshlibs; @@ -172,10 +170,7 @@ repodata_commit(struct xbps_handle *xhp, const char *repodir, printf("stage: added `%s' (%s)\n", pkgver, arch); } xbps_object_iterator_release(iter); - rv = repodata_flush(xhp, repodir, "stagedata", stage, NULL, compression); - } - else { - char *stagefile; + } else { iter = xbps_dictionary_iterator(stage); while ((keysym = xbps_object_iterator_next(iter))) { const char *pkgname = xbps_dictionary_keysym_cstring_nocopy(keysym); @@ -184,221 +179,182 @@ repodata_commit(struct xbps_handle *xhp, const char *repodir, xbps_dictionary_get_cstring_nocopy(pkg, "pkgver", &pkgver); xbps_dictionary_get_cstring_nocopy(pkg, "architecture", &arch); printf("index: added `%s' (%s).\n", pkgver, arch); - xbps_dictionary_set(idx, pkgname, pkg); + xbps_dictionary_set(index, pkgname, pkg); } - xbps_object_iterator_release(iter); - stagefile = xbps_repo_path_with_name(xhp, repodir, "stagedata"); - unlink(stagefile); - free(stagefile); - rv = repodata_flush(xhp, repodir, "repodata", idx, meta, compression); + stage = NULL; } + + r = repodata_flush(repodir, repoarch, index, stage, meta, compression); xbps_object_release(usedshlibs); xbps_object_release(oldshlibs); - return rv; + return r; } -int -index_add(struct xbps_handle *xhp, int args, int argmax, char **argv, bool force, const char *compression) +static int +index_add_pkg(struct xbps_handle *xhp, xbps_dictionary_t index, xbps_dictionary_t stage, + const char *file, bool force) { - xbps_dictionary_t idx, idxmeta, idxstage, binpkgd, curpkgd; - struct xbps_repo *repo = NULL, *stage = NULL; + char sha256[XBPS_SHA256_SIZE]; + char pkgname[XBPS_NAME_SIZE]; struct stat st; - char *tmprepodir = NULL, *repodir = NULL, *rlockfname = NULL; - int rv = 0, ret = 0, rlockfd = -1; + const char *arch = NULL; + const char *pkgver = NULL; + xbps_dictionary_t binpkgd, curpkgd; + int r; - assert(argv); /* - * Read the repository data or create index dictionaries otherwise. + * Read metadata props plist dictionary from binary package. */ - if ((tmprepodir = strdup(argv[args])) == NULL) - return ENOMEM; - - repodir = dirname(tmprepodir); - if (!xbps_repo_lock(xhp, repodir, &rlockfd, &rlockfname)) { - xbps_error_printf("xbps-rindex: cannot lock repository " - "%s: %s\n", repodir, strerror(errno)); - rv = -1; - goto earlyout; - } - repo = xbps_repo_public_open(xhp, repodir); - if (repo == NULL && errno != ENOENT) { - xbps_error_printf("xbps-rindex: cannot open/lock repository " - "%s: %s\n", repodir, strerror(errno)); - rv = -1; - goto earlyout; - } - if (repo) { - idx = xbps_dictionary_copy_mutable(repo->idx); - idxmeta = xbps_dictionary_copy_mutable(repo->idxmeta); - } else { - idx = xbps_dictionary_create(); - idxmeta = NULL; - } - stage = xbps_repo_stage_open(xhp, repodir); - if (stage == NULL && errno != ENOENT) { - xbps_error_printf("xbps-rindex: cannot open/lock stage repository " - "%s: %s\n", repodir, strerror(errno)); - rv = -1; - goto earlyout; + binpkgd = xbps_archive_fetch_plist(file, "/props.plist"); + if (!binpkgd) { + xbps_error_printf("index: failed to read %s metadata for " + "`%s', skipping!\n", XBPS_PKGPROPS, file); + return 0; } - if (stage) { - idxstage = xbps_dictionary_copy_mutable(stage->idx); + xbps_dictionary_get_cstring_nocopy(binpkgd, "architecture", &arch); + xbps_dictionary_get_cstring_nocopy(binpkgd, "pkgver", &pkgver); + if (!xbps_pkg_arch_match(xhp, arch, NULL)) { + fprintf(stderr, "index: ignoring %s, unmatched arch (%s)\n", pkgver, arch); + goto out; } - else { - idxstage = xbps_dictionary_create(); + if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver)) { + r = -EINVAL; + goto err; } + /* - * Process all packages specified in argv. + * Check if this package exists already in the index, but first + * checking the version. If current package version is greater + * than current registered package, update the index; otherwise + * pass to the next one. */ - for (int i = args; i < argmax; i++) { - const char *arch = NULL, *pkg = argv[i]; - char *pkgver = NULL; - char sha256[XBPS_SHA256_SIZE]; - char pkgname[XBPS_NAME_SIZE]; - - assert(pkg); - /* - * Read metadata props plist dictionary from binary package. - */ - binpkgd = xbps_archive_fetch_plist(pkg, "/props.plist"); - if (binpkgd == NULL) { - xbps_error_printf("index: failed to read %s metadata for " - "`%s', skipping!\n", XBPS_PKGPROPS, pkg); - continue; - } - xbps_dictionary_get_cstring_nocopy(binpkgd, "architecture", &arch); - xbps_dictionary_get_cstring(binpkgd, "pkgver", &pkgver); - if (!xbps_pkg_arch_match(xhp, arch, NULL)) { - fprintf(stderr, "index: ignoring %s, unmatched arch (%s)\n", pkgver, arch); - xbps_object_release(binpkgd); - free(pkgver); - continue; - } - if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver)) { - abort(); - } - /* - * Check if this package exists already in the index, but first - * checking the version. If current package version is greater - * than current registered package, update the index; otherwise - * pass to the next one. - */ - curpkgd = xbps_dictionary_get(idxstage, pkgname); - if (curpkgd == NULL) - curpkgd = xbps_dictionary_get(idx, pkgname); - if (curpkgd == NULL) { - if (errno && errno != ENOENT) { - rv = errno; - xbps_object_release(binpkgd); - free(pkgver); - goto out; - } - } else if (!force) { - char *opkgver = NULL, *oarch = NULL; + curpkgd = xbps_dictionary_get(stage, pkgname); + if (!curpkgd) + curpkgd = xbps_dictionary_get(index, pkgname); - /* Only check version if !force */ - xbps_dictionary_get_cstring(curpkgd, "pkgver", &opkgver); - xbps_dictionary_get_cstring(curpkgd, "architecture", &oarch); - ret = xbps_cmpver(pkgver, opkgver); + if (curpkgd && !force) { + const char *opkgver = NULL, *oarch = NULL; + int cmp; + xbps_dictionary_get_cstring_nocopy(curpkgd, "pkgver", &opkgver); + xbps_dictionary_get_cstring_nocopy(curpkgd, "architecture", &oarch); + + cmp = xbps_cmpver(pkgver, opkgver); + if (cmp < 0 && xbps_pkg_reverts(binpkgd, opkgver)) { /* * If the considered package reverts the package in the index, * consider the current package as the newer one. */ - if (ret < 0 && xbps_pkg_reverts(binpkgd, opkgver)) { - ret = 1; + cmp = 1; + } else if (cmp > 0 && xbps_pkg_reverts(curpkgd, pkgver)) { /* * If package in the index reverts considered package, consider the * package in the index as the newer one. */ - } else if (ret > 0 && xbps_pkg_reverts(curpkgd, pkgver)) { - ret = -1; - } - - if (ret <= 0) { - /* Same version or index version greater */ - fprintf(stderr, "index: skipping `%s' (%s), already registered.\n", pkgver, arch); - xbps_object_release(binpkgd); - free(opkgver); - free(oarch); - free(pkgver); - continue; - } - free(opkgver); - free(oarch); - } - /* - * Add additional objects for repository ops: - * - filename-size - * - filename-sha256 - */ - if (!xbps_file_sha256(sha256, sizeof sha256, pkg)) { - xbps_object_release(binpkgd); - free(pkgver); - rv = EINVAL; - goto out; + cmp = -1; } - if (!xbps_dictionary_set_cstring(binpkgd, "filename-sha256", sha256)) { - xbps_object_release(binpkgd); - free(pkgver); - rv = EINVAL; + if (cmp <= 0) { + fprintf(stderr, "index: skipping `%s' (%s), already registered.\n", pkgver, arch); goto out; } - if (stat(pkg, &st) == -1) { - xbps_object_release(binpkgd); - free(pkgver); - rv = EINVAL; - goto out; - } - if (!xbps_dictionary_set_uint64(binpkgd, "filename-size", (uint64_t)st.st_size)) { - xbps_object_release(binpkgd); - free(pkgver); - rv = EINVAL; - goto out; - } - /* Remove unneeded objects */ - xbps_dictionary_remove(binpkgd, "pkgname"); - xbps_dictionary_remove(binpkgd, "version"); - xbps_dictionary_remove(binpkgd, "packaged-with"); - - /* - * Add new pkg dictionary into the stage index - */ - if (!xbps_dictionary_set(idxstage, pkgname, binpkgd)) { - xbps_object_release(binpkgd); - free(pkgver); - rv = EINVAL; - goto out; - } - xbps_object_release(binpkgd); - free(pkgver); } + + if (!xbps_file_sha256(sha256, sizeof(sha256), file)) + goto err_errno; + if (!xbps_dictionary_set_cstring(binpkgd, "filename-sha256", sha256)) + goto err_errno; + if (stat(file, &st) == -1) + goto err_errno; + if (!xbps_dictionary_set_uint64(binpkgd, "filename-size", (uint64_t)st.st_size)) + goto err_errno; + + xbps_dictionary_remove(binpkgd, "pkgname"); + xbps_dictionary_remove(binpkgd, "version"); + xbps_dictionary_remove(binpkgd, "packaged-with"); + /* - * Generate repository data files. + * Add new pkg dictionary into the stage index */ - if (!repodata_commit(xhp, repodir, idx, idxmeta, idxstage, compression)) { - xbps_error_printf("%s: failed to write repodata: %s\n", - _XBPS_RINDEX, strerror(errno)); - goto out; - } - printf("index: %u packages registered.\n", xbps_dictionary_count(idx)); + if (!xbps_dictionary_set(stage, pkgname, binpkgd)) + goto err_errno; out: - xbps_object_release(idx); - xbps_object_release(idxstage); - if (idxmeta) - xbps_object_release(idxmeta); + xbps_object_release(binpkgd); + return 0; +err_errno: + r = -errno; +err: + xbps_object_release(binpkgd); + return r; +} + +int +index_add(struct xbps_handle *xhp, int args, int argc, char **argv, bool force, const char *compression) +{ + xbps_dictionary_t index, stage, meta; + struct xbps_repo *repo; + char *tmprepodir = NULL, *repodir = NULL; + int lockfd; + int r; + const char *repoarch = xhp->target_arch ? xhp->target_arch : xhp->native_arch; -earlyout: - if (repo) - xbps_repo_release(repo); - if (stage) - xbps_repo_release(stage); + if ((tmprepodir = strdup(argv[args])) == NULL) + return EXIT_FAILURE; + repodir = dirname(tmprepodir); - xbps_repo_unlock(rlockfd, rlockfname); + lockfd = xbps_repo_lock(repodir, repoarch); + if (lockfd < 0) { + xbps_error_printf("xbps-rindex: cannot lock repository " + "%s: %s\n", repodir, strerror(-lockfd)); + free(tmprepodir); + return EXIT_FAILURE; + } - if (tmprepodir) + repo = xbps_repo_open(xhp, repodir); + if (!repo && errno != ENOENT) { free(tmprepodir); + return EXIT_FAILURE; + } + + if (repo) { + index = xbps_dictionary_copy_mutable(repo->index); + stage = xbps_dictionary_copy_mutable(repo->stage); + meta = xbps_dictionary_copy_mutable(repo->idxmeta); + } else { + index = xbps_dictionary_create(); + stage = xbps_dictionary_create(); + meta = NULL; + } + + for (int i = args; i < argc; i++) { + r = index_add_pkg(xhp, index, stage, argv[i], force); + if (r < 0) + goto err2; + } + + r = repodata_commit(repodir, repoarch, index, stage, meta, compression); + if (r < 0) { + xbps_error_printf("failed to write repodata: %s\n", strerror(-r)); + goto err2; + } + printf("index: %u packages registered.\n", xbps_dictionary_count(index)); + + xbps_object_release(index); + xbps_object_release(stage); + if (meta) + xbps_object_release(meta); + xbps_repo_release(repo); + xbps_repo_unlock(repodir, repoarch, lockfd); + free(tmprepodir); + return EXIT_SUCCESS; - return rv; +err2: + xbps_object_release(index); + xbps_object_release(stage); + if (meta) + xbps_object_release(meta); + xbps_repo_release(repo); + xbps_repo_unlock(repodir, repoarch, lockfd); + free(tmprepodir); + return EXIT_FAILURE; } diff --git a/bin/xbps-rindex/index-clean.c b/bin/xbps-rindex/index-clean.c index 9e33e4095..0246e6968 100644 --- a/bin/xbps-rindex/index-clean.c +++ b/bin/xbps-rindex/index-clean.c @@ -23,26 +23,19 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include +#include +#include #include -#include #include #include -#include -#include -#include -#include -#include -#include #include #include "defs.h" -static xbps_dictionary_t dest; - -struct CleanerCbInfo { +struct cleaner_ctx { const char *repourl; bool hashcheck; + xbps_dictionary_t dict; }; static int @@ -52,79 +45,91 @@ idx_cleaner_cb(struct xbps_handle *xhp UNUSED, void *arg, bool *done UNUSED) { - struct CleanerCbInfo *info = arg; - const char *arch = NULL, *pkgver = NULL, *sha256 = NULL; - char *filen, pkgname[XBPS_NAME_SIZE]; + char path[PATH_MAX]; + char pkgname[XBPS_NAME_SIZE]; + struct cleaner_ctx *ctx = arg; + const char *arch = NULL, *pkgver = NULL; + int r; xbps_dictionary_get_cstring_nocopy(obj, "architecture", &arch); xbps_dictionary_get_cstring_nocopy(obj, "pkgver", &pkgver); - xbps_dbg_printf("%s: checking %s [%s] ...\n", info->repourl, pkgver, arch); + xbps_dbg_printf("%s: checking %s [%s] ...\n", ctx->repourl, pkgver, arch); - filen = xbps_xasprintf("%s/%s.%s.xbps", info->repourl, pkgver, arch); - if (access(filen, R_OK) == -1) { + r = snprintf(path, sizeof(path), "%s/%s.%s.xbps", ctx->repourl, pkgver, arch); + if (r < 0 || (size_t)r >= sizeof(path)) { + r = -ENAMETOOLONG; + xbps_error_printf("package path too long: %s: %s\n", path, strerror(-r)); + return r; + } + if (access(path, R_OK) == -1) { /* * File cannot be read, might be permissions, * broken or simply unexistent; either way, remove it. */ - if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver)) - goto out; - xbps_dictionary_remove(dest, pkgname); - printf("index: removed pkg %s\n", pkgver); - } else if (info->hashcheck) { + goto remove; + } + if (ctx->hashcheck) { + const char *sha256 = NULL; /* * File can be read; check its hash. */ - xbps_dictionary_get_cstring_nocopy(obj, - "filename-sha256", &sha256); - if (xbps_file_sha256_check(filen, sha256) != 0) { - if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver)) - goto out; - xbps_dictionary_remove(dest, pkgname); - printf("index: removed pkg %s\n", pkgver); - } + xbps_dictionary_get_cstring_nocopy(obj, "filename-sha256", &sha256); + if (xbps_file_sha256_check(path, sha256) != 0) + goto remove; } -out: - free(filen); + + return 0; +remove: + if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver)) { + xbps_error_printf("invalid pkgver: %s\n", pkgver); + return -EINVAL; + } + xbps_dictionary_remove(ctx->dict, pkgname); + printf("index: removed pkg %s\n", pkgver); return 0; } static int cleanup_repo(struct xbps_handle *xhp, const char *repodir, struct xbps_repo *repo, - const char *reponame, bool hashcheck, const char *compression) + bool hashcheck, const char *compression) { - int rv = 0; - xbps_array_t allkeys; - struct CleanerCbInfo info = { + struct cleaner_ctx ctx = { .hashcheck = hashcheck, .repourl = repodir }; + xbps_dictionary_t index, stage; + xbps_array_t allkeys; + int r = 0; + const char *repoarch = xhp->target_arch ? xhp->target_arch : xhp->native_arch; /* * First pass: find out obsolete entries on index and index-files. */ - dest = xbps_dictionary_copy_mutable(repo->idx); - allkeys = xbps_dictionary_all_keys(dest); - (void)xbps_array_foreach_cb_multi(xhp, allkeys, repo->idx, idx_cleaner_cb, &info); + index = xbps_dictionary_copy_mutable(repo->idx); + stage = xbps_dictionary_copy_mutable(repo->stage); + + allkeys = xbps_dictionary_all_keys(index); + ctx.dict = index; + (void)xbps_array_foreach_cb_multi(xhp, allkeys, repo->idx, idx_cleaner_cb, &ctx); xbps_object_release(allkeys); - if (strcmp("stagedata", reponame) == 0 && xbps_dictionary_count(dest) == 0) { - char *stagefile = xbps_repo_path_with_name(xhp, repodir, "stagedata"); - unlink(stagefile); - free(stagefile); - } - if (!xbps_dictionary_equals(dest, repo->idx)) { - if (!repodata_flush(xhp, repodir, reponame, dest, repo->idxmeta, compression)) { - rv = errno; - xbps_error_printf("failed to write repodata: %s\n", - strerror(errno)); - return rv; - } + allkeys = xbps_dictionary_all_keys(stage); + ctx.dict = stage; + (void)xbps_array_foreach_cb_multi(xhp, allkeys, repo->idx, idx_cleaner_cb, &ctx); + xbps_object_release(allkeys); + + if (xbps_dictionary_equals(index, repo->index) && + xbps_dictionary_equals(stage, repo->stage)) + return 0; + + r = repodata_flush(repodir, repoarch, index, stage, repo->idxmeta, compression); + if (r < 0) { + xbps_error_printf("failed to write repodata: %s\n", strerror(-r)); + return r; } - if (strcmp("stagedata", reponame) == 0) - printf("stage: %u packages registered.\n", xbps_dictionary_count(dest)); - else - printf("index: %u packages registered.\n", xbps_dictionary_count(dest)); - return rv; + printf("stage: %u packages registered.\n", xbps_dictionary_count(stage)); + printf("index: %u packages registered.\n", xbps_dictionary_count(index)); + return r; } /* @@ -134,48 +139,33 @@ cleanup_repo(struct xbps_handle *xhp, const char *repodir, struct xbps_repo *rep int index_clean(struct xbps_handle *xhp, const char *repodir, const bool hashcheck, const char *compression) { - struct xbps_repo *repo, *stage; - char *rlockfname = NULL; - int rv = 0, rlockfd = -1; - - if (!xbps_repo_lock(xhp, repodir, &rlockfd, &rlockfname)) { - rv = errno; - xbps_error_printf("%s: cannot lock repository: %s\n", - _XBPS_RINDEX, strerror(rv)); - return rv; + struct xbps_repo *repo; + const char *arch = xhp->target_arch ? xhp->target_arch : xhp->native_arch; + int lockfd; + int r; + + lockfd = xbps_repo_lock(repodir, arch); + if (lockfd < 0) { + xbps_error_printf("cannot lock repository: %s\n", strerror(-lockfd)); + return EXIT_FAILURE; } - repo = xbps_repo_public_open(xhp, repodir); - if (repo == NULL) { - rv = errno; - if (rv == ENOENT) { - xbps_repo_unlock(rlockfd, rlockfname); + + repo = xbps_repo_open(xhp, repodir); + if (!repo) { + r = -errno; + if (r == -ENOENT) { + xbps_repo_unlock(repodir, arch, lockfd); return 0; } - xbps_error_printf("%s: cannot read repository data: %s\n", - _XBPS_RINDEX, strerror(errno)); - xbps_repo_unlock(rlockfd, rlockfname); - return rv; - } - stage = xbps_repo_stage_open(xhp, repodir); - if (repo->idx == NULL || (stage && stage->idx == NULL)) { - xbps_error_printf("%s: incomplete repository data file!\n", _XBPS_RINDEX); - rv = EINVAL; - goto out; + xbps_error_printf("cannot read repository data: %s\n", + strerror(errno)); + xbps_repo_unlock(repodir, arch, lockfd); + return EXIT_FAILURE; } printf("Cleaning `%s' index, please wait...\n", repodir); - if ((rv = cleanup_repo(xhp, repodir, repo, "repodata", hashcheck, compression))) { - goto out; - } - if (stage) { - cleanup_repo(xhp, repodir, stage, "stagedata", hashcheck, compression); - } - -out: + r = cleanup_repo(xhp, repodir, repo, hashcheck, compression); xbps_repo_release(repo); - if(stage) - xbps_repo_release(stage); - xbps_repo_unlock(rlockfd, rlockfname); - - return rv; + xbps_repo_unlock(repodir, arch, lockfd); + return r ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/bin/xbps-rindex/main.c b/bin/xbps-rindex/main.c index da80d3d00..0f9ed3166 100644 --- a/bin/xbps-rindex/main.c +++ b/bin/xbps-rindex/main.c @@ -170,5 +170,5 @@ main(int argc, char **argv) else if (sign_pkg_mode) rv = sign_pkgs(&xh, optind, argc, argv, privkey, force); - exit(rv ? EXIT_FAILURE : EXIT_SUCCESS); + exit(rv); } diff --git a/bin/xbps-rindex/remove-obsoletes.c b/bin/xbps-rindex/remove-obsoletes.c index 913c04292..c65e35d83 100644 --- a/bin/xbps-rindex/remove-obsoletes.c +++ b/bin/xbps-rindex/remove-obsoletes.c @@ -24,6 +24,7 @@ */ #include + #include #include #include @@ -32,144 +33,178 @@ #include #include #include +#include #include #include "defs.h" +#include "xbps/xbps_dictionary.h" +#include "xbps/xbps_object.h" +#include "xbps_api_impl.h" static int remove_pkg(const char *repodir, const char *file) { - char *filepath, *sigpath, *sig2path; - int rv = 0; - - filepath = xbps_xasprintf("%s/%s", repodir, file); - sigpath = xbps_xasprintf("%s.sig", filepath); - sig2path = xbps_xasprintf("%s.sig2", filepath); - if (remove(filepath) == -1) { - if (errno != ENOENT) { - rv = errno; - xbps_error_printf("xbps-rindex: failed to remove " - "package `%s': %s\n", file, strerror(rv)); - } + char filepath[PATH_MAX]; + int r; + + r = snprintf(filepath, sizeof(filepath), "%s/%s", repodir, file); + if (r < 0 || (size_t)r >= sizeof(r)) { + r = -ENAMETOOLONG; + goto err; } - if (remove(sigpath) == -1) { - if (errno != ENOENT) { - rv = errno; - xbps_error_printf("xbps-rindex: failed to remove " - "legacy package signature `%s': %s\n", sigpath, strerror(rv)); - } + if (remove(filepath) == -1 && errno != ENOENT) { + r = -errno; + goto err; } - if (remove(sig2path) == -1) { - if (errno != ENOENT) { - rv = errno; - xbps_error_printf("xbps-rindex: failed to remove " - "package signature `%s': %s\n", sig2path, strerror(rv)); - } + return 0; +err: + xbps_error_printf("failed to remove package: %s: %s\n", + filepath, strerror(-r)); + return r; +} + +static int +remove_sig(const char *repodir, const char *file, const char *suffix) +{ + char sigpath[PATH_MAX]; + int r; + + r = snprintf(sigpath, sizeof(sigpath), "%s/%s.%s", repodir, file, suffix); + if (r < 0 || (size_t)r >= sizeof(r)) { + r = -ENAMETOOLONG; + goto err; + } + if (remove(sigpath) == -1 && errno != ENOENT) { + r = -errno; + goto err; } - free(sigpath); - free(sig2path); - free(filepath); + return 0; +err: + xbps_error_printf("failed to remove package: %s: %s\n", + sigpath, strerror(-r)); + return r; +} + +static bool +index_match_pkgver(xbps_dictionary_t index, const char *pkgname, const char *pkgver) +{ + xbps_dictionary_t pkgd; + const char *dict_pkgver; + + pkgd = xbps_dictionary_get(index, pkgname); + if (!pkgd) + return false; - return rv; + xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &dict_pkgver); + return strcmp(dict_pkgver, pkgver) == 0; } static int cleaner_cb(struct xbps_handle *xhp, xbps_object_t obj, const char *key UNUSED, void *arg, bool *done UNUSED) { - struct xbps_repo *repo = ((struct xbps_repo **)arg)[0], *stage = ((struct xbps_repo **)arg)[1]; + char pkgname[XBPS_NAME_SIZE]; + struct xbps_repo *repo = arg; const char *binpkg; - char *pkgver, *arch = NULL; - int rv; + char *pkgver; binpkg = xbps_string_cstring_nocopy(obj); - if (access(binpkg, R_OK) == -1) { - if (errno == ENOENT) { - if ((rv = remove_pkg(repo->uri, binpkg)) != 0) - return 0; - - printf("Removed broken package `%s'.\n", binpkg); - } - } - arch = xbps_binpkg_arch(binpkg); - assert(arch); - /* ignore pkgs from other archs */ - if (!xbps_pkg_arch_match(xhp, arch, NULL)) { - free(arch); + pkgver = xbps_binpkg_pkgver(binpkg); + if (!pkgver || !xbps_pkg_name(pkgname, sizeof(pkgname), pkgver)) { + xbps_warn_printf("%s: invalid pkgver in xbps filename\n", binpkg); return 0; } - free(arch); - pkgver = xbps_binpkg_pkgver(binpkg); - assert(pkgver); if (xhp->flags & XBPS_FLAG_VERBOSE) printf("checking %s (%s)\n", pkgver, binpkg); - /* - * If binpkg is not registered in index, remove binpkg. - */ - if (!xbps_repo_get_pkg(repo, pkgver) && !(stage && xbps_repo_get_pkg(stage, pkgver))) { - if ((rv = remove_pkg(repo->uri, binpkg)) != 0) { - free(pkgver); - return 0; - } - printf("Removed obsolete package `%s'.\n", binpkg); + + if (index_match_pkgver(repo->stage, pkgname, pkgver) || + index_match_pkgver(repo->index, pkgname, pkgver)) { + free(pkgver); + return 0; } free(pkgver); + + remove_pkg(repo->uri, binpkg); + remove_sig(repo->uri, binpkg, "sig"); + remove_sig(repo->uri, binpkg, "sig2"); + + printf("Removed obsolete package `%s'.\n", binpkg); return 0; } +static bool +match_suffix(const char *str, size_t len, const char *suffix, size_t suffixlen) +{ + if (len < suffixlen) + return false; + return strcmp(str+len-suffixlen, suffix) == 0; +} + int remove_obsoletes(struct xbps_handle *xhp, const char *repodir) { xbps_array_t array = NULL; - struct xbps_repo *repos[2], *repo, *stage; + struct xbps_repo *repo; DIR *dirp; struct dirent *dp; - char *ext; - int rv = 0; + int r; + char suffix[NAME_MAX]; + int suffixlen; - repo = xbps_repo_public_open(xhp, repodir); + repo = xbps_repo_open(xhp, repodir); if (repo == NULL) { - if (errno != ENOENT) { - xbps_error_printf("xbps-rindex: cannot read repository data: %s\n", - strerror(errno)); - return -1; - } - return 0; - } - stage = xbps_repo_stage_open(xhp, repodir); - if (chdir(repodir) == -1) { - xbps_error_printf("xbps-rindex: cannot chdir to %s: %s\n", - repodir, strerror(errno)); - rv = errno; - goto out; + if (errno != ENOENT) + return EXIT_FAILURE; + return EXIT_SUCCESS; } + if ((dirp = opendir(".")) == NULL) { xbps_error_printf("xbps-rindex: failed to open %s: %s\n", repodir, strerror(errno)); - rv = errno; - goto out; + goto err; + } + + array = xbps_array_create(); + if (!array) { + xbps_error_printf("failed to allocate array: %s\n", strerror(-errno)); + goto err; + } + + suffixlen = snprintf(suffix, sizeof(suffix), ".%s.xbps", + xhp->target_arch ? xhp->target_arch : xhp->native_arch); + if (suffixlen < 0 || (size_t)suffixlen >= sizeof(suffix)) { + xbps_error_printf("failed to create pacakge suffix: %s", strerror(ENAMETOOLONG)); + goto err; } + while ((dp = readdir(dirp))) { - if (strcmp(dp->d_name, "..") == 0) - continue; - if ((ext = strrchr(dp->d_name, '.')) == NULL) + size_t len; + + if (dp->d_name[0] == '.') continue; - if (strcmp(ext, ".xbps")) + + len = strlen(dp->d_name); + if (!match_suffix(dp->d_name, len, suffix, suffixlen) && + !match_suffix(dp->d_name, len, ".noarch.xbps", sizeof(".noarch.xbps") - 1)) continue; - if (array == NULL) - array = xbps_array_create(); - xbps_array_add_cstring(array, dp->d_name); + if (!xbps_array_add_cstring(array, dp->d_name)) { + xbps_error_printf("failed to add string to array: %s\n", + strerror(-errno)); + goto err; + } } - (void)closedir(dirp); + closedir(dirp); + + r = xbps_array_foreach_cb_multi(xhp, array, NULL, cleaner_cb, repo); + if (r < 0) + goto err; - repos[0] = repo; - repos[1] = stage; - rv = xbps_array_foreach_cb_multi(xhp, array, NULL, cleaner_cb, repos); -out: xbps_repo_release(repo); - xbps_repo_release(stage); xbps_object_release(array); - - return rv; + return EXIT_SUCCESS; +err: + xbps_repo_release(repo); + xbps_object_release(array); + return EXIT_FAILURE; } diff --git a/bin/xbps-rindex/repoflush.c b/bin/xbps-rindex/repoflush.c index be7bd7705..14ddd7eae 100644 --- a/bin/xbps-rindex/repoflush.c +++ b/bin/xbps-rindex/repoflush.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2013-2019 Juan Romero Pardines. + * Copyright (c) 2023 Duncan Overbruck . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,45 +25,32 @@ */ #include -#include +#include +#include +#include +#include +#include +#include #include +#include #include #include -#include -#include -#include -#include -#include + +#include #include + #include "defs.h" -bool -repodata_flush(struct xbps_handle *xhp, const char *repodir, - const char *reponame, xbps_dictionary_t idx, xbps_dictionary_t meta, - const char *compression) +static struct archive * +open_archive(int fd, const char *compression) { struct archive *ar; - char *repofile, *tname, *buf; - int rv, repofd = -1; - mode_t mask; - bool result; - - /* Create a tempfile for our repository archive */ - repofile = xbps_repo_path_with_name(xhp, repodir, reponame); - assert(repofile); - tname = xbps_xasprintf("%s.XXXXXXXXXX", repofile); - assert(tname); - mask = umask(S_IXUSR|S_IRWXG|S_IRWXO); - if ((repofd = mkstemp(tname)) == -1) - return false; - - umask(mask); - /* Create and write our repository archive */ - ar = archive_write_new(); - if (ar == NULL) - return false; + int r; + ar = archive_write_new(); + if (!ar) + return NULL; /* * Set compression format, zstd by default. */ @@ -84,64 +72,158 @@ repodata_flush(struct xbps_handle *xhp, const char *repodir, } else if (strcmp(compression, "none") == 0) { /* empty */ } else { - return false; + archive_write_free(ar); + errno = EINVAL; + return NULL; } archive_write_set_format_pax_restricted(ar); - if (archive_write_open_fd(ar, repofd) != ARCHIVE_OK) - return false; - - /* XBPS_REPOIDX */ - buf = xbps_dictionary_externalize(idx); - if (buf == NULL) - return false; - rv = xbps_archive_append_buf(ar, buf, strlen(buf), - XBPS_REPOIDX, 0644, "root", "root"); - free(buf); - if (rv != 0) - return false; - - /* XBPS_REPOIDX_META */ - if (meta == NULL) { - /* fake entry */ - buf = strdup("DEADBEEF"); - if (buf == NULL) - return false; - } else { - buf = xbps_dictionary_externalize(meta); + r = archive_write_open_fd(ar, fd); + if (r != ARCHIVE_OK) { + r = -archive_errno(ar); + archive_write_free(ar); + errno = -r; + return NULL; + } + + return ar; +} + +static int +archive_dict(struct archive *ar, const char *filename, xbps_dictionary_t dict) +{ + char *buf; + int r; + + if (xbps_dictionary_count(dict) == 0) { + r = xbps_archive_append_buf(ar, "", 0, filename, 0644, + "root", "root"); + if (r < 0) + return r; + return 0; } - rv = xbps_archive_append_buf(ar, buf, strlen(buf), - XBPS_REPOIDX_META, 0644, "root", "root"); + + errno = 0; + buf = xbps_dictionary_externalize(dict); + if (!buf) { + r = -errno; + xbps_error_printf("failed to externalize dictionary for: %s\n", + filename); + if (r == 0) + return -EINVAL; + return 0; + } + + r = xbps_archive_append_buf(ar, buf, strlen(buf), filename, 0644, + "root", "root"); + free(buf); - if (rv != 0) - return false; + + if (r < 0) { + xbps_error_printf("failed to write archive entry: %s: %s\n", + filename, strerror(-r)); + } + return r; +} + +int +repodata_flush(const char *repodir, + const char *arch, + xbps_dictionary_t index, + xbps_dictionary_t stage, + xbps_dictionary_t meta, + const char *compression) +{ + char path[PATH_MAX]; + char tmp[PATH_MAX]; + struct archive *ar = NULL; + mode_t prevumask; + int r; + int fd; + + r = snprintf(path, sizeof(path), "%s/%s-repodata", repodir, arch); + if (r < 0 || (size_t)r >= sizeof(tmp)) { + xbps_error_printf("repodata path too long: %s: %s\n", path, + strerror(ENAMETOOLONG)); + return -ENAMETOOLONG; + } + + r = snprintf(tmp, sizeof(tmp), "%s.XXXXXXX", path); + if (r < 0 || (size_t)r >= sizeof(tmp)) { + xbps_error_printf("repodata tmp path too long: %s: %s\n", path, + strerror(ENAMETOOLONG)); + return -ENAMETOOLONG; + } + + prevumask = umask(S_IXUSR|S_IRWXG|S_IRWXO); + fd = mkstemp(tmp); + if (fd == -1) { + r = -errno; + xbps_error_printf("failed to open temp file: %s: %s", tmp, strerror(-r)); + umask(prevumask); + goto err; + } + umask(prevumask); + + ar = open_archive(fd, compression); + if (!ar) { + r = -errno; + goto err; + } + + r = archive_dict(ar, XBPS_REPODATA_INDEX, index); + if (r < 0) + goto err; + r = archive_dict(ar, XBPS_REPODATA_META, meta); + if (r < 0) + goto err; + r = archive_dict(ar, XBPS_REPODATA_STAGE, stage); + if (r < 0) + goto err; /* Write data to tempfile and rename */ - if (archive_write_close(ar) != ARCHIVE_OK) - return false; - if (archive_write_free(ar) != ARCHIVE_OK) - return false; + if (archive_write_close(ar) == ARCHIVE_FATAL) { + r = -archive_errno(ar); + xbps_error_printf("failed to close archive: %s\n", archive_error_string(ar)); + goto err; + } + if (archive_write_free(ar) == ARCHIVE_FATAL) { + r = -errno; + xbps_error_printf("failed to free archive: %s\n", strerror(-r)); + goto err; + } + #ifdef HAVE_FDATASYNC - fdatasync(repofd); + fdatasync(fd); #else - fsync(repofd); + fsync(fd); #endif - if (fchmod(repofd, 0664) == -1) { - close(repofd); - unlink(tname); - result = false; - goto out; + + if (fchmod(fd, 0664) == -1) { + errno = -r; + xbps_error_printf("failed to set mode: %s: %s\n", + tmp, strerror(-r)); + close(fd); + unlink(tmp); + return r; } - close(repofd); - if (rename(tname, repofile) == -1) { - unlink(tname); - result = false; - goto out; + close(fd); + + if (rename(tmp, path) == -1) { + r = -errno; + xbps_error_printf("failed to rename repodata: %s: %s: %s\n", + tmp, path, strerror(-r)); + unlink(tmp); + return r; } - result = true; -out: - free(repofile); - free(tname); + return 0; - return result; +err: + if (ar) { + archive_write_close(ar); + archive_write_free(ar); + } + close(fd); + unlink(tmp); + return r; } diff --git a/bin/xbps-rindex/sign.c b/bin/xbps-rindex/sign.c index 7e6cd2ce2..8c0fce8e9 100644 --- a/bin/xbps-rindex/sign.c +++ b/bin/xbps-rindex/sign.c @@ -37,6 +37,7 @@ #include #include "defs.h" +#include "xbps.h" static RSA * load_rsa_privkey(const char *path) @@ -151,9 +152,11 @@ sign_repo(struct xbps_handle *xhp, const char *repodir, RSA *rsa = NULL; uint16_t rpubkeysize, pubkeysize; const char *rsignedby = NULL; - char *buf = NULL, *rlockfname = NULL; - int rlockfd = -1, rv = 0; - bool flush_failed = false, flush = false; + char *buf = NULL; + int lockfd = -1; + int r = 0; + bool flush = false; + const char *repoarch = xhp->target_arch ? xhp->target_arch : xhp->native_arch; if (signedby == NULL) { xbps_error_printf("--signedby unset! cannot initialize signed repository\n"); @@ -165,14 +168,14 @@ sign_repo(struct xbps_handle *xhp, const char *repodir, */ repo = xbps_repo_open(xhp, repodir); if (repo == NULL) { - rv = errno; + r = -errno; xbps_error_printf("%s: cannot read repository data: %s\n", _XBPS_RINDEX, strerror(errno)); goto out; } if (xbps_dictionary_count(repo->idx) == 0) { + r = -EINVAL; xbps_error_printf("%s: invalid repository, exiting!\n", _XBPS_RINDEX); - rv = EINVAL; goto out; } @@ -184,10 +187,15 @@ sign_repo(struct xbps_handle *xhp, const char *repodir, * current state. */ if ((buf = pubkey_from_privkey(rsa)) == NULL) { - rv = EINVAL; + r = -EINVAL; goto out; } + meta = xbps_dictionary_create(); + if (!meta) { + r = -ENOMEM; + goto out; + } data = xbps_data_create_data(buf, strlen(buf)); rpubkey = xbps_dictionary_get(repo->idxmeta, "public-key"); @@ -216,15 +224,15 @@ sign_repo(struct xbps_handle *xhp, const char *repodir, data = NULL; /* lock repository to write repodata file */ - if (!xbps_repo_lock(xhp, repodir, &rlockfd, &rlockfname)) { - rv = errno; - xbps_error_printf("%s: cannot lock repository: %s\n", - _XBPS_RINDEX, strerror(errno)); + lockfd = xbps_repo_lock(repodir, repoarch); + if (lockfd < 0) { + r = errno; + xbps_error_printf("cannot lock repository: %s\n", strerror(errno)); goto out; } - flush_failed = repodata_flush(xhp, repodir, "repodata", repo->idx, meta, compression); - xbps_repo_unlock(rlockfd, rlockfname); - if (!flush_failed) { + r = repodata_flush(repodir, repoarch, repo->index, repo->stage, meta, compression); + xbps_repo_unlock(repodir, repoarch, lockfd); + if (r < 0) { xbps_error_printf("failed to write repodata: %s\n", strerror(errno)); goto out; } @@ -240,7 +248,7 @@ sign_repo(struct xbps_handle *xhp, const char *repodir, if (repo) xbps_repo_release(repo); - return rv ? -1 : 0; + return r; } static int From 4576ef080c8a56a0a2b4974481821e8b3a0d0f9f Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Sun, 29 Oct 2023 00:02:14 +0200 Subject: [PATCH 3/8] bin/xbps-query: for -L show if repositories are staged --- bin/xbps-query/list.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/xbps-query/list.c b/bin/xbps-query/list.c index c22ae00de..cf853314c 100644 --- a/bin/xbps-query/list.c +++ b/bin/xbps-query/list.c @@ -188,6 +188,8 @@ repo_list_uri(struct xbps_repo *repo) printf("%5zd %s", repo->idx ? (ssize_t)xbps_dictionary_count(repo->idx) : -1, repo->uri); + if (repo->stage && xbps_dictionary_count(repo->stage) > 0) + printf(" (Staged)"); printf(" (RSA %s)\n", repo->is_signed ? "signed" : "unsigned"); if (repo->xhp->flags & XBPS_FLAG_VERBOSE) { xbps_data_t pubkey; From c1c770a9e7df399752105eb6df4230290f018f5b Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Sun, 29 Oct 2023 00:03:22 +0200 Subject: [PATCH 4/8] tests: move the repository stage into the repodata file --- tests/xbps/xbps-rindex/add_test.sh | 59 +++++++++++---------------- tests/xbps/xbps-rindex/clean_test.sh | 10 ++--- tests/xbps/xbps-rindex/remove_test.sh | 5 +-- 3 files changed, 31 insertions(+), 43 deletions(-) diff --git a/tests/xbps/xbps-rindex/add_test.sh b/tests/xbps/xbps-rindex/add_test.sh index 37e5c141f..089c5eaa4 100644 --- a/tests/xbps/xbps-rindex/add_test.sh +++ b/tests/xbps/xbps-rindex/add_test.sh @@ -42,24 +42,13 @@ revert_body() { mkdir -p some_repo pkg_A touch pkg_A/file00 cd some_repo - xbps-create -A noarch -n foo-1.1_1 -s "foo pkg" ../pkg_A - atf_check_equal $? 0 - xbps-rindex -d -a $PWD/*.xbps - atf_check_equal $? 0 - xbps-create -A noarch -n foo-1.0_1 -r "1.1_1" -s "foo pkg" ../pkg_A - atf_check_equal $? 0 - xbps-rindex -d -a $PWD/*.xbps - atf_check_equal $? 0 + atf_check -o ignore -e ignore -- xbps-create -A noarch -n foo-1.1_1 -s "foo pkg" ../pkg_A + atf_check -o ignore -e ignore -- xbps-rindex -d -a $PWD/*.xbps + atf_check -o ignore -e ignore -- xbps-create -A noarch -n foo-1.0_1 -r "1.1_1" -s "foo pkg" ../pkg_A + atf_check -o ignore -e ignore -- xbps-rindex -d -a $PWD/*.xbps cd .. - result="$(xbps-query -r root -C empty.conf --repository=some_repo -s '')" - expected="[-] foo-1.0_1 foo pkg" - rv=0 - if [ "$result" != "$expected" ]; then - echo "result: $result" - echo "expected: $expected" - rv=1 - fi - atf_check_equal $rv 0 + atf_check -o "inline:[-] foo-1.0_1 foo pkg\n" -e empty -- \ + xbps-query -r root -C empty.conf --repository=some_repo -Rs '' } atf_test_case stage @@ -76,36 +65,36 @@ stage_body() { atf_check_equal $? 0 xbps-rindex -d -a $PWD/*.xbps atf_check_equal $? 0 - [ -f *-stagedata ] - atf_check_equal $? 1 + atf_check -o inline:" 1 $PWD (RSA unsigned)\n" -- \ + xbps-query -r ../root -i --repository=$PWD -L xbps-create -A noarch -n foo-1.1_1 -s "foo pkg" --shlib-provides "libfoo.so.2" ../pkg_A atf_check_equal $? 0 xbps-rindex -d -a $PWD/*.xbps atf_check_equal $? 0 - [ -f *-stagedata ] - atf_check_equal $? 1 + atf_check -o inline:" 1 $PWD (RSA unsigned)\n" -- \ + xbps-query -r ../root -i --repository=$PWD -L xbps-create -A noarch -n bar-1.0_1 -s "foo pkg" --shlib-requires "libfoo.so.2" ../pkg_B atf_check_equal $? 0 xbps-rindex -d -a $PWD/*.xbps atf_check_equal $? 0 - [ -f *-stagedata ] - atf_check_equal $? 1 + atf_check -o inline:" 2 $PWD (RSA unsigned)\n" -- \ + xbps-query -r ../root -i --repository=$PWD -L xbps-create -A noarch -n foo-1.2_1 -s "foo pkg" --shlib-provides "libfoo.so.3" ../pkg_A atf_check_equal $? 0 xbps-rindex -d -a $PWD/*.xbps atf_check_equal $? 0 - [ -f *-stagedata ] - atf_check_equal $? 0 + atf_check -o inline:" 2 $PWD (Staged) (RSA unsigned)\n" -- \ + xbps-query -r ../root -i --repository=$PWD -L xbps-create -A noarch -n bar-1.1_1 -s "foo pkg" --shlib-requires "libfoo.so.3" ../pkg_A atf_check_equal $? 0 xbps-rindex -d -a $PWD/*.xbps atf_check_equal $? 0 - [ -f *-stagedata ] - atf_check_equal $? 1 + atf_check -o inline:" 2 $PWD (RSA unsigned)\n" -- \ + xbps-query -r ../root -i --repository=$PWD -L } atf_test_case stage_resolve_bug @@ -136,16 +125,16 @@ stage_resolve_bug_body() { atf_check_equal $? 0 xbps-rindex -d -a $PWD/*.xbps atf_check_equal $? 0 - [ -f *-stagedata ] - atf_check_equal $? 1 + atf_check -o inline:" 4 $PWD (RSA unsigned)\n" -- \ + xbps-query -r ../root -i --repository=$PWD -L # trigger staging xbps-create -A noarch -n provider-1.0_2 -s "foo pkg" --shlib-provides "libfoo.so.1" ../provider atf_check_equal $? 0 xbps-rindex -d -a $PWD/*.xbps atf_check_equal $? 0 - [ -f *-stagedata ] - atf_check_equal $? 0 + atf_check -o inline:" 4 $PWD (Staged) (RSA unsigned)\n" -- \ + xbps-query -r ../root -i --repository=$PWD -L # then add a new provider not containing the provides field. This resulted in # a stage state despites the library is resolved through libprovides @@ -153,8 +142,8 @@ stage_resolve_bug_body() { atf_check_equal $? 0 xbps-rindex -d -a $PWD/*.xbps atf_check_equal $? 0 - [ -f *-stagedata ] - atf_check_equal $? 0 + atf_check -o inline:" 4 $PWD (Staged) (RSA unsigned)\n" -- \ + xbps-query -r ../root -i --repository=$PWD -L # resolve staging # the actual bug appeared here: libfoo.so.1 is still provided by libprovider, but @@ -163,8 +152,8 @@ stage_resolve_bug_body() { atf_check_equal $? 0 xbps-rindex -d -a $PWD/*.xbps atf_check_equal $? 0 - [ -f *-stagedata ] - atf_check_equal $? 1 + atf_check -o inline:" 4 $PWD (RSA unsigned)\n" -- \ + xbps-query -r ../root -i --repository=$PWD -L } atf_init_test_cases() { diff --git a/tests/xbps/xbps-rindex/clean_test.sh b/tests/xbps/xbps-rindex/clean_test.sh index e7cb0253d..6c5ca6365 100644 --- a/tests/xbps/xbps-rindex/clean_test.sh +++ b/tests/xbps/xbps-rindex/clean_test.sh @@ -69,14 +69,14 @@ remove_from_stage_body() { xbps-create -A noarch -n foo-1.1_1 -s "foo pkg" --shlib-provides "foo.so.2" ../pkg_A atf_check_equal $? 0 xbps-rindex -d -a $PWD/*.xbps - atf_check_equal $? 0 - [ -f *-stagedata ] + atf_check -o inline:" 2 $PWD (Staged) (RSA unsigned)\n" -- \ + xbps-query -r ../root -i --repository=$PWD -L atf_check_equal $? 0 rm foo-1.1_1* + xbps-rindex -c . + atf_check -o inline:" 1 $PWD (RSA unsigned)\n" -- \ + xbps-query -r ../root -i --repository=$PWD -L cd .. - xbps-rindex -c some_repo - [ -f *-stagedata ] - atf_check_equal $? 1 } atf_init_test_cases() { diff --git a/tests/xbps/xbps-rindex/remove_test.sh b/tests/xbps/xbps-rindex/remove_test.sh index 897466150..9a886ef46 100644 --- a/tests/xbps/xbps-rindex/remove_test.sh +++ b/tests/xbps/xbps-rindex/remove_test.sh @@ -21,11 +21,10 @@ noremove_stage_body() { atf_check_equal $? 0 xbps-rindex -d -a $PWD/*.xbps atf_check_equal $? 0 - [ -f *-stagedata ] - atf_check_equal $? 0 + atf_check -o inline:" 2 $PWD (Staged) (RSA unsigned)\n" -- \ + xbps-query -r ../root -i --repository=$PWD -L xbps-rindex -r some_repo atf_check_equal $? 0 - cd some_repo [ -f foo-1.0_1* ] atf_check_equal $? 0 [ -f foo-1.1_1* ] From 354ce90257599f4b8ec73d6ddd2f1397ec2ea287 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Wed, 1 Nov 2023 00:38:00 +0100 Subject: [PATCH 5/8] lib: add xbps flag to enable using staged remote repositories --- data/xbps.d.5 | 2 ++ include/xbps.h.in | 7 +++++++ lib/conf.c | 11 +++++++++++ lib/initend.c | 8 ++++++++ lib/repo.c | 6 ++---- 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/data/xbps.d.5 b/data/xbps.d.5 index 6e671847c..3f02a61d1 100644 --- a/data/xbps.d.5 +++ b/data/xbps.d.5 @@ -121,6 +121,8 @@ example: .El .It Sy rootdir=path Sets the default root directory. +.It Sy staged=true|false +Enables or disables the use of staged packages in remote repositories. .It Sy syslog=true|false Enables or disables syslog logging. Enabled by default. .It Sy virtualpkg=[vpkgname|vpkgver]:pkgname diff --git a/include/xbps.h.in b/include/xbps.h.in index 783dfe62d..3e351c630 100644 --- a/include/xbps.h.in +++ b/include/xbps.h.in @@ -246,6 +246,13 @@ */ #define XBPS_FLAG_KEEP_CONFIG 0x00010000 +/** + * @def XBPS_FLAG_USE_STAGE + * Use the staging repository if available. + * Must be set through the xbps_handle::flags member. + */ +#define XBPS_FLAG_USE_STAGE 0x00020000 + /** * @def XBPS_FETCH_CACHECONN * Default (global) limit of cached connections used in libfetch. diff --git a/lib/conf.c b/lib/conf.c index f9724db81..6fa32bd73 100644 --- a/lib/conf.c +++ b/lib/conf.c @@ -161,6 +161,7 @@ enum { KEY_PRESERVE, KEY_REPOSITORY, KEY_ROOTDIR, + KEY_STAGED, KEY_SYSLOG, KEY_VIRTUALPKG, KEY_KEEPCONF, @@ -181,6 +182,7 @@ static const struct key { { "preserve", 8, KEY_PRESERVE }, { "repository", 10, KEY_REPOSITORY }, { "rootdir", 7, KEY_ROOTDIR }, + { "staged", 6, KEY_STAGED }, { "syslog", 6, KEY_SYSLOG }, { "virtualpkg", 10, KEY_VIRTUALPKG }, }; @@ -341,6 +343,15 @@ parse_file(struct xbps_handle *xhp, const char *path, bool nested) xbps_dbg_printf("%s: native architecture set to %s\n", path, val); break; + case KEY_STAGED: + if (strcasecmp(val, "true") == 0) { + xhp->flags |= XBPS_FLAG_USE_STAGE; + xbps_dbg_printf("%s: repository stage enabled\n", path); + } else { + xhp->flags &= ~XBPS_FLAG_USE_STAGE; + xbps_dbg_printf("%s: repository stage disabled\n", path); + } + break; case KEY_SYSLOG: if (strcasecmp(val, "true") == 0) { xhp->flags &= ~XBPS_FLAG_DISABLE_SYSLOG; diff --git a/lib/initend.c b/lib/initend.c index 70ee26cb3..b74b3374e 100644 --- a/lib/initend.c +++ b/lib/initend.c @@ -163,6 +163,14 @@ xbps_init(struct xbps_handle *xhp) xhp->flags |= XBPS_FLAG_DISABLE_SYSLOG; } + p = getenv("XBPS_STAGED"); + if (p) { + if (strcasecmp(p, "true") == 0) + xhp->flags &= ~XBPS_FLAG_USE_STAGE; + else if (strcasecmp(p, "false") == 0) + xhp->flags |= XBPS_FLAG_USE_STAGE; + } + if (xhp->flags & XBPS_FLAG_DEBUG) { const char *repodir; for (unsigned int i = 0; i < xbps_array_count(xhp->repositories); i++) { diff --git a/lib/repo.c b/lib/repo.c index 76decbfd2..32e5c6163 100644 --- a/lib/repo.c +++ b/lib/repo.c @@ -507,15 +507,13 @@ xbps_repo_open(struct xbps_handle *xhp, const char *url) return NULL; } - if (repo->is_remote || xbps_dictionary_count(repo->stage) == 0) { + if (xbps_dictionary_count(repo->stage) == 0 || + (repo->is_remote && !(xhp->flags & XBPS_FLAG_USE_STAGE))) { repo->idx = repo->index; xbps_object_retain(repo->idx); return repo; } - /* - * Load and merge staging repository if the repository is local. - */ r = repo_merge_stage(repo); if (r < 0) { xbps_error_printf( From ff468c6e2ecd791cc9f524ed9764994ff2e1d9a1 Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Wed, 1 Nov 2023 00:40:34 +0100 Subject: [PATCH 6/8] bin/xbps-install: add --staged flag --- bin/xbps-install/main.c | 5 +++++ bin/xbps-install/xbps-install.1 | 2 ++ 2 files changed, 7 insertions(+) diff --git a/bin/xbps-install/main.c b/bin/xbps-install/main.c index 639847a6f..108805853 100644 --- a/bin/xbps-install/main.c +++ b/bin/xbps-install/main.c @@ -61,6 +61,7 @@ usage(bool fail) " This option can be specified multiple times\n" " -r, --rootdir Full path to rootdir\n" " --reproducible Enable reproducible mode in pkgdb\n" + " --staged Enable staged packages\n" " -S, --sync Sync remote repository index\n" " -u, --update Update target package(s)\n" " -v, --verbose Verbose messages\n" @@ -118,6 +119,7 @@ main(int argc, char **argv) { "version", no_argument, NULL, 'V' }, { "yes", no_argument, NULL, 'y' }, { "reproducible", no_argument, NULL, 1 }, + { "staged", no_argument, NULL, 2 }, { NULL, 0, NULL, 0 } }; struct xbps_handle xh; @@ -138,6 +140,9 @@ main(int argc, char **argv) case 1: flags |= XBPS_FLAG_INSTALL_REPRO; break; + case 2: + flags |= XBPS_FLAG_USE_STAGE; + break; case 'A': flags |= XBPS_FLAG_INSTALL_AUTO; break; diff --git a/bin/xbps-install/xbps-install.1 b/bin/xbps-install/xbps-install.1 index a68f3ed7a..4324c171f 100644 --- a/bin/xbps-install/xbps-install.1 +++ b/bin/xbps-install/xbps-install.1 @@ -154,6 +154,8 @@ The and .Ar repository package objects are not stored in pkgdb. +.It Fl -staged +Enables the use of staged packages from remote repositories. .It Fl r , Fl -rootdir Ar dir Specifies a full path for the target root directory. .It Fl S , Fl -sync From 63d507307e75c65f3ea7e00de00d6f8d450acd0d Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Wed, 1 Nov 2023 00:41:24 +0100 Subject: [PATCH 7/8] bin/xbps-query: add --staged flag --- bin/xbps-query/main.c | 5 +++++ bin/xbps-query/xbps-query.1 | 2 ++ 2 files changed, 7 insertions(+) diff --git a/bin/xbps-query/main.c b/bin/xbps-query/main.c index 44316c1ad..ba1c15bb3 100644 --- a/bin/xbps-query/main.c +++ b/bin/xbps-query/main.c @@ -51,6 +51,7 @@ usage(bool fail) " --repository= Enable repository mode and add repository\n" " to the top of the list. This option can be\n" " specified multiple times\n" + " --staged Enable staged packages\n" " --regex Use Extended Regular Expressions to match\n" " --fulldeptree Full dependency tree for -x/--deps\n" " -r, --rootdir Full path to rootdir\n" @@ -105,6 +106,7 @@ main(int argc, char **argv) { "regex", no_argument, NULL, 0 }, { "fulldeptree", no_argument, NULL, 1 }, { "cat", required_argument, NULL, 2 }, + { "staged", required_argument, NULL, 4 }, { NULL, 0, NULL, 0 }, }; struct xbps_handle xh; @@ -213,6 +215,9 @@ main(int argc, char **argv) case 3: list_repolock = opmode = true; break; + case 4: + flags |= XBPS_FLAG_USE_STAGE; + break; case '?': default: usage(true); diff --git a/bin/xbps-query/xbps-query.1 b/bin/xbps-query/xbps-query.1 index b4c364180..840b35492 100644 --- a/bin/xbps-query/xbps-query.1 +++ b/bin/xbps-query/xbps-query.1 @@ -127,6 +127,8 @@ and modes. .It Fl -fulldeptree Prints a full dependency tree in the +.It Fl -staged +Enables the use of staged packages from remote repositories. .Sy show dependencies mode. .It Fl r, Fl -rootdir Ar dir From 5ffa594ec0633b4d399f1cd64471cdc08e7a110c Mon Sep 17 00:00:00 2001 From: Duncan Overbruck Date: Wed, 1 Nov 2023 00:46:16 +0100 Subject: [PATCH 8/8] bin/xbps-checkvers: add --staged flag --- bin/xbps-checkvers/main.c | 5 +++++ bin/xbps-checkvers/xbps-checkvers.1 | 2 ++ 2 files changed, 7 insertions(+) diff --git a/bin/xbps-checkvers/main.c b/bin/xbps-checkvers/main.c index ab1f56f34..043b66ad2 100644 --- a/bin/xbps-checkvers/main.c +++ b/bin/xbps-checkvers/main.c @@ -92,6 +92,7 @@ show_usage(const char *prog, bool fail) " -R, --repository= Append repository to the head of repository list\n" " -r, --rootdir Set root directory (defaults to /)\n" " -s, --show-all List all packages, in the format 'pkgname repover srcver'\n" +" --staged Enable staged packages\n" "\n [FILES...] Extra packages to process with the outdated\n" " ones (only processed if missing).\n", prog); return fail ? EXIT_FAILURE: EXIT_SUCCESS; @@ -716,6 +717,7 @@ main(int argc, char **argv) { "rootdir", required_argument, NULL, 'r' }, { "show-all", no_argument, NULL, 's' }, { "version", no_argument, NULL, 'V' }, + { "staged", no_argument, NULL, 1 }, { NULL, 0, NULL, 0 } }; @@ -763,6 +765,9 @@ main(int argc, char **argv) case 'V': printf("%s\n", XBPS_RELVER); exit(EXIT_SUCCESS); + case 1: + rcv.xhp.flags |= XBPS_FLAG_USE_STAGE; + break; case '?': default: return show_usage(prog, true); diff --git a/bin/xbps-checkvers/xbps-checkvers.1 b/bin/xbps-checkvers/xbps-checkvers.1 index efc8def81..e9d2e2c78 100644 --- a/bin/xbps-checkvers/xbps-checkvers.1 +++ b/bin/xbps-checkvers/xbps-checkvers.1 @@ -92,6 +92,8 @@ tree and prints version available in repository and srcpkgs with the following f If any version can't be resolved, .Em ? is printed instead. +.It Fl -staged +Enables the use of staged packages from remote repositories. .It Fl V, Fl -version Show the version information. .El