Improve error on failure to lock database
authorJ. Ali Harlow <ali@juiblex.co.uk>
Thu Jun 07 18:36:20 2018 +0100 (2018-06-07)
changeset 499c89e5edb8eae
parent 498 5a49f274ab2d
child 500 f98d77376544
Improve error on failure to lock database
librazor/error.c
librazor/razor-internal.h
librazor/razor.c
librazor/razor.h.in
librazor/root.c
librazor/types/array.c
librazor/types/types.h
     1.1 --- a/librazor/error.c	Tue Jun 05 11:07:53 2018 +0100
     1.2 +++ b/librazor/error.c	Thu Jun 07 18:36:20 2018 +0100
     1.3 @@ -42,6 +42,30 @@
     1.4  	return error->code;
     1.5  }
     1.6  
     1.7 +void
     1.8 +razor_error_set_object(struct razor_error *error, const char *object)
     1.9 +{
    1.10 +	if (!error)
    1.11 +		return;
    1.12 +
    1.13 +	free(error->object);
    1.14 +
    1.15 +	if (object)
    1.16 +		error->object = strdup(object);
    1.17 +	else
    1.18 +		error->object = NULL;
    1.19 +
    1.20 +	if (error->obj_str) {
    1.21 +		free(error->obj_str);
    1.22 +		error->obj_str = NULL;
    1.23 +	}
    1.24 +
    1.25 +	if (error->msg) {
    1.26 +		free(error->msg);
    1.27 +		error->msg = NULL;
    1.28 +	}
    1.29 +}
    1.30 +
    1.31  RAZOR_EXPORT const char *
    1.32  razor_error_get_object(struct razor_error *error)
    1.33  {
     2.1 --- a/librazor/razor-internal.h	Tue Jun 05 11:07:53 2018 +0100
     2.2 +++ b/librazor/razor-internal.h	Thu Jun 07 18:36:20 2018 +0100
     2.3 @@ -125,7 +125,7 @@
     2.4  	struct array file_string_pool;
     2.5  	struct array details_string_pool;
     2.6  	struct razor_mapped_file *mapped_files;
     2.7 -	int lock_fd, ref_count;
     2.8 +	int lock, ref_count;
     2.9  	enum razor_set_flags flags;
    2.10  };
    2.11  
    2.12 @@ -184,7 +184,8 @@
    2.13  };
    2.14  
    2.15  int
    2.16 -razor_set_acquire_lock(struct razor_set *set, const char *path, int exclusive);
    2.17 +razor_set_acquire_lock(struct razor_set *set, const char *path, int exclusive,
    2.18 +		       struct razor_error **error);
    2.19  
    2.20  struct razor_entry *
    2.21  razor_set_find_entry(struct razor_set *set,
    2.22 @@ -315,6 +316,7 @@
    2.23  	if (error) \
    2.24  		*(error) = razor_error_new_str(domain, code, object, str); \
    2.25  	else
    2.26 +void razor_error_set_object(struct razor_error *error, const char *object);
    2.27  
    2.28  #ifdef MSWIN_API
    2.29  struct razor_error *razor_error_new_mswin(const wchar_t *object, DWORD error);
     3.1 --- a/librazor/razor.c	Tue Jun 05 11:07:53 2018 +0100
     3.2 +++ b/librazor/razor.c	Thu Jun 07 18:36:20 2018 +0100
     3.3 @@ -1,7 +1,7 @@
     3.4  /*
     3.5   * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
     3.6   * Copyright (C) 2008  Red Hat, Inc
     3.7 - * Copyright (C) 2009-2012, 2016  J. Ali Harlow <ali@juiblex.co.uk>
     3.8 + * Copyright (C) 2009-2012, 2016, 2018  J. Ali Harlow <ali@juiblex.co.uk>
     3.9   *
    3.10   * This program is free software; you can redistribute it and/or modify
    3.11   * it under the terms of the GNU General Public License as published by
    3.12 @@ -39,8 +39,9 @@
    3.13  #include <windows.h>
    3.14  #endif
    3.15  
    3.16 +#include "razor.h"
    3.17 +#include "types/types.h"
    3.18  #include "razor-internal.h"
    3.19 -#include "razor.h"
    3.20  
    3.21  #ifndef O_BINARY
    3.22  #define O_BINARY	0
    3.23 @@ -84,7 +85,7 @@
    3.24  		empty = array_add(&set->string_pool, 1);
    3.25  		*empty = '\0';
    3.26  
    3.27 -		set->lock_fd = -1;
    3.28 +		set->lock = -1;
    3.29  
    3.30  		set->ref_count = 1;
    3.31  
    3.32 @@ -229,7 +230,7 @@
    3.33  		return NULL;
    3.34  	}
    3.35  
    3.36 -	set->lock_fd = -1;
    3.37 +	set->lock = -1;
    3.38  	set->ref_count = 1;
    3.39  	if (razor_set_bind_sections(set, uri, flags, error)) {
    3.40  		free(set);
    3.41 @@ -238,56 +239,189 @@
    3.42  	return set;
    3.43  }
    3.44  
    3.45 -int
    3.46 -razor_set_acquire_lock(struct razor_set *set, const char *uri, int exclusive)
    3.47 +struct razor_uri_lock {
    3.48 +	char *uri;
    3.49 +	int fd;
    3.50 +	int exclusive;
    3.51 +};
    3.52 +
    3.53 +static struct array razor_uri_locks = { 0, };
    3.54 +
    3.55 +/*
    3.56 + * The underlying locks on MS-Windows and POSIX are different. Posix locks
    3.57 + * created with fcntl(F_SETLK) only affect other processes whereas MS-Windows
    3.58 + * locks affect the locking process as well. Partly because the latter is
    3.59 + * preferable and partly because it makes testing easier if platforms behave
    3.60 + * in the same way, we implement platform independent locks which follow the
    3.61 + * MS-Windows pattern.
    3.62 + *
    3.63 + * Note that razor_set_aquire_lock() currently assumes that this is a private
    3.64 + * helper function. Should we ever want to make this a library-wide API then
    3.65 + * we should probably add a 'void *owner' parameter and add a new error code
    3.66 + * (RAZOR_GENERAL_ERROR_LOCALLY_LOCKED?) if a lock is present with a different
    3.67 + * owner so that razor_set_aquire_lock() can generate a different error message.
    3.68 + */
    3.69 +
    3.70 +static int
    3.71 +razor_uri_lock(const char *uri, int flags, mode_t mode, int exclusive,
    3.72 +	       struct razor_error **error)
    3.73  {
    3.74 -	int fd;
    3.75 -	assert(set != NULL);
    3.76 +	int fd, i;
    3.77 +	struct razor_uri_lock *ulock;
    3.78 +#ifdef MSWIN_API
    3.79 +	DWORD lflags = LOCKFILE_FAIL_IMMEDIATELY, err;
    3.80 +	OVERLAPPED lock = {0};
    3.81 +#else
    3.82 +	struct flock lock = {0};
    3.83 +#endif
    3.84  
    3.85 -	if (uri) {
    3.86 -		fd = razor_uri_open(uri, O_CREAT | O_RDWR | O_TRUNC | O_BINARY,
    3.87 -				    0666, NULL);
    3.88 -		if (fd < 0)
    3.89 +	fd = razor_uri_open(uri, flags, mode, error);
    3.90 +	if (fd < 0)
    3.91 +		return -1;
    3.92 +
    3.93 +	for (i = ptr_array_len(&razor_uri_locks) - 1; i >= 0; i--) {
    3.94 +		ulock = ptr_array_index(&razor_uri_locks, i);
    3.95 +		if (ulock && !strcmp(ulock->uri, uri)) {
    3.96 +			if (!ulock->exclusive && !exclusive)
    3.97 +				break;
    3.98 +			razor_set_error(error, RAZOR_GENERAL_ERROR,
    3.99 +					RAZOR_GENERAL_ERROR_ALREADY_LOCKED,
   3.100 +					uri,
   3.101 +					"An existing lock is already held on this file");
   3.102 +			close(fd);
   3.103  			return -1;
   3.104 -	} else
   3.105 -		fd = -1;
   3.106 +		}
   3.107 +	}
   3.108  
   3.109  #ifdef MSWIN_API
   3.110 -	DWORD flags = LOCKFILE_FAIL_IMMEDIATELY;
   3.111 -	OVERLAPPED lock = {0};
   3.112 -
   3.113  	if (exclusive)
   3.114 -		flags |= LOCKFILE_EXCLUSIVE_LOCK;
   3.115 -	if (fd >= 0 && !LockFileEx((HANDLE)_get_osfhandle(fd), flags, 0, 1, 0,
   3.116 -				   &lock)) {
   3.117 +		lflags |= LOCKFILE_EXCLUSIVE_LOCK;
   3.118 +	if (!LockFileEx((HANDLE)_get_osfhandle(fd), lflags, 0, 1, 0, &lock)) {
   3.119 +		err = GetLastError();
   3.120 +		if (err == ERROR_IO_PENDING)
   3.121 +			razor_set_error(error, RAZOR_GENERAL_ERROR,
   3.122 +					RAZOR_GENERAL_ERROR_EXTERNALLY_LOCKED,
   3.123 +					uri,
   3.124 +					"An existing lock is already held on this file");
   3.125 +		else
   3.126 +			razor_set_error_mswin(error, uri, err);
   3.127  		close(fd);
   3.128  		return -1;
   3.129  	}
   3.130 -	if (set->lock_fd >= 0)
   3.131 -		(void)UnlockFile((HANDLE)_get_osfhandle(set->lock_fd), 0, 0, 1,
   3.132 -				 0);
   3.133  #else
   3.134 -	struct flock lock = {0};
   3.135 -
   3.136  	lock.l_type = exclusive ? F_WRLCK : F_RDLCK;
   3.137  	lock.l_whence = SEEK_SET;
   3.138  	lock.l_start = 0;
   3.139  	lock.l_len = 0;
   3.140 -	if (fd >= 0 && fcntl(fd, F_SETLK, &lock) < 0) {
   3.141 +	if (fcntl(fd, F_SETLK, &lock) < 0) {
   3.142 +		if (errno == EAGAIN || errno == EACCES)
   3.143 +			razor_set_error(error, RAZOR_GENERAL_ERROR,
   3.144 +					RAZOR_GENERAL_ERROR_EXTERNALLY_LOCKED,
   3.145 +					uri,
   3.146 +					"An existing lock is already held on this file");
   3.147 +		else
   3.148 +			razor_set_error_posix(error, uri);
   3.149  		close(fd);
   3.150  		return -1;
   3.151  	}
   3.152 -	if (set->lock_fd >= 0) {
   3.153 -		lock.l_type = F_UNLCK;
   3.154 -		(void)fcntl(set->lock_fd, F_SETLK, &lock);
   3.155 -	}
   3.156  #endif
   3.157  
   3.158 -	if (set->lock_fd >= 0)
   3.159 -		close(set->lock_fd);
   3.160 -	set->lock_fd = fd;
   3.161 +	ulock = zalloc(sizeof *ulock);
   3.162 +	ulock->uri = strdup(uri);
   3.163 +	ulock->fd = fd;
   3.164 +	ulock->exclusive = exclusive;
   3.165 +	return ptr_array_add(&razor_uri_locks, ulock);
   3.166 +}
   3.167  
   3.168 -	return 0;
   3.169 +static void
   3.170 +razor_uri_unlock(int id)
   3.171 +{
   3.172 +	struct razor_uri_lock *ulock;
   3.173 +#ifndef MSWIN_API
   3.174 +	struct flock lock = {0};
   3.175 +#endif
   3.176 +
   3.177 +	if (id < 0 || id >= ptr_array_len(&razor_uri_locks))
   3.178 +		return;
   3.179 +
   3.180 +	ulock = ptr_array_index(&razor_uri_locks, id);
   3.181 +
   3.182 +	if (!ulock)
   3.183 +		return;
   3.184 +
   3.185 +#ifdef MSWIN_API
   3.186 +	(void)UnlockFile((HANDLE)_get_osfhandle(ulock->fd), 0, 0, 1, 0);
   3.187 +#else
   3.188 +	lock.l_type = F_UNLCK;
   3.189 +	lock.l_whence = SEEK_SET;
   3.190 +	lock.l_start = 0;
   3.191 +	lock.l_len = 0;
   3.192 +	(void)fcntl(ulock->fd, F_SETLK, &lock);
   3.193 +#endif
   3.194 +
   3.195 +	ptr_array_remove_index(&razor_uri_locks, id);
   3.196 +	free(ulock->uri);
   3.197 +	close(ulock->fd);
   3.198 +	free(ulock);
   3.199 +}
   3.200 +
   3.201 +int
   3.202 +razor_set_acquire_lock(struct razor_set *set, const char *uri, int exclusive,
   3.203 +		       struct razor_error **error)
   3.204 +{
   3.205 +	int lock;
   3.206 +	const char *errstr = NULL;
   3.207 +	assert(set != NULL);
   3.208 +
   3.209 +	if (!uri) {
   3.210 +		razor_uri_unlock(set->lock);
   3.211 +		set->lock = -1;
   3.212 +		return 0;
   3.213 +	}
   3.214 +
   3.215 +	lock = razor_uri_lock(uri,
   3.216 +			      O_CREAT | O_RDWR | O_TRUNC | O_BINARY,
   3.217 +			      0666, exclusive, error);
   3.218 +
   3.219 +	if (lock >= 0) {
   3.220 +		set->lock = lock;
   3.221 +		return 0;
   3.222 +	}
   3.223 +
   3.224 +	if (!error)
   3.225 +		return -1;
   3.226 +		
   3.227 +	if (razor_error_matches(*error, RAZOR_GENERAL_ERROR,
   3.228 +				RAZOR_GENERAL_ERROR_ALREADY_LOCKED)) {
   3.229 +		/*
   3.230 +		 * This represents a design flaw in the caller.
   3.231 +		 */
   3.232 +		if (exclusive)
   3.233 +			errstr = "Database is in use and cannot be locked";
   3.234 +		else
   3.235 +			errstr = "Database is being updated and cannot be locked";
   3.236 +	} else if (razor_error_matches(*error, RAZOR_GENERAL_ERROR,
   3.237 +				       RAZOR_GENERAL_ERROR_EXTERNALLY_LOCKED)) {
   3.238 +		/*
   3.239 +		 * This is probably caused by another application holding
   3.240 +		 * the lock, but under MS-Windows the file could in theory
   3.241 +		 * be locked by this application outside of razor_set.
   3.242 +		 */
   3.243 +		if (exclusive)
   3.244 +			errstr = "Database is in use. Please try again later";
   3.245 +		else
   3.246 +			errstr = "Database is being updated. Please try again later";
   3.247 +	}
   3.248 +
   3.249 +	if (errstr) {
   3.250 +		razor_error_free(*error);
   3.251 +		razor_set_error(error,
   3.252 +				RAZOR_GENERAL_ERROR,
   3.253 +				RAZOR_GENERAL_ERROR_DATABASE_LOCKED,
   3.254 +				uri, errstr);
   3.255 +	}
   3.256 +
   3.257 +	return -1;
   3.258  }
   3.259  
   3.260  static void
   3.261 @@ -312,7 +446,7 @@
   3.262  		}
   3.263  	}
   3.264  
   3.265 -	razor_set_acquire_lock(set, NULL, 0);
   3.266 +	razor_set_acquire_lock(set, NULL, 0, NULL);
   3.267  	free(set);
   3.268  }
   3.269  
     4.1 --- a/librazor/razor.h.in	Tue Jun 05 11:07:53 2018 +0100
     4.2 +++ b/librazor/razor.h.in	Thu Jun 07 18:36:20 2018 +0100
     4.3 @@ -1,7 +1,8 @@
     4.4  /*
     4.5   * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
     4.6   * Copyright (C) 2008  Red Hat, Inc
     4.7 - * Copyright (C) 2009, 2011, 2012, 2014, 2016  J. Ali Harlow <ali@juiblex.co.uk>
     4.8 + * Copyright (C) 2009, 2011, 2012, 2014, 2016, 2018
     4.9 + *		 J. Ali Harlow <ali@juiblex.co.uk>
    4.10   *
    4.11   * This program is free software; you can redistribute it and/or modify
    4.12   * it under the terms of the GNU General Public License as published by
    4.13 @@ -152,6 +153,8 @@
    4.14  	RAZOR_GENERAL_ERROR_UNSUPPORTED_URI,
    4.15  	RAZOR_GENERAL_ERROR_BAD_URI,
    4.16  	RAZOR_GENERAL_ERROR_UNSUPPORTED_ARCHIVE,
    4.17 +	RAZOR_GENERAL_ERROR_ALREADY_LOCKED,
    4.18 +	RAZOR_GENERAL_ERROR_EXTERNALLY_LOCKED,
    4.19  };
    4.20  
    4.21  int razor_error_get_domain(struct razor_error *error);
     5.1 --- a/librazor/root.c	Tue Jun 05 11:07:53 2018 +0100
     5.2 +++ b/librazor/root.c	Thu Jun 07 18:36:20 2018 +0100
     5.3 @@ -1,7 +1,7 @@
     5.4  /*
     5.5   * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
     5.6   * Copyright (C) 2008  Red Hat, Inc
     5.7 - * Copyright (C) 2009, 2011, 2012, 2014, 2016  J. Ali Harlow <ali@juiblex.co.uk>
     5.8 + * Copyright (C) 2009, 2011, 2012, 2014, 2016, 2018  J. Ali Harlow <ali@juiblex.co.uk>
     5.9   *
    5.10   * This program is free software; you can redistribute it and/or modify
    5.11   * it under the terms of the GNU General Public License as published by
    5.12 @@ -67,6 +67,7 @@
    5.13  #endif
    5.14  static char *razor_database_uri = RAZOR_DATABASE_URI;
    5.15  static int razor_database_uri_alloced = FALSE;
    5.16 +static int razor_database_uri_default = TRUE;
    5.17  
    5.18  struct razor_root {
    5.19  	struct razor_set *system;
    5.20 @@ -107,9 +108,11 @@
    5.21  	if (database_uri) {
    5.22  		razor_database_uri = strdup(database_uri);
    5.23  		razor_database_uri_alloced = TRUE;
    5.24 +		razor_database_uri_default = FALSE;
    5.25  	} else {
    5.26  		razor_database_uri = RAZOR_DATABASE_URI;
    5.27  		razor_database_uri_alloced = FALSE;
    5.28 +		razor_database_uri_default = TRUE;
    5.29  	}
    5.30  }
    5.31  
    5.32 @@ -223,14 +226,13 @@
    5.33  		return NULL;
    5.34  	}
    5.35  
    5.36 -	r = razor_set_acquire_lock(image->system, lock_uri, 1);
    5.37 +	r = razor_set_acquire_lock(image->system, lock_uri, 1, error);
    5.38  
    5.39  	free(lock_uri);
    5.40  
    5.41  	if (r < 0) {
    5.42 -		razor_set_error(error, RAZOR_GENERAL_ERROR,
    5.43 -				RAZOR_GENERAL_ERROR_DATABASE_LOCKED, NULL,
    5.44 -				"Failed to acquire exclusive system lock");
    5.45 +		if (error && razor_database_uri_default)
    5.46 +			razor_error_set_object(*error, root_uri);
    5.47  		razor_set_unref(image->system);
    5.48  		free(image);
    5.49  		return NULL;
    5.50 @@ -277,14 +279,13 @@
    5.51  		return NULL;
    5.52  	}
    5.53  
    5.54 -	r = razor_set_acquire_lock(set, uri, 0);
    5.55 +	r = razor_set_acquire_lock(set, uri, 0, error);
    5.56  
    5.57  	free(uri);
    5.58  
    5.59  	if (r < 0) {
    5.60 -		razor_set_error(error, RAZOR_GENERAL_ERROR,
    5.61 -				RAZOR_GENERAL_ERROR_DATABASE_LOCKED, NULL,
    5.62 -				"Failed to acquire non-exclusive system lock");
    5.63 +		if (error && razor_database_uri_default)
    5.64 +			razor_error_set_object(*error, root_uri);
    5.65  		razor_set_unref(set);
    5.66  		return NULL;
    5.67  	}
     6.1 --- a/librazor/types/array.c	Tue Jun 05 11:07:53 2018 +0100
     6.2 +++ b/librazor/types/array.c	Thu Jun 07 18:36:20 2018 +0100
     6.3 @@ -1,7 +1,7 @@
     6.4  /*
     6.5   * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
     6.6   * Copyright (C) 2008  Red Hat, Inc
     6.7 - * Copyright (C) 2016  J. Ali Harlow <ali@juiblex.co.uk>
     6.8 + * Copyright (C) 2016, 2018  J. Ali Harlow <ali@juiblex.co.uk>
     6.9   *
    6.10   * This program is free software; you can redistribute it and/or modify
    6.11   * it under the terms of the GNU General Public License as published by
    6.12 @@ -74,7 +74,16 @@
    6.13  	return array->data + array->size - size;
    6.14  }
    6.15  
    6.16 -void ptr_array_add(struct array *ptr_array, void *data)
    6.17 +int ptr_array_len(struct array *ptr_array)
    6.18 +{
    6.19 +	void **ptrend;
    6.20 +
    6.21 +	ptrend = ptr_array->data + ptr_array->size;
    6.22 +
    6.23 +	return ptrend - (void **)ptr_array->data;
    6.24 +}
    6.25 +
    6.26 +int ptr_array_add(struct array *ptr_array, void *data)
    6.27  {
    6.28  	void **ptr, **ptrend;
    6.29  
    6.30 @@ -87,6 +96,21 @@
    6.31  		ptr = array_add(ptr_array, sizeof *ptr);
    6.32  
    6.33  	*ptr = data;
    6.34 +
    6.35 +	return ptr - (void **)ptr_array->data;
    6.36 +}
    6.37 +
    6.38 +void *ptr_array_index(struct array *ptr_array, int indx)
    6.39 +{
    6.40 +	void **ptr, **ptrend;
    6.41 +
    6.42 +	ptrend = ptr_array->data + ptr_array->size;
    6.43 +
    6.44 +	ptr = (void **)ptr_array->data + indx;
    6.45 +	if (ptr < ptr_array->data || ptr >= ptrend)
    6.46 +		return NULL;
    6.47 +
    6.48 +	return *ptr;
    6.49  }
    6.50  
    6.51  int ptr_array_find(struct array *ptr_array, void *data)
    6.52 @@ -108,7 +132,7 @@
    6.53  	ptrend = ptr_array->data + ptr_array->size;
    6.54  
    6.55  	ptr = (void **)ptr_array->data + indx;
    6.56 -	if (ptr >= ptrend)
    6.57 +	if (ptr < ptr_array->data || ptr >= ptrend)
    6.58  		return;
    6.59  	*ptr = NULL;
    6.60  
     7.1 --- a/librazor/types/types.h	Tue Jun 05 11:07:53 2018 +0100
     7.2 +++ b/librazor/types/types.h	Thu Jun 07 18:36:20 2018 +0100
     7.3 @@ -1,7 +1,7 @@
     7.4  /*
     7.5   * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
     7.6   * Copyright (C) 2008  Red Hat, Inc
     7.7 - * Copyright (C) 2009  J. Ali Harlow <ali@juiblex.co.uk>
     7.8 + * Copyright (C) 2009, 2018  J. Ali Harlow <ali@juiblex.co.uk>
     7.9   *
    7.10   * This program is free software; you can redistribute it and/or modify
    7.11   * it under the terms of the GNU General Public License as published by
    7.12 @@ -37,7 +37,9 @@
    7.13  void array_release(struct array *array);
    7.14  int array_set_size(struct array *array, int size);
    7.15  void *array_add(struct array *array, int size);
    7.16 -void ptr_array_add(struct array *ptr_array, void *data);
    7.17 +int ptr_array_len(struct array *ptr_array);
    7.18 +int ptr_array_add(struct array *ptr_array, void *data);
    7.19 +void *ptr_array_index(struct array *ptr_array, int indx);
    7.20  int ptr_array_find(struct array *ptr_array, void *data);
    7.21  void ptr_array_remove_index(struct array *ptr_array, int indx);
    7.22