diff --git a/bin/xbps-rindex/index-add.c b/bin/xbps-rindex/index-add.c index 22e6f669..c466af62 100644 --- a/bin/xbps-rindex/index-add.c +++ b/bin/xbps-rindex/index-add.c @@ -43,8 +43,8 @@ index_add(struct xbps_handle *xhp, int args, int argmax, char **argv, bool force xbps_dictionary_t idx, idxmeta, binpkgd, curpkgd; struct xbps_repo *repo = NULL; struct stat st; - char *tmprepodir = NULL, *repodir = NULL; - int rv = 0, ret = 0; + char *tmprepodir = NULL, *repodir = NULL, *rlockfname = NULL; + int rv = 0, ret = 0, rlockfd = -1; bool flush = false; assert(argv); @@ -55,7 +55,13 @@ index_add(struct xbps_handle *xhp, int args, int argmax, char **argv, bool force return ENOMEM; repodir = dirname(tmprepodir); - repo = xbps_repo_open(xhp, repodir, true); + if (!xbps_repo_lock(xhp, repodir, &rlockfd, &rlockfname)) { + fprintf(stderr, "xbps-rindex: cannot lock repository " + "%s: %s\n", repodir, strerror(errno)); + rv = -1; + goto out; + } + repo = xbps_repo_open(xhp, repodir); if (repo == NULL && errno != ENOENT) { fprintf(stderr, "xbps-rindex: cannot open/lock repository " "%s: %s\n", repodir, strerror(errno)); @@ -217,6 +223,9 @@ index_add(struct xbps_handle *xhp, int args, int argmax, char **argv, bool force out: if (repo) xbps_repo_close(repo); + + xbps_repo_unlock(rlockfd, rlockfname); + if (tmprepodir) free(tmprepodir); diff --git a/bin/xbps-rindex/index-clean.c b/bin/xbps-rindex/index-clean.c index 86189fbd..334b06b3 100644 --- a/bin/xbps-rindex/index-clean.c +++ b/bin/xbps-rindex/index-clean.c @@ -96,10 +96,17 @@ index_clean(struct xbps_handle *xhp, const char *repodir) xbps_dictionary_t idx = NULL, idxmeta = NULL; struct xbps_repo *repo; struct cbdata cbd; - int rv = 0; + char *rlockfname = NULL; + int rv = 0, rlockfd = -1; bool flush = false; - repo = xbps_repo_open(xhp, repodir, true); + if (!xbps_repo_lock(xhp, repodir, &rlockfd, &rlockfname)) { + rv = errno; + fprintf(stderr, "%s: cannot lock repository: %s\n", + _XBPS_RINDEX, strerror(rv)); + return rv; + } + repo = xbps_repo_open(xhp, repodir); if (repo == NULL) { rv = errno; if (rv == ENOENT) @@ -156,6 +163,8 @@ index_clean(struct xbps_handle *xhp, const char *repodir) out: xbps_repo_close(repo); + xbps_repo_unlock(rlockfd, rlockfname); + if (idx) xbps_object_release(idx); if (idxmeta) diff --git a/bin/xbps-rindex/remove-obsoletes.c b/bin/xbps-rindex/remove-obsoletes.c index d9375765..f01cbff3 100644 --- a/bin/xbps-rindex/remove-obsoletes.c +++ b/bin/xbps-rindex/remove-obsoletes.c @@ -118,7 +118,7 @@ remove_obsoletes(struct xbps_handle *xhp, const char *repodir) char *ext; int rv = 0; - repo = xbps_repo_open(xhp, repodir, false); + repo = xbps_repo_open(xhp, repodir); if (repo == NULL) { if (errno != ENOENT) { fprintf(stderr, "xbps-rindex: cannot read repository data: %s\n", diff --git a/bin/xbps-rindex/sign.c b/bin/xbps-rindex/sign.c index 80798ebf..d14494a6 100644 --- a/bin/xbps-rindex/sign.c +++ b/bin/xbps-rindex/sign.c @@ -119,7 +119,7 @@ sign_repo(struct xbps_handle *xhp, const char *repodir, const char *privkey, const char *signedby) { struct stat st; - struct xbps_repo *repo; + struct xbps_repo *repo = NULL; xbps_dictionary_t pkgd, meta = NULL; xbps_data_t data = NULL, rpubkey = NULL; xbps_object_iterator_t iter = NULL; @@ -129,8 +129,9 @@ sign_repo(struct xbps_handle *xhp, const char *repodir, unsigned int siglen; uint16_t rpubkeysize, pubkeysize; const char *arch, *pkgver, *rsignedby = NULL; - char *binpkg = NULL, *binpkg_sig = NULL, *buf = NULL, *defprivkey = NULL; - int binpkg_fd, binpkg_sig_fd, rv = 0; + char *binpkg = NULL, *binpkg_sig = NULL, *buf = NULL; + char *rlockfname = NULL, *defprivkey = NULL; + int binpkg_fd, binpkg_sig_fd, rlockfd = -1, rv = 0; bool flush = false; if (signedby == NULL) { @@ -141,7 +142,13 @@ sign_repo(struct xbps_handle *xhp, const char *repodir, /* * Check that repository index exists and not empty, otherwise bail out. */ - repo = xbps_repo_open(xhp, repodir, true); + if (!xbps_repo_lock(xhp, repodir, &rlockfd, &rlockfname)) { + rv = errno; + fprintf(stderr, "%s: cannot lock repository: %s\n", + _XBPS_RINDEX, strerror(errno)); + goto out; + } + repo = xbps_repo_open(xhp, repodir); if (repo == NULL) { rv = errno; fprintf(stderr, "%s: cannot read repository data: %s\n", @@ -304,5 +311,7 @@ out: if (repo) { xbps_repo_close(repo); } + xbps_repo_unlock(rlockfd, rlockfname); + return rv ? -1 : 0; } diff --git a/include/xbps.h.in b/include/xbps.h.in index 23dea55b..0516750e 100644 --- a/include/xbps.h.in +++ b/include/xbps.h.in @@ -48,7 +48,7 @@ * * This header documents the full API for the XBPS Library. */ -#define XBPS_API_VERSION "20150219-1" +#define XBPS_API_VERSION "20150323" #ifndef XBPS_VERSION #define XBPS_VERSION "UNSET" @@ -1265,7 +1265,6 @@ struct xbps_repo { * @private */ int fd; - bool is_locked; /** * var is_remote * @@ -1412,16 +1411,36 @@ xbps_dictionary_t xbps_rpool_get_pkg_plist(struct xbps_handle *xhp, */ bool xbps_repo_store(struct xbps_handle *xhp, const char *url); +/** + * Creates a lock for a local repository to obtain exclusive access (write). + * + * @param[in] xhp Pointer to the xbps_handle struct. + * @param[in] uri Repository URI to match. + * @param[out] lockfd Lock file descriptor assigned. + * @param[out] lockfname Lock filename assigned. + * + * @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); + +/** + * Unlocks a local repository and removes its lock file. + * + * @param[in] lockfd Lock file descriptor. + * @param[in] lockfname Lock filename. + */ +void xbps_repo_unlock(int lockfd, char *lockfname); + /** * Opens a repository and returns a xbps_repo object. * * @param[in] xhp Pointer to the xbps_handle struct. * @param[in] uri Repository URI to match. - * @param[in] lock Set it to true to acquire a POSIX file lock. * * @return The matching repository object, NULL otherwise. */ -struct xbps_repo *xbps_repo_open(struct xbps_handle *xhp, const char *url, bool lock); +struct xbps_repo *xbps_repo_open(struct xbps_handle *xhp, const char *url); /** * Closes a repository object and releases resources. diff --git a/lib/repo.c b/lib/repo.c index b02e00a9..45644b93 100644 --- a/lib/repo.c +++ b/lib/repo.c @@ -73,22 +73,61 @@ repo_get_dict(struct xbps_repo *repo) 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; + + assert(repodir); + assert(lockfd); + assert(lockfname); + + repofile = xbps_repo_path(xhp, repodir); + assert(repofile); + + lockfile = xbps_xasprintf("%s.lock", repofile); + free(repofile); + + for (;;) { + fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0660); + rv = errno; + if (fd != -1) + break; + if (rv != EEXIST) { + xbps_dbg_printf(xhp, "[repo] `%s' failed to " + "create lock file %s\n", lockfile, strerror(rv)); + free(lockfile); + return false; + } else { + xbps_dbg_printf(xhp, "[repo] `%s' lock file exists," + "waiting...\n", lockfile); + } + } + *lockfname = lockfile; + *lockfd = fd; + return true; +} + +void +xbps_repo_unlock(int lockfd, char *lockfname) +{ + if (lockfd != -1) { + close(lockfd); + } + if (lockfname) { + unlink(lockfname); + free(lockfname); + } +} + static bool -repo_open_local(struct xbps_repo *repo, const char *repofile, bool lock) +repo_open_local(struct xbps_repo *repo, const char *repofile) { struct stat st; int rv = 0; - /* - * Acquire a POSIX file lock on the archive; wait if the lock is - * already taken. - */ - if (lock && lockf(repo->fd, F_LOCK, 0) == -1) { - rv = errno; - xbps_dbg_printf(repo->xhp, "[repo] failed to lock %s: %s\n", - repofile, strerror(rv)); - return false; - } if (fstat(repo->fd, &st) == -1) { rv = errno; xbps_dbg_printf(repo->xhp, "[repo] `%s' fstat repodata %s\n", @@ -179,7 +218,7 @@ xbps_repo_store(struct xbps_handle *xhp, const char *repo) } struct xbps_repo * -xbps_repo_open(struct xbps_handle *xhp, const char *url, bool lock) +xbps_repo_open(struct xbps_handle *xhp, const char *url) { struct xbps_repo *repo; const char *arch; @@ -195,6 +234,7 @@ xbps_repo_open(struct xbps_handle *xhp, const char *url, bool lock) repo = calloc(1, sizeof(struct xbps_repo)); assert(repo); + repo->fd = -1; repo->xhp = xhp; repo->uri = url; @@ -225,31 +265,21 @@ xbps_repo_open(struct xbps_handle *xhp, const char *url, bool lock) /* * Open the repository archive. */ - if (lock) { - repo->fd = open(repofile, O_RDWR); - repo->is_locked = true; - } else { - repo->fd = open(repofile, O_RDONLY); - } - + repo->fd = open(repofile, O_RDONLY); if (repo->fd == -1) { int rv = errno; xbps_dbg_printf(xhp, "[repo] `%s' open repodata %s\n", repofile, strerror(rv)); goto out; } - if (repo_open_local(repo, repofile, lock)) { + if (repo_open_local(repo, repofile)) { free(repofile); return repo; } out: - if (repo->ar) - archive_read_free(repo->ar); - if (repo->fd != -1) - close(repo->fd); free(repofile); - free(repo); + xbps_repo_close(repo); return NULL; } @@ -269,10 +299,10 @@ xbps_repo_close(struct xbps_repo *repo) xbps_object_release(repo->idxmeta); repo->idxmeta = NULL; } - if (repo->is_locked && lockf(repo->fd, F_ULOCK, 0) == -1) - xbps_dbg_printf(repo->xhp, "[repo] failed to unlock %s: %s\n", repo->uri, strerror(errno)); + if (repo->fd != -1) + close(repo->fd); - close(repo->fd); + free(repo); } xbps_dictionary_t diff --git a/lib/rpool.c b/lib/rpool.c index e2c8a53c..e7132727 100644 --- a/lib/rpool.c +++ b/lib/rpool.c @@ -92,7 +92,7 @@ xbps_regget_repo(struct xbps_handle *xhp, const char *url) if (strcmp(repouri, url)) continue; - repo = xbps_repo_open(xhp, repouri, false); + repo = xbps_repo_open(xhp, repouri); if (!repo) return NULL; @@ -148,7 +148,7 @@ xbps_rpool_foreach(struct xbps_handle *xhp, for (unsigned int i = 0; i < xbps_array_count(xhp->repositories); i++) { xbps_array_get_cstring_nocopy(xhp->repositories, i, &repouri); if ((repo = xbps_rpool_get_repo(repouri)) == NULL) { - repo = xbps_repo_open(xhp, repouri, false); + repo = xbps_repo_open(xhp, repouri); if (!repo) continue; SIMPLEQ_INSERT_TAIL(&rpool_queue, repo, entries);