2
* Copyright (C) 2006 Mark Pizzolato <clamav-devel@subscriptions.pizzolato.net>
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or
7
* (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21
* This is a problem, which from a purist point of view, best wants an
22
* RW locking mechanism.
23
* On Posix platforms, we leverage advisory locks provided by fcntl().
24
* Windows doesn't have a native interprocess RW exclusion mechanism,
25
* one could be constructed from the services available, but it is somewhat
26
* complicated. Meanwhile, we observe that in ClamAV, it is extremely rare
27
* that there will ever be an occasion when multiple processes will be
28
* reading the ClamAV database from a given directory at the same, and in
29
* none of those possible cases would it matter if they serialized their
30
* accesses. So, a simple mutual exclusion mechanism will suffice for both
31
* the reader and writer locks on Windows.
38
#include "clamav-config.h"
49
#ifdef HAVE_SYS_STAT_H
61
pthread_mutex_t lock_mutex = PTHREAD_MUTEX_INITIALIZER;
63
#define pthread_mutex_lock(arg)
64
#define pthread_mutex_unlock(arg)
68
struct dblock *lock_link;
69
char lock_file[NAME_MAX];
78
static struct dblock *dblocks = NULL;
80
static void cli_lockname(char *lock_file, size_t lock_file_size, const char *dbdirpath);
81
static int cli_lockdb(const char *dbdirpath, int wait, int writelock);
83
#ifdef DONT_LOCK_DBDIRS
85
int cli_readlockdb(const char *dbdirpath, int wait)
90
int cli_writelockdb(const char *dbdirpath, int wait)
95
int cli_unlockdb(const char *dbdirpath)
100
int cli_freelocks(void)
105
#else /* !DONT_LOCK_DBDIRS */
107
int cli_readlockdb(const char *dbdirpath, int wait)
109
return cli_lockdb(dbdirpath, wait, 0);
112
int cli_writelockdb(const char *dbdirpath, int wait)
114
return cli_lockdb(dbdirpath, wait, 1);
117
int cli_freelocks(void)
119
struct dblock * lock, *nextlock, *usedlocks = NULL;
121
pthread_mutex_lock(&lock_mutex);
122
for(lock = dblocks; lock; lock = nextlock) {
123
/* there might be some locks in use, eg: during a db reload, a failure can lead
124
* to cl_free being called */
125
nextlock = lock->lock_link;
126
if(lock->lock_type != -1 && lock->lock_fd != -1) {
127
lock->lock_link = usedlocks;
135
pthread_mutex_unlock(&lock_mutex);
140
int cli_unlockdb(const char *dbdirpath)
142
char lock_file[NAME_MAX];
148
cli_lockname(lock_file, sizeof(lock_file), dbdirpath);
149
pthread_mutex_lock(&lock_mutex);
150
for(lock=dblocks; lock; lock=lock->lock_link)
151
if(!strcmp(lock_file, lock->lock_file))
153
if((!lock) || (lock->lock_type == -1)) {
154
cli_errmsg("Database Directory: %s not locked\n", dbdirpath);
155
pthread_mutex_unlock(&lock_mutex);
159
memset(&fl, 0, sizeof(fl));
161
if(fcntl(lock->lock_fd, F_SETLK, &fl) == -1) {
163
if(!ReleaseMutex(lock->lock_fd)) {
165
cli_errmsg("Error Unlocking Database Directory %s\n", dbdirpath);
166
pthread_mutex_unlock(&lock_mutex);
168
close(lock->lock_fd);
170
unlink(lock->lock_file);
174
lock->lock_type = -1;
176
close(lock->lock_fd);
178
unlink(lock->lock_file);
180
pthread_mutex_unlock(&lock_mutex);
185
static int cli_lockdb(const char *dbdirpath, int wait, int writelock)
187
char lock_file[NAME_MAX];
194
SECURITY_ATTRIBUTES saAttr;
195
SECURITY_DESCRIPTOR sdDesc;
198
cli_lockname(lock_file, sizeof(lock_file), dbdirpath);
199
pthread_mutex_lock(&lock_mutex);
200
for(lock=dblocks; lock; lock=lock->lock_link)
201
if(!strcmp(lock_file, lock->lock_file))
204
lock = cli_calloc(1, sizeof(*lock));
206
cli_errmsg("cli_lockdb(): Can't allocate lock structure to lock Database Directory: %s\n", dbdirpath);
207
pthread_mutex_unlock(&lock_mutex);
210
lock->lock_link = dblocks;
211
strcpy(lock->lock_file, lock_file);
213
lock->lock_type = -1;
216
if(lock->lock_type != -1) {
217
cli_dbgmsg("Database Directory: %s already %s locked\n", dbdirpath, (lock->lock_type? "write" : "read"));
218
pthread_mutex_unlock(&lock_mutex);
222
if(lock->lock_fd == -1) {
224
if(-1 == (lock->lock_fd = open(lock->lock_file, O_RDWR|O_CREAT|O_TRUNC, S_IRWXU|S_IRWXG|S_IROTH))) {
226
(-1 == (lock->lock_fd = open(lock->lock_file, O_RDWR, 0)))) {
227
cli_dbgmsg("Can't %s Lock file for Database Directory: %s\n", (writelock ? "create" : "open"), dbdirpath);
229
pthread_mutex_unlock(&lock_mutex);
230
return CL_EIO; /* or CL_EACCESS */
236
if(lock->lock_fd != -1) {
237
/* Create a security descriptor which allows any process to acquire the Mutex */
238
InitializeSecurityDescriptor(&sdDesc, SECURITY_DESCRIPTOR_REVISION);
239
SetSecurityDescriptorDacl(&sdDesc, TRUE, NULL, FALSE);
240
saAttr.nLength = sizeof(saAttr);
241
saAttr.bInheritHandle = FALSE;
242
saAttr.lpSecurityDescriptor = &sdDesc;
243
if(!(lock->lock_fd = CreateMutexA(&saAttr, TRUE, lock->lock_file))) {
244
if((GetLastError() != ERROR_ACCESS_DENIED) ||
245
(!(lock->lock_fd = OpenMutexA(MUTEX_MODIFY_STATE, FALSE, lock->lock_file)))) {
246
cli_dbgmsg("Can't Create Mutex Lock for Database Directory: %s\n", dbdirpath);
247
pthread_mutex_unlock(&lock_mutex);
250
LastError = ERROR_ALREADY_EXISTS;
252
LastError = GetLastError();
254
LastError = ERROR_ALREADY_EXISTS;
257
pthread_mutex_unlock(&lock_mutex);
260
memset(&fl, 0, sizeof(fl));
261
fl.l_type = (writelock ? F_WRLCK : F_RDLCK);
262
if(fcntl(lock->lock_fd, ((wait) ? F_SETLKW : F_SETLK), &fl) == -1) {
264
if(errno != EACCES && errno != EAGAIN) {
265
close(lock->lock_fd);
267
unlink(lock->lock_file);
274
if(LastError == ERROR_ALREADY_EXISTS) {
275
if(WAIT_TIMEOUT == WaitForSingleObject(lock->lock_fd, ((wait) ? INFINITE : 0))) {
276
lock->lock_type = -1;
281
lock->lock_type = writelock;
286
static void cli_lockname(char *lock_file, size_t lock_file_size, const char *dbdirpath)
290
lock_file[lock_file_size-1] = '\0';
292
snprintf(lock_file, lock_file_size-1, "%s/.dbLock", dbdirpath);
293
for (c=lock_file; *c; ++c) {
295
snprintf(lock_file, lock_file_size-1, "Global\\ClamAVDB-%s", dbdirpath);
296
for (c=lock_file+16; *c; ++c) {
304
if(c!=lock_file && *(c-1) == '/') { /* compress imbedded // */
306
memmove(c, c+1,strlen(c+1)+1);
307
} else if(c > lock_file+1 && (*(c-2) == '/') && (*(c-1) == '.')) { /* compress imbedded /./ */
309
memmove(c, c+2,strlen(c+2)+1);
314
if(islower(*c)) /* Normalize to upper case */
321
if('/' == lock_file[strlen(lock_file)-1]) /* Remove trailing / */
322
lock_file[strlen(lock_file)-1] = '\0';
326
#endif /* DONT_LOCK_DBDIRS */