~ubuntu-branches/ubuntu/feisty/clamav/feisty

« back to all changes in this revision

Viewing changes to libclamav/lockdb.c

  • Committer: Bazaar Package Importer
  • Author(s): Kees Cook
  • Date: 2007-02-20 10:33:44 UTC
  • mto: This revision was merged to the branch mainline in revision 16.
  • Revision ID: james.westby@ubuntu.com-20070220103344-zgcu2psnx9d98fpa
Tags: upstream-0.90
ImportĀ upstreamĀ versionĀ 0.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  Copyright (C) 2006 Mark Pizzolato <clamav-devel@subscriptions.pizzolato.net>
 
3
 *
 
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.
 
8
 *
 
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.
 
13
 *
 
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,
 
17
 *  MA 02110-1301, USA.
 
18
 */
 
19
 
 
20
/*
 
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.
 
32
 */
 
33
#ifdef  _MSC_VER
 
34
#include <windows.h>
 
35
#endif
 
36
 
 
37
#if HAVE_CONFIG_H
 
38
#include "clamav-config.h"
 
39
#endif
 
40
 
 
41
#include <stdio.h>
 
42
#include <stdarg.h>
 
43
#include <stdlib.h>
 
44
#include <string.h>
 
45
#include <ctype.h>
 
46
#ifdef HAVE_UNISTD_H
 
47
#include <unistd.h>
 
48
#endif
 
49
#ifdef  HAVE_SYS_STAT_H
 
50
#include <sys/stat.h>
 
51
#endif
 
52
#include <fcntl.h>
 
53
#include <errno.h>
 
54
 
 
55
#include "clamav.h"
 
56
#include "others.h"
 
57
#include "lockdb.h"
 
58
 
 
59
#ifdef CL_THREAD_SAFE
 
60
#include <pthread.h>
 
61
pthread_mutex_t lock_mutex = PTHREAD_MUTEX_INITIALIZER;
 
62
#else
 
63
#define pthread_mutex_lock(arg)
 
64
#define pthread_mutex_unlock(arg)
 
65
#endif
 
66
 
 
67
struct dblock {
 
68
        struct dblock *lock_link;
 
69
        char lock_file[NAME_MAX];
 
70
#ifndef C_WINDOWS
 
71
        int lock_fd;
 
72
#else
 
73
        HANDLE lock_fd;
 
74
#endif
 
75
        int lock_type;
 
76
};
 
77
 
 
78
static struct dblock *dblocks = NULL;
 
79
 
 
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);
 
82
 
 
83
#ifdef DONT_LOCK_DBDIRS
 
84
 
 
85
int cli_readlockdb(const char *dbdirpath, int wait)
 
86
{
 
87
    return CL_SUCCESS;
 
88
}
 
89
 
 
90
int cli_writelockdb(const char *dbdirpath, int wait)
 
91
{
 
92
    return CL_SUCCESS;
 
93
}
 
94
 
 
95
int cli_unlockdb(const char *dbdirpath)
 
96
{
 
97
    return CL_SUCCESS;
 
98
}
 
99
 
 
100
int cli_freelocks(void)
 
101
{
 
102
        return CL_SUCCESS;
 
103
}
 
104
 
 
105
#else /* !DONT_LOCK_DBDIRS */
 
106
 
 
107
int cli_readlockdb(const char *dbdirpath, int wait)
 
108
{
 
109
    return cli_lockdb(dbdirpath, wait, 0);
 
110
}
 
111
 
 
112
int cli_writelockdb(const char *dbdirpath, int wait)
 
113
{
 
114
    return cli_lockdb(dbdirpath, wait, 1);
 
115
}
 
116
 
 
117
int cli_freelocks(void)
 
118
{
 
119
        struct dblock * lock, *nextlock, *usedlocks = NULL;
 
120
 
 
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;
 
128
                        usedlocks = lock;
 
129
                }
 
130
                else {
 
131
                        free(lock);
 
132
                }
 
133
        }
 
134
        dblocks = usedlocks;
 
135
        pthread_mutex_unlock(&lock_mutex);
 
136
        return CL_SUCCESS;
 
137
}
 
138
 
 
139
 
 
140
int cli_unlockdb(const char *dbdirpath)
 
141
{
 
142
        char lock_file[NAME_MAX];
 
143
        struct dblock *lock;
 
144
#ifndef C_WINDOWS
 
145
        struct flock fl;
 
146
#endif
 
147
 
 
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))
 
152
            break;
 
153
    if((!lock) || (lock->lock_type == -1)) {
 
154
        cli_errmsg("Database Directory: %s not locked\n", dbdirpath);
 
155
        pthread_mutex_unlock(&lock_mutex);
 
156
        return CL_ELOCKDB;
 
157
    }
 
158
#ifndef C_WINDOWS
 
159
    memset(&fl, 0, sizeof(fl));
 
160
    fl.l_type = F_UNLCK;
 
161
    if(fcntl(lock->lock_fd, F_SETLK, &fl) == -1) {
 
162
#else
 
163
    if(!ReleaseMutex(lock->lock_fd)) {
 
164
#endif
 
165
        cli_errmsg("Error Unlocking Database Directory %s\n", dbdirpath);
 
166
        pthread_mutex_unlock(&lock_mutex);
 
167
#ifndef C_WINDOWS
 
168
        close(lock->lock_fd);
 
169
        lock->lock_fd=-1;
 
170
        unlink(lock->lock_file);
 
171
#endif
 
172
        return CL_ELOCKDB;
 
173
    }
 
174
    lock->lock_type = -1;
 
175
#ifndef C_WINDOWS
 
176
    close(lock->lock_fd);
 
177
    lock->lock_fd=-1;
 
178
    unlink(lock->lock_file);
 
179
#endif
 
180
    pthread_mutex_unlock(&lock_mutex);
 
181
 
 
182
    return CL_SUCCESS;
 
183
}
 
