diff --git a/NEWS b/NEWS index bc184038..0b5294a6 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,8 @@ xbps-0.31 (??): + * xbps-rindex(8): use a POSIX named semaphore in modes that can modify + repository data archive, to avoid concurrency issues. + * Removed 0.27 compat from repodata. * If a repository has been signed previously respect its settings diff --git a/bin/xbps-rindex/Makefile b/bin/xbps-rindex/Makefile index 2f17c90a..0b404f11 100644 --- a/bin/xbps-rindex/Makefile +++ b/bin/xbps-rindex/Makefile @@ -2,6 +2,6 @@ TOPDIR = ../.. -include $(TOPDIR)/config.mk BIN = xbps-rindex -OBJS = main.o index-add.o index-clean.o remove-obsoletes.o repoflush.o sign.o +OBJS = main.o sem.o index-add.o index-clean.o remove-obsoletes.o repoflush.o sign.o include $(TOPDIR)/mk/prog.mk diff --git a/bin/xbps-rindex/defs.h b/bin/xbps-rindex/defs.h index ade5440c..2f0fa122 100644 --- a/bin/xbps-rindex/defs.h +++ b/bin/xbps-rindex/defs.h @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2012-2013 Juan Romero Pardines. + * Copyright (c) 2012-2014 Juan Romero Pardines. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,6 +27,7 @@ #define _XBPS_RINDEX_DEFS_H_ #include +#include /* libarchive compat */ #if ARCHIVE_VERSION_NUMBER >= 3000000 @@ -64,6 +65,9 @@ #define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) #endif +#define _XBPS_RINDEX "xbps-rindex" +#define _XBPS_RINDEX_SEMNAME "/xbps-rindex-write" + /* From index-add.c */ int index_add(struct xbps_handle *, int, char **, bool); @@ -81,4 +85,8 @@ int sign_repo(struct xbps_handle *, const char *, const char *, bool repodata_flush(struct xbps_handle *, const char *, xbps_dictionary_t, xbps_dictionary_t, xbps_dictionary_t); +/* From sem.c */ +sem_t *index_lock(void); +void index_unlock(sem_t *); + #endif /* !_XBPS_RINDEX_DEFS_H_ */ diff --git a/bin/xbps-rindex/index-add.c b/bin/xbps-rindex/index-add.c index 0325e4f7..2208f88c 100644 --- a/bin/xbps-rindex/index-add.c +++ b/bin/xbps-rindex/index-add.c @@ -43,6 +43,7 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force) xbps_array_t array, pkg_files, pkg_links, pkg_cffiles; xbps_dictionary_t idx, idxmeta, idxfiles, binpkgd, pkg_filesd, curpkgd; xbps_object_t obj, fileobj; + sem_t *sem; struct xbps_repo *repo; struct stat st; const char *arch; @@ -50,14 +51,16 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force) int rv = 0, ret = 0; bool flush = false, found = false; - if ((tmprepodir = strdup(argv[0])) == NULL) - return ENOMEM; - + if ((sem = index_lock()) == NULL) + return EINVAL; /* * Read the repository data or create index dictionaries otherwise. */ + if ((tmprepodir = strdup(argv[0])) == NULL) { + rv = ENOMEM; + goto out; + } repodir = dirname(tmprepodir); - repo = xbps_repo_open(xhp, repodir); if (repo && repo->idx) { xbps_repo_open_idxfiles(repo); @@ -70,7 +73,6 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force) idxmeta = NULL; idxfiles = xbps_dictionary_create(); } - /* * Process all packages specified in argv. */ @@ -81,7 +83,8 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force) binpkgd = xbps_get_pkg_plist_from_binpkg(argv[i], "./props.plist"); if (binpkgd == NULL) { - fprintf(stderr, "failed to read %s metadata for `%s', skipping!\n", XBPS_PKGPROPS, argv[i]); + fprintf(stderr, "index: failed to read %s metadata for " + "`%s', skipping!\n", XBPS_PKGPROPS, argv[i]); continue; } xbps_dictionary_get_cstring_nocopy(binpkgd, "architecture", &arch); @@ -102,9 +105,10 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force) curpkgd = xbps_dictionary_get(idx, pkgname); if (curpkgd == NULL) { if (errno && errno != ENOENT) { + rv = errno; free(pkgver); free(pkgname); - return errno; + goto out; } } else if (!force) { /* Only check version if !force */ @@ -139,22 +143,26 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force) if ((sha256 = xbps_file_hash(argv[i])) == NULL) { free(pkgver); free(pkgname); - return errno; + rv = EINVAL; + goto out; } if (!xbps_dictionary_set_cstring(binpkgd, "filename-sha256", sha256)) { free(pkgver); free(pkgname); - return errno; + rv = EINVAL; + goto out; } if (stat(argv[i], &st) == -1) { free(pkgver); free(pkgname); - return errno; + rv = EINVAL; + goto out; } if (!xbps_dictionary_set_uint64(binpkgd, "filename-size", (uint64_t)st.st_size)) { free(pkgver); free(pkgname); - return errno; + rv = EINVAL; + goto out; } /* Remove unneeded objects */ xbps_dictionary_remove(binpkgd, "pkgname"); @@ -165,7 +173,8 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force) */ if (!xbps_dictionary_set(idx, pkgname, binpkgd)) { free(pkgname); - return EINVAL; + rv = EINVAL; + goto out; } flush = true; printf("index: added `%s' (%s).\n", pkgver, arch); @@ -178,7 +187,8 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force) pkg_filesd = xbps_get_pkg_plist_from_binpkg(argv[i], "./files.plist"); if (pkg_filesd == NULL) { free(pkgver); - return EINVAL; + rv = EINVAL; + goto out; } pkg_cffiles = xbps_dictionary_get(pkg_filesd, "conf_files"); @@ -244,12 +254,16 @@ index_add(struct xbps_handle *xhp, int argc, char **argv, bool force) */ if (flush) { if (!repodata_flush(xhp, repodir, idx, idxfiles, idxmeta)) { - fprintf(stderr, "failed to write repodata: %s\n", strerror(errno)); + fprintf(stderr, "%s: failed to write repodata: %s\n", + _XBPS_RINDEX, strerror(errno)); return -1; } } printf("index: %u packages registered.\n", xbps_dictionary_count(idx)); printf("index-files: %u packages registered.\n", xbps_dictionary_count(idxfiles)); +out: + index_unlock(sem); + return rv; } diff --git a/bin/xbps-rindex/index-clean.c b/bin/xbps-rindex/index-clean.c index ecc86dca..95eea6f5 100644 --- a/bin/xbps-rindex/index-clean.c +++ b/bin/xbps-rindex/index-clean.c @@ -118,20 +118,27 @@ idxfiles_cleaner_cb(struct xbps_handle *xhp _unused, xbps_object_t obj _unused, int index_clean(struct xbps_handle *xhp, const char *repodir) { + xbps_array_t allkeys; + xbps_dictionary_t idx = NULL, idxmeta = NULL, idxfiles = NULL; struct xbps_repo *repo; struct cbdata cbd; - xbps_array_t allkeys; - xbps_dictionary_t idx, idxmeta, idxfiles; + sem_t *sem; char *keyname, *pkgname; int rv = 0; bool flush = false; + if ((sem = index_lock()) == NULL) + return EINVAL; + repo = xbps_repo_open(xhp, repodir); if (repo == NULL) { if (errno == ENOENT) - return 0; - fprintf(stderr, "index: cannot read repository data: %s\n", strerror(errno)); - return -1; + goto out; + + fprintf(stderr, "%s: cannot read repository data: %s\n", + _XBPS_RINDEX, strerror(errno)); + rv = errno; + goto out; } xbps_repo_open_idxfiles(repo); idx = xbps_dictionary_copy(repo->idx); @@ -139,8 +146,9 @@ index_clean(struct xbps_handle *xhp, const char *repodir) idxfiles = xbps_dictionary_copy(repo->idxfiles); xbps_repo_close(repo); if (idx == NULL || idxfiles == NULL) { - fprintf(stderr, "incomplete repository data file!\n"); - return -1; + fprintf(stderr, "%s: incomplete repository data file!\n", _XBPS_RINDEX); + rv = EINVAL; + goto out; } printf("Cleaning `%s' index, please wait...\n", repodir); @@ -187,9 +195,10 @@ index_clean(struct xbps_handle *xhp, const char *repodir) if (flush) { if (!repodata_flush(xhp, repodir, idx, idxfiles, idxmeta)) { + rv = errno; fprintf(stderr, "failed to write repodata: %s\n", strerror(errno)); - return -1; + goto out; } } printf("index: %u packages registered.\n", @@ -197,8 +206,15 @@ index_clean(struct xbps_handle *xhp, const char *repodir) printf("index-files: %u packages registered.\n", xbps_dictionary_count(idxfiles)); - xbps_object_release(idx); - xbps_object_release(idxfiles); +out: + index_unlock(sem); + + if (idx) + xbps_object_release(idx); + if (idxmeta) + xbps_object_release(idxmeta); + if (idxfiles) + xbps_object_release(idxfiles); return rv; } diff --git a/bin/xbps-rindex/sem.c b/bin/xbps-rindex/sem.c new file mode 100644 index 00000000..d34c40ed --- /dev/null +++ b/bin/xbps-rindex/sem.c @@ -0,0 +1,64 @@ +/*- + * Copyright (c) 2014 Juan Romero Pardines. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include + +#include "defs.h" + +sem_t * +index_lock(void) +{ + sem_t *sem; + /* + * Create/open the POSIX named semaphore. + */ + sem = sem_open(_XBPS_RINDEX_SEMNAME, O_CREAT, 0660, 1); + if (sem == SEM_FAILED) { + fprintf(stderr, "%s: failed to create/open named " + "semaphore: %s\n", _XBPS_RINDEX, strerror(errno)); + return NULL; + } + if (sem_wait(sem) == -1) { + fprintf(stderr, "%s: failed to lock named semaphore: %s\n", + _XBPS_RINDEX, strerror(errno)); + return NULL; + } + + return sem; +} + +void +index_unlock(sem_t *sem) +{ + /* Unblock semaphore, close and destroy it (if possible) */ + sem_post(sem); + sem_close(sem); + sem_unlink(_XBPS_RINDEX_SEMNAME); +} diff --git a/bin/xbps-rindex/sign.c b/bin/xbps-rindex/sign.c index cd282f00..49966141 100644 --- a/bin/xbps-rindex/sign.c +++ b/bin/xbps-rindex/sign.c @@ -116,6 +116,7 @@ int sign_repo(struct xbps_handle *xhp, const char *repodir, const char *privkey, const char *signedby) { + sem_t *sem; struct stat st; struct xbps_repo *repo; xbps_dictionary_t pkgd, meta = NULL; @@ -135,18 +136,24 @@ sign_repo(struct xbps_handle *xhp, const char *repodir, fprintf(stderr, "--signedby unset! cannot sign repository\n"); return -1; } + + if ((sem = index_lock()) == NULL) + return EINVAL; + /* * Check that repository index exists and not empty, otherwise bail out. */ repo = xbps_repo_open(xhp, repodir); if (repo == NULL) { - fprintf(stderr, "cannot read repository data: %s\n", strerror(errno)); - return -1; + rv = errno; + fprintf(stderr, "%s: cannot read repository data: %s\n", + _XBPS_RINDEX, strerror(errno)); + goto out; } if (xbps_dictionary_count(repo->idx) == 0) { - fprintf(stderr, "Invalid repository, existing!\n"); - xbps_repo_close(repo); - return -1; + fprintf(stderr, "%s: invalid repository, existing!\n", _XBPS_RINDEX); + rv = EINVAL; + goto out; } xbps_repo_open_idxfiles(repo); /* @@ -163,7 +170,7 @@ sign_repo(struct xbps_handle *xhp, const char *repodir, OpenSSL_add_all_digests(); if ((rsa = load_rsa_privkey(defprivkey)) == NULL) { - fprintf(stderr, "failed to read the RSA privkey\n"); + fprintf(stderr, "%s: failed to read the RSA privkey\n", _XBPS_RINDEX); rv = EINVAL; goto out; } @@ -289,6 +296,8 @@ sign_repo(struct xbps_handle *xhp, const char *repodir, xbps_dictionary_count(repo->idx) == 1 ? "" : "s"); out: + index_unlock(sem); + if (rsa) { RSA_free(rsa); rsa = NULL; diff --git a/configure b/configure index a3efc2e9..02645cb4 100755 --- a/configure +++ b/configure @@ -338,6 +338,39 @@ echo "CPPFLAGS += -I\$(TOPDIR)/lib/portableproplib/prop" >>$CONFIG_MK echo "LDFLAGS += -lpthread" >>$CONFIG_MK echo "STATIC_LIBS += -lpthread" >>$CONFIG_MK +# +# Check for POSIX semaphores. +# +printf "Checking for POSIX semaphores ... " +cat < _$func.c +#include +#include +#include + +int main(void) { + sem_t *sem; + + sem = sem_open("/xbps0000test", 0644, O_CREAT, 1); + sem_wait(sem); + sem_post(sem); + sem_close(sem); + sem_unlink("/xbps0000test"); + + return 0; +} +EOF +if $XCC -pthread -lrt _$func.c -o _$func 2>/dev/null; then + POSIXSEM=yes + echo yes. +fi +rm -f _$func.c _$func +if [ -z "$POSIXSEM" ]; then + echo "no! POSIX semaphores are required, exiting..." + exit 1 +fi +echo "LDFLAGS += -lrt" >>$CONFIG_MK +echo "STATIC_LIBS += -lrt" >>$CONFIG_MK + # # Check for vasprintf(). #