commit 01b7ba6b7375d24a087d44aa48c0b78d66a6187e from: Stefan Sperling date: Mon Mar 11 13:57:59 2019 UTC add a lockfile API commit - 5892cdd6cc518cfa46a3828c79870aa6ac8fb326 commit + 01b7ba6b7375d24a087d44aa48c0b78d66a6187e blob - 106fa01cdcb16ebcfb3d4274fd2a3f883de11933 blob + f6f99ad590182ea6fbbc1c325681cd23eda29ec4 --- include/got_error.h +++ include/got_error.h @@ -77,6 +77,7 @@ #define GOT_ERR_UUID_VERSION 61 #define GOT_ERR_UUID_INVALID 62 #define GOT_ERR_UUID 63 +#define GOT_ERR_LOCKFILE_TIMEOUT 64 static const struct got_error { int code; @@ -144,6 +145,7 @@ static const struct got_error { { GOT_ERR_UUID_VERSION, "bad uuid version" }, { GOT_ERR_UUID_INVALID, "uuid invalid" }, { GOT_ERR_UUID, "uuid error" }, + { GOT_ERR_LOCKFILE_TIMEOUT,"lockfile timeout" }, }; /* blob - /dev/null blob + 379232fed66680154f7f43abb7948287cd9bd809 (mode 644) --- /dev/null +++ lib/got_lib_lockfile.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019 Stefan Sperling + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Git-compatible lock file implementation. Lock files are used to + * ensure exclusive access when files in a GIt repository are modified. + */ + +#define GOT_LOCKFILE_SUFFIX ".lock" + +struct got_lockfile { + char *path; + char *locked_path; + int fd; +}; + +const struct got_error *got_lockfile_lock(struct got_lockfile **, const char *); +const struct got_error *got_lockfile_unlock(struct got_lockfile *); blob - /dev/null blob + 2ea32dfb79258128985f28ea721c9b5757888284 (mode 644) --- /dev/null +++ lib/lockfile.c @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019 Stefan Sperling + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "got_error.h" + +#include "got_lib_lockfile.h" +#include "got_lib_path.h" + +const struct got_error * +got_lockfile_lock(struct got_lockfile **lf, const char *path) +{ + const struct got_error *err = NULL; + const int flags = O_RDONLY | O_CREAT | O_EXCL | O_EXLOCK; + int attempts = 5; + + *lf = calloc(1, sizeof(*lf)); + if (*lf == NULL) + return got_error_from_errno(); + (*lf)->fd = -1; + + (*lf)->locked_path = strdup(path); + if ((*lf)->locked_path == NULL) { + err = got_error_from_errno(); + goto done; + } + + if (asprintf(&(*lf)->path, "%s%s", path, GOT_LOCKFILE_SUFFIX) == -1) { + err = got_error_from_errno(); + goto done; + } + + do { + (*lf)->fd = open((*lf)->path, flags, GOT_DEFAULT_FILE_MODE); + if ((*lf)->fd == -1) { + if (errno != EEXIST) { + err = got_error_from_errno(); + goto done; + } + sleep(1); + } + } while (--attempts > 0); + + if ((*lf)->fd == -1) + err = got_error(GOT_ERR_LOCKFILE_TIMEOUT); +done: + if (err) { + got_lockfile_unlock(*lf); + *lf = NULL; + } + return err; +} + +const struct got_error * +got_lockfile_unlock(struct got_lockfile *lf) +{ + const struct got_error *err = NULL; + + if (lf->path && lf->fd != -1 && unlink(lf->path) != 0) + err = got_error_from_errno(); + if (lf->fd != -1 && close(lf->fd) != 0 && err == NULL) + err = got_error_from_errno(); + free(lf->path); + free(lf->locked_path); + free(lf); + return err; +}