diff -r 4204db81cdbc -r f98d77376544 librazor/razor.c --- a/librazor/razor.c Thu Jul 07 15:17:29 2016 +0100 +++ b/librazor/razor.c Fri Jun 08 18:02:33 2018 +0100 @@ -1,7 +1,7 @@ /* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc - * Copyright (C) 2009-2012, 2016 J. Ali Harlow + * Copyright (C) 2009-2012, 2016, 2018 J. Ali Harlow * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -39,8 +39,9 @@ #include #endif +#include "razor.h" +#include "types/types.h" #include "razor-internal.h" -#include "razor.h" #ifndef O_BINARY #define O_BINARY 0 @@ -84,7 +85,7 @@ empty = array_add(&set->string_pool, 1); *empty = '\0'; - set->lock_fd = -1; + set->lock = -1; set->ref_count = 1; @@ -229,7 +230,7 @@ return NULL; } - set->lock_fd = -1; + set->lock = -1; set->ref_count = 1; if (razor_set_bind_sections(set, uri, flags, error)) { free(set); @@ -238,56 +239,189 @@ return set; } -int -razor_set_acquire_lock(struct razor_set *set, const char *uri, int exclusive) +struct razor_uri_lock { + char *uri; + int fd; + int exclusive; +}; + +static struct array razor_uri_locks = { 0, }; + +/* + * The underlying locks on MS-Windows and POSIX are different. Posix locks + * created with fcntl(F_SETLK) only affect other processes whereas MS-Windows + * locks affect the locking process as well. Partly because the latter is + * preferable and partly because it makes testing easier if platforms behave + * in the same way, we implement platform independent locks which follow the + * MS-Windows pattern. + * + * Note that razor_set_aquire_lock() currently assumes that this is a private + * helper function. Should we ever want to make this a library-wide API then + * we should probably add a 'void *owner' parameter and add a new error code + * (RAZOR_GENERAL_ERROR_LOCALLY_LOCKED?) if a lock is present with a different + * owner so that razor_set_aquire_lock() can generate a different error message. + */ + +static int +razor_uri_lock(const char *uri, int flags, mode_t mode, int exclusive, + struct razor_error **error) { - int fd; - assert(set != NULL); + int fd, i; + struct razor_uri_lock *ulock; +#ifdef MSWIN_API + DWORD lflags = LOCKFILE_FAIL_IMMEDIATELY, err; + OVERLAPPED lock = {0}; +#else + struct flock lock = {0}; +#endif - if (uri) { - fd = razor_uri_open(uri, O_CREAT | O_RDWR | O_TRUNC | O_BINARY, - 0666, NULL); - if (fd < 0) + fd = razor_uri_open(uri, flags, mode, error); + if (fd < 0) + return -1; + + for (i = ptr_array_len(&razor_uri_locks) - 1; i >= 0; i--) { + ulock = ptr_array_index(&razor_uri_locks, i); + if (ulock && !strcmp(ulock->uri, uri)) { + if (!ulock->exclusive && !exclusive) + break; + razor_set_error(error, RAZOR_GENERAL_ERROR, + RAZOR_GENERAL_ERROR_ALREADY_LOCKED, + uri, + "An existing lock is already held on this file"); + close(fd); return -1; - } else - fd = -1; + } + } #ifdef MSWIN_API - DWORD flags = LOCKFILE_FAIL_IMMEDIATELY; - OVERLAPPED lock = {0}; - if (exclusive) - flags |= LOCKFILE_EXCLUSIVE_LOCK; - if (fd >= 0 && !LockFileEx((HANDLE)_get_osfhandle(fd), flags, 0, 1, 0, - &lock)) { + lflags |= LOCKFILE_EXCLUSIVE_LOCK; + if (!LockFileEx((HANDLE)_get_osfhandle(fd), lflags, 0, 1, 0, &lock)) { + err = GetLastError(); + if (err == ERROR_IO_PENDING) + razor_set_error(error, RAZOR_GENERAL_ERROR, + RAZOR_GENERAL_ERROR_EXTERNALLY_LOCKED, + uri, + "An existing lock is already held on this file"); + else + razor_set_error_mswin(error, uri, err); close(fd); return -1; } - if (set->lock_fd >= 0) - (void)UnlockFile((HANDLE)_get_osfhandle(set->lock_fd), 0, 0, 1, - 0); #else - struct flock lock = {0}; - lock.l_type = exclusive ? F_WRLCK : F_RDLCK; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; - if (fd >= 0 && fcntl(fd, F_SETLK, &lock) < 0) { + if (fcntl(fd, F_SETLK, &lock) < 0) { + if (errno == EAGAIN || errno == EACCES) + razor_set_error(error, RAZOR_GENERAL_ERROR, + RAZOR_GENERAL_ERROR_EXTERNALLY_LOCKED, + uri, + "An existing lock is already held on this file"); + else + razor_set_error_posix(error, uri); close(fd); return -1; } - if (set->lock_fd >= 0) { - lock.l_type = F_UNLCK; - (void)fcntl(set->lock_fd, F_SETLK, &lock); - } #endif - if (set->lock_fd >= 0) - close(set->lock_fd); - set->lock_fd = fd; + ulock = zalloc(sizeof *ulock); + ulock->uri = strdup(uri); + ulock->fd = fd; + ulock->exclusive = exclusive; + return ptr_array_add(&razor_uri_locks, ulock); +} - return 0; +static void +razor_uri_unlock(int id) +{ + struct razor_uri_lock *ulock; +#ifndef MSWIN_API + struct flock lock = {0}; +#endif + + if (id < 0 || id >= ptr_array_len(&razor_uri_locks)) + return; + + ulock = ptr_array_index(&razor_uri_locks, id); + + if (!ulock) + return; + +#ifdef MSWIN_API + (void)UnlockFile((HANDLE)_get_osfhandle(ulock->fd), 0, 0, 1, 0); +#else + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + (void)fcntl(ulock->fd, F_SETLK, &lock); +#endif + + ptr_array_remove_index(&razor_uri_locks, id); + free(ulock->uri); + close(ulock->fd); + free(ulock); +} + +int +razor_set_acquire_lock(struct razor_set *set, const char *uri, int exclusive, + struct razor_error **error) +{ + int lock; + const char *errstr = NULL; + assert(set != NULL); + + if (!uri) { + razor_uri_unlock(set->lock); + set->lock = -1; + return 0; + } + + lock = razor_uri_lock(uri, + O_CREAT | O_RDWR | O_TRUNC | O_BINARY, + 0666, exclusive, error); + + if (lock >= 0) { + set->lock = lock; + return 0; + } + + if (!error) + return -1; + + if (razor_error_matches(*error, RAZOR_GENERAL_ERROR, + RAZOR_GENERAL_ERROR_ALREADY_LOCKED)) { + /* + * This represents a design flaw in the caller. + */ + if (exclusive) + errstr = "Database is in use and cannot be locked"; + else + errstr = "Database is being updated and cannot be locked"; + } else if (razor_error_matches(*error, RAZOR_GENERAL_ERROR, + RAZOR_GENERAL_ERROR_EXTERNALLY_LOCKED)) { + /* + * This is probably caused by another application holding + * the lock, but under MS-Windows the file could in theory + * be locked by this application outside of razor_set. + */ + if (exclusive) + errstr = "Database is in use. Please try again later"; + else + errstr = "Database is being updated. Please try again later"; + } + + if (errstr) { + razor_error_free(*error); + razor_set_error(error, + RAZOR_GENERAL_ERROR, + RAZOR_GENERAL_ERROR_DATABASE_LOCKED, + uri, errstr); + } + + return -1; } static void @@ -312,7 +446,7 @@ } } - razor_set_acquire_lock(set, NULL, 0); + razor_set_acquire_lock(set, NULL, 0, NULL); free(set); }