librazor/razor.c
changeset 501 850be6a6885c
parent 479 4204db81cdbc
     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