184
 
 
185
static int cli_lockdb(const char *dbdirpath, int wait, int writelock)
 
186
{
 
187
        char lock_file[NAME_MAX];
 
188
        struct dblock *lock;
 
189
#ifndef C_WINDOWS
 
190
        struct flock fl;
 
191
        mode_t old_mask;
 
192
#else
 
193
        DWORD LastError;
 
194
        SECURITY_ATTRIBUTES saAttr;
 
195
        SECURITY_DESCRIPTOR sdDesc;
 
196
#endif
 
197
 
 
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))
 
202
            break;
 
203
    if(!lock) {
 
204
        lock = cli_calloc(1, sizeof(*lock));
 
205
        if(!lock) {
 
206
            cli_errmsg("cli_lockdb(): Can't allocate lock structure to lock Database Directory: %s\n", dbdirpath);
 
207
            pthread_mutex_unlock(&lock_mutex);
 
208
            return CL_EMEM;
 
209
        }
 
210
        lock->lock_link = dblocks;
 
211
        strcpy(lock->lock_file, lock_file);
 
212
        lock->lock_fd = -1;
 
213
        lock->lock_type = -1;
 
214
        dblocks = lock;
 
215
    }
 
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);
 
219
        return CL_ELOCKDB;
 
220
    }
 
221
#ifndef C_WINDOWS
 
222
    if(lock->lock_fd == -1) {
 
223
        old_mask = umask(0);
 
224
        if(-1 == (lock->lock_fd = open(lock->lock_file, O_RDWR|O_CREAT|O_TRUNC, S_IRWXU|S_IRWXG|S_IROTH))) {
 
225
            if((writelock) ||
 
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);
 
228
                umask(old_mask);
 
229
                pthread_mutex_unlock(&lock_mutex);
 
230
                return CL_EIO; /* or CL_EACCESS */
 
231
            }
 
232
        }
 
233
        umask(old_mask);
 
234
    }
 
235
#else
 
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);
 
248
                return CL_EIO;
 
249
            }
 
250
            LastError = ERROR_ALREADY_EXISTS;
 
251
        }
 
252
        LastError = GetLastError();
 
253
    } else {
 
254
        LastError = ERROR_ALREADY_EXISTS;
 
255
    }
 
256
#endif
 
257
    pthread_mutex_unlock(&lock_mutex);
 
258
 
 
259
#ifndef C_WINDOWS
 
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) {
 
263
#ifndef C_WINDOWS
 
264
        if(errno != EACCES && errno != EAGAIN) {
 
265
            close(lock->lock_fd);
 
266
            lock->lock_fd=-1;
 
267
            unlink(lock->lock_file);
 
268
            return CL_EIO;
 
269
        }
 
270
#endif
 
271
        return CL_ELOCKDB;
 
272
    }
 
273
#else
 
274
    if(LastError == ERROR_ALREADY_EXISTS) {
 
275
        if(WAIT_TIMEOUT == WaitForSingleObject(lock->lock_fd, ((wait) ? INFINITE : 0))) {
 
276
            lock->lock_type = -1;
 
277
            return CL_ELOCKDB;
 
278
        }
 
279
    }
 
280
#endif
 
281
    lock->lock_type = writelock;
 
282
 
 
283
    return CL_SUCCESS;
 
284
}
 
285
 
 
286
static void cli_lockname(char *lock_file, size_t lock_file_size, const char *dbdirpath)
 
287
{
 
288
        char *c;
 
289
 
 
290
    lock_file[lock_file_size-1] = '\0';
 
291
#ifndef C_WINDOWS
 
292
    snprintf(lock_file, lock_file_size-1, "%s/.dbLock", dbdirpath);
 
293
    for (c=lock_file; *c; ++c) {
 
294
#else
 
295
    snprintf(lock_file, lock_file_size-1, "Global\\ClamAVDB-%s", dbdirpath);
 
296
    for (c=lock_file+16; *c; ++c) {
 
297
#endif
 
298
        switch (*c) {
 
299
#ifdef C_WINDOWS
 
300
        case '\\':
 
301
            *c = '/';
 
302
#endif
 
303
        case '/':
 
304
            if(c!=lock_file && *(c-1) == '/') { /* compress imbedded // */
 
305
                --c;
 
306
                memmove(c, c+1,strlen(c+1)+1);
 
307
            } else if(c > lock_file+1 && (*(c-2) == '/') && (*(c-1) == '.')) { /* compress imbedded /./ */
 
308
                c -= 2;
 
309
                memmove(c, c+2,strlen(c+2)+1);
 
310
            }
 
311
            break;
 
312
#ifdef C_WINDOWS
 
313
        default:
 
314
            if(islower(*c)) /* Normalize to upper case */
 
315
                *c = toupper(*c);
 
316
            break;
 
317
#endif
 
318
        }
 
319
    }
 
320
#ifdef C_WINDOWS
 
321
    if('/' == lock_file[strlen(lock_file)-1]) /* Remove trailing / */
 
322
        lock_file[strlen(lock_file)-1] = '\0';
 
323
#endif
 
324
}
 
325
 
 
326
#endif /* DONT_LOCK_DBDIRS */