1.1 --- a/librazor/razor.c Thu Jul 07 15:17:29 2016 +0100
1.2 +++ b/librazor/razor.c Fri Jun 08 18:02:49 2018 +0100
1.3 @@ -1,7 +1,7 @@
1.4 /*
1.5 * Copyright (C) 2008 Kristian Høgsberg <krh@redhat.com>
1.6 * Copyright (C) 2008 Red Hat, Inc
1.7 - * Copyright (C) 2009-2012, 2016 J. Ali Harlow <ali@juiblex.co.uk>
1.8 + * Copyright (C) 2009-2012, 2016, 2018 J. Ali Harlow <ali@juiblex.co.uk>
1.9 *
1.10 * This program is free software; you can redistribute it and/or modify
1.11 * it under the terms of the GNU General Public License as published by
1.12 @@ -39,8 +39,9 @@
1.13 #include <windows.h>
1.14 #endif
1.15
1.16 +#include "razor.h"
1.17 +#include "types/types.h"
1.18 #include "razor-internal.h"
1.19 -#include "razor.h"
1.20
1.21 #ifndef O_BINARY
1.22 #define O_BINARY 0
1.23 @@ -84,7 +85,7 @@
1.24 empty = array_add(&set->string_pool, 1);
1.25 *empty = '\0';
1.26
1.27 - set->lock_fd = -1;
1.28 + set->lock = -1;
1.29
1.30 set->ref_count = 1;
1.31
1.32 @@ -229,7 +230,7 @@
1.33 return NULL;
1.34 }
1.35
1.36 - set->lock_fd = -1;
1.37 + set->lock = -1;
1.38 set->ref_count = 1;
1.39 if (razor_set_bind_sections(set, uri, flags, error)) {
1.40 free(set);
1.41 @@ -238,56 +239,189 @@
1.42 return set;
1.43 }
1.44
1.45 -int
1.46 -razor_set_acquire_lock(struct razor_set *set, const char *uri, int exclusive)
1.47 +struct razor_uri_lock {
1.48 + char *uri;
1.49 + int fd;
1.50 + int exclusive;
1.51 +};
1.52 +
1.53 +static struct array razor_uri_locks = { 0, };
1.54 +
1.55 +/*
1.56 + * The underlying locks on MS-Windows and POSIX are different. Posix locks
1.57 + * created with fcntl(F_SETLK) only affect other processes whereas MS-Windows
1.58 + * locks affect the locking process as well. Partly because the latter is
1.59 + * preferable and partly because it makes testing easier if platforms behave
1.60 + * in the same way, we implement platform independent locks which follow the
1.61 + * MS-Windows pattern.
1.62 + *
1.63 + * Note that razor_set_aquire_lock() currently assumes that this is a private
1.64 + * helper function. Should we ever want to make this a library-wide API then
1.65 + * we should probably add a 'void *owner' parameter and add a new error code
1.66 + * (RAZOR_GENERAL_ERROR_LOCALLY_LOCKED?) if a lock is present with a different
1.67 + * owner so that razor_set_aquire_lock() can generate a different error message.
1.68 + */
1.69 +
1.70 +static int
1.71 +razor_uri_lock(const char *uri, int flags, mode_t mode, int exclusive,
1.72 + struct razor_error **error)
1.73 {
1.74 - int fd;
1.75 - assert(set != NULL);
1.76 + int fd, i;
1.77 + struct razor_uri_lock *ulock;
1.78 +#ifdef MSWIN_API
1.79 + DWORD lflags = LOCKFILE_FAIL_IMMEDIATELY, err;
1.80 + OVERLAPPED lock = {0};
1.81 +#else
1.82 + struct flock lock = {0};
1.83 +#endif
1.84
1.85 - if (uri) {
1.86 - fd = razor_uri_open(uri, O_CREAT | O_RDWR | O_TRUNC | O_BINARY,
1.87 - 0666, NULL);
1.88 - if (fd < 0)
1.89 + fd = razor_uri_open(uri, flags, mode, error);
1.90 + if (fd < 0)
1.91 + return -1;
1.92 +
1.93 + for (i = ptr_array_len(&razor_uri_locks) - 1; i >= 0; i--) {
1.94 + ulock = ptr_array_index(&razor_uri_locks, i);
1.95 + if (ulock && !strcmp(ulock->uri, uri)) {
1.96 + if (!ulock->exclusive && !exclusive)
1.97 + break;
1.98 + razor_set_error(error, RAZOR_GENERAL_ERROR,
1.99 + RAZOR_GENERAL_ERROR_ALREADY_LOCKED,
1.100 + uri,
1.101 + "An existing lock is already held on this file");
1.102 + close(fd);
1.103 return -1;
1.104 - } else
1.105 - fd = -1;
1.106 + }
1.107 + }
1.108
1.109 #ifdef MSWIN_API
1.110 - DWORD flags = LOCKFILE_FAIL_IMMEDIATELY;
1.111 - OVERLAPPED lock = {0};
1.112 -
1.113 if (exclusive)
1.114 - flags |= LOCKFILE_EXCLUSIVE_LOCK;
1.115 - if (fd >= 0 && !LockFileEx((HANDLE)_get_osfhandle(fd), flags, 0, 1, 0,
1.116 - &lock)) {
1.117 + lflags |= LOCKFILE_EXCLUSIVE_LOCK;
1.118 + if (!LockFileEx((HANDLE)_get_osfhandle(fd), lflags, 0, 1, 0, &lock)) {
1.119 + err = GetLastError();
1.120 + if (err == ERROR_IO_PENDING)
1.121 + razor_set_error(error, RAZOR_GENERAL_ERROR,
1.122 + RAZOR_GENERAL_ERROR_EXTERNALLY_LOCKED,
1.123 + uri,
1.124 + "An existing lock is already held on this file");
1.125 + else
1.126 + razor_set_error_mswin(error, uri, err);
1.127 close(fd);
1.128 return -1;
1.129 }
1.130 - if (set->lock_fd >= 0)
1.131 - (void)UnlockFile((HANDLE)_get_osfhandle(set->lock_fd), 0, 0, 1,
1.132 - 0);
1.133 #else
1.134 - struct flock lock = {0};
1.135 -
1.136 lock.l_type = exclusive ? F_WRLCK : F_RDLCK;
1.137 lock.l_whence = SEEK_SET;
1.138 lock.l_start = 0;
1.139 lock.l_len = 0;
1.140 - if (fd >= 0 && fcntl(fd, F_SETLK, &lock) < 0) {
1.141 + if (fcntl(fd, F_SETLK, &lock) < 0) {
1.142 + if (errno == EAGAIN || errno == EACCES)
1.143 + razor_set_error(error, RAZOR_GENERAL_ERROR,
1.144 + RAZOR_GENERAL_ERROR_EXTERNALLY_LOCKED,
1.145 + uri,
1.146 + "An existing lock is already held on this file");
1.147 + else
1.148 + razor_set_error_posix(error, uri);
1.149 close(fd);
1.150 return -1;
1.151 }
1.152 - if (set->lock_fd >= 0) {
1.153 - lock.l_type = F_UNLCK;
1.154 - (void)fcntl(set->lock_fd, F_SETLK, &lock);
1.155 - }
1.156 #endif
1.157
1.158 - if (set->lock_fd >= 0)
1.159 - close(set->lock_fd);
1.160 - set->lock_fd = fd;
1.161 + ulock = zalloc(sizeof *ulock);
1.162 + ulock->uri = strdup(uri);
1.163 + ulock->fd = fd;
1.164 + ulock->exclusive = exclusive;
1.165 + return ptr_array_add(&razor_uri_locks, ulock);
1.166 +}
1.167
1.168 - return 0;
1.169 +static void
1.170 +razor_uri_unlock(int id)
1.171 +{
1.172 + struct razor_uri_lock *ulock;
1.173 +#ifndef MSWIN_API
1.174 + struct flock lock = {0};
1.175 +#endif
1.176 +
1.177 + if (id < 0 || id >= ptr_array_len(&razor_uri_locks))
1.178 + return;
1.179 +
1.180 + ulock = ptr_array_index(&razor_uri_locks, id);
1.181 +
1.182 + if (!ulock)
1.183 + return;
1.184 +
1.185 +#ifdef MSWIN_API
1.186 + (void)UnlockFile((HANDLE)_get_osfhandle(ulock->fd), 0, 0, 1, 0);
1.187 +#else
1.188 + lock.l_type = F_UNLCK;
1.189 + lock.l_whence = SEEK_SET;
1.190 + lock.l_start = 0;
1.191 + lock.l_len = 0;
1.192 + (void)fcntl(ulock->fd, F_SETLK, &lock);
1.193 +#endif
1.194 +
1.195 + ptr_array_remove_index(&razor_uri_locks, id);
1.196 + free(ulock->uri);
1.197 + close(ulock->fd);
1.198 + free(ulock);
1.199 +}
1.200 +
1.201 +int
1.202 +razor_set_acquire_lock(struct razor_set *set, const char *uri, int exclusive,
1.203 + struct razor_error **error)
1.204 +{
1.205 + int lock;
1.206 + const char *errstr = NULL;
1.207 + assert(set != NULL);
1.208 +
1.209 + if (!uri) {
1.210 + razor_uri_unlock(set->lock);
1.211 + set->lock = -1;
1.212 + return 0;
1.213 + }
1.214 +
1.215 + lock = razor_uri_lock(uri,
1.216 + O_CREAT | O_RDWR | O_TRUNC | O_BINARY,
1.217 + 0666, exclusive, error);
1.218 +
1.219 + if (lock >= 0) {
1.220 + set->lock = lock;
1.221 + return 0;
1.222 + }
1.223 +
1.224 + if (!error)
1.225 + return -1;
1.226 +
1.227 + if (razor_error_matches(*error, RAZOR_GENERAL_ERROR,
1.228 + RAZOR_GENERAL_ERROR_ALREADY_LOCKED)) {
1.229 + /*
1.230 + * This represents a design flaw in the caller.
1.231 + */
1.232 + if (exclusive)
1.233 + errstr = "Database is in use and cannot be locked";
1.234 + else
1.235 + errstr = "Database is being updated and cannot be locked";
1.236 + } else if (razor_error_matches(*error, RAZOR_GENERAL_ERROR,
1.237 + RAZOR_GENERAL_ERROR_EXTERNALLY_LOCKED)) {
1.238 + /*
1.239 + * This is probably caused by another application holding
1.240 + * the lock, but under MS-Windows the file could in theory
1.241 + * be locked by this application outside of razor_set.
1.242 + */
1.243 + if (exclusive)
1.244 + errstr = "Database is in use. Please try again later";
1.245 + else
1.246 + errstr = "Database is being updated. Please try again later";
1.247 + }
1.248 +
1.249 + if (errstr) {
1.250 + razor_error_free(*error);
1.251 + razor_set_error(error,
1.252 + RAZOR_GENERAL_ERROR,
1.253 + RAZOR_GENERAL_ERROR_DATABASE_LOCKED,
1.254 + uri, errstr);
1.255 + }
1.256 +
1.257 + return -1;
1.258 }
1.259
1.260 static void
1.261 @@ -312,7 +446,7 @@
1.262 }
1.263 }
1.264
1.265 - razor_set_acquire_lock(set, NULL, 0);
1.266 + razor_set_acquire_lock(set, NULL, 0, NULL);
1.267 free(set);
1.268 }
1.269