Imported Upstream version 3.3.2
[debian/amanda] / common-src / amflock.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: amflock.c 7161 2007-07-03 16:27:26Z dustin $
28  *
29  * file locking routines, put here to hide the system dependant stuff
30  * from the rest of the code
31  */
32
33 #include "amanda.h"
34
35 /*
36  * New Implementation
37  */
38
39 #if (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 31))
40 # pragma GCC diagnostic push
41 # pragma GCC diagnostic ignored "-Wmissing-field-initializers"
42   static GStaticMutex lock_lock = G_STATIC_MUTEX_INIT;
43 # pragma GCC diagnostic pop
44 #else
45   static GStaticMutex lock_lock = G_STATIC_MUTEX_INIT;
46 #endif
47 static GHashTable *locally_locked_files = NULL;
48 static int lock_rw_rd(file_lock *lock, short l_type);
49
50 file_lock *
51 file_lock_new(
52     const char *filename)
53 {
54     file_lock *lock = g_new0(file_lock, 1);
55     lock->filename = g_strdup(filename);
56     lock->fd = -1;
57
58     return lock;
59 }
60
61 void
62 file_lock_free(
63     file_lock *lock)
64 {
65     g_static_mutex_lock(&lock_lock);
66     if (locally_locked_files) {
67         g_hash_table_remove(locally_locked_files,
68                             lock->filename);
69     }
70
71     if (lock->data)
72         g_free(lock->data);
73     if (lock->filename)
74         g_free(lock->filename);
75
76     if (lock->fd != -1)
77         close(lock->fd);
78
79     g_static_mutex_unlock(&lock_lock);
80 }
81
82 int
83 file_lock_lock(
84     file_lock *lock)
85 {
86     int rv = -2;
87     int fd = -1;
88     int saved_errno;
89     struct flock lock_buf;
90     struct stat stat_buf;
91
92     g_assert(!lock->locked);
93
94     /* protect from overlapping lock operations within a process */
95     g_static_mutex_lock(&lock_lock);
96     if (!locally_locked_files) {
97         locally_locked_files = g_hash_table_new(g_str_hash, g_str_equal);
98     }
99
100     /* if this filename is in the hash table, then some other thread in this
101      * process has locked it */
102     if (g_hash_table_lookup(locally_locked_files, lock->filename)) {
103         rv = 1;
104         goto done;
105     }
106
107     /* The locks are advisory, so an error here never means the lock is already
108      * taken. */
109     lock->fd = fd = open(lock->filename, O_CREAT|O_RDWR, 0666);
110     if (fd < 0) {
111         rv = -1;
112         goto done;
113     }
114
115     /* now try locking it */
116     lock_buf.l_type = F_WRLCK;
117     lock_buf.l_start = 0;
118     lock_buf.l_whence = SEEK_SET;
119     lock_buf.l_len = 0; /* to EOF */
120     if (fcntl(fd, F_SETLK, &lock_buf) < 0) {
121         if (errno == EACCES || errno == EAGAIN)
122             rv = 1;
123         else
124             rv = -1;
125         goto done;
126     }
127
128     /* and read the file in its entirety */
129     if (fstat(fd, &stat_buf) < 0) {
130         rv = -1;
131         goto done;
132     }
133
134     if (!(stat_buf.st_mode & S_IFREG)) {
135         rv = -1;
136         errno = EINVAL;
137         goto done;
138     }
139
140     if (stat_buf.st_size) {
141         lock->data = g_malloc(stat_buf.st_size);
142         lock->len = stat_buf.st_size;
143         if (full_read(fd, lock->data, lock->len) < lock->len) {
144             rv = -1;
145             goto done;
146         }
147     }
148
149     fd = -1; /* we'll keep the file now */
150     lock->locked = TRUE;
151
152     /* the lock is acquired; record this in the hash table */
153     g_hash_table_insert(locally_locked_files, lock->filename, lock->filename);
154
155     rv = 0;
156
157 done:
158     saved_errno = errno;
159     g_static_mutex_unlock(&lock_lock);
160     if (fd >= 0) /* close and unlock if an error occurred */
161         close(fd);
162     errno = saved_errno;
163     return rv;
164 }
165
166 static int
167 lock_rw_rd(
168     file_lock *lock,
169     short      l_type)
170 {
171     int rv = -2;
172     int fd = -1;
173     int saved_errno;
174     struct flock lock_buf;
175     struct stat stat_buf;
176
177     g_assert(!lock->locked);
178
179     /* protect from overlapping lock operations within a process */
180     g_static_mutex_lock(&lock_lock);
181
182     /* The locks are advisory, so an error here never means the lock is already
183      * taken. */
184     lock->fd = fd = open(lock->filename, O_CREAT|O_RDWR, 0666);
185     if (fd < 0) {
186         rv = -1;
187         goto done;
188     }
189
190     /* now try locking it */
191     lock_buf.l_type = l_type;
192     lock_buf.l_start = 0;
193     lock_buf.l_whence = SEEK_SET;
194     lock_buf.l_len = 0; /* to EOF */
195     if (fcntl(fd, F_SETLK, &lock_buf) < 0) {
196         if (errno == EACCES || errno == EAGAIN)
197             rv = 1;
198         else
199             rv = -1;
200         goto done;
201     }
202
203     /* and read the file in its entirety */
204     if (fstat(fd, &stat_buf) < 0) {
205         rv = -1;
206         goto done;
207     }
208
209     if (!(stat_buf.st_mode & S_IFREG)) {
210         rv = -1;
211         errno = EINVAL;
212         goto done;
213     }
214
215     fd = -1; /* we'll keep the file now */
216     lock->locked = TRUE;
217
218     rv = 0;
219
220 done:
221     saved_errno = errno;
222     g_static_mutex_unlock(&lock_lock);
223     if (fd >= 0) /* close and unlock if an error occurred */
224         close(fd);
225     errno = saved_errno;
226     return rv;
227 }
228
229 int
230 file_lock_lock_wr(
231     file_lock *lock)
232 {
233     return lock_rw_rd(lock, F_WRLCK);
234 }
235
236 int
237 file_lock_lock_rd(
238     file_lock *lock)
239 {
240     return lock_rw_rd(lock, F_RDLCK);
241 }
242
243 int
244 file_lock_locked(
245     file_lock *lock)
246 {
247     return lock->locked;
248 }
249
250 int
251 file_lock_write(
252     file_lock *lock,
253     const char *data,
254     size_t len)
255 {
256     int fd = lock->fd;
257
258     g_assert(lock->locked);
259
260     /* seek to position 0, rewrite, and truncate */
261     if (lseek(fd, 0, SEEK_SET) < 0)
262         return -1;
263
264     /* from here on out, any errors have corrupted the datafile.. */
265     if (full_write(fd, data, len) < len)
266         return -1;
267
268     if (lock->len > len) {
269         if (ftruncate(fd, len) < 0)
270             return -1;
271     }
272
273     if (lock->data)
274         g_free(lock->data);
275     lock->data = g_strdup(data);
276     lock->len = len;
277
278     return 0;
279 }
280
281 int
282 file_lock_unlock(
283     file_lock *lock)
284 {
285     g_assert(lock->locked);
286
287     g_static_mutex_lock(&lock_lock);
288
289     /* relase the filesystem-level lock */
290     close(lock->fd);
291
292     /* and the hash table entry */
293     if (locally_locked_files) {
294         g_hash_table_remove(locally_locked_files, lock->filename);
295     }
296
297     g_static_mutex_unlock(&lock_lock);
298
299     if (lock->data)
300         g_free(lock->data);
301     lock->data = NULL;
302     lock->len = 0;
303     lock->fd = -1;
304     lock->locked = FALSE;
305
306     return 0;
307 }
308
309 /*
310  * Old Implementation
311  */
312
313 /*
314 **
315 ** Notes:
316 ** - These are "best effort" routines.
317 ** - "configure" has four variables that are used to determine which type of
318 **   locking to use:
319 **     USE_POSIX_FCNTL - use fcntl().  The full job.
320 **     USE_FLOCK       - use flock().  Does just as well.
321 **     USE_LOCKF       - use lockf().  Only handles advisory, exclusive,
322 **                       blocking file locks as used by Amanda.
323 **     USE_LNLOCK      - Home brew exclusive, blocking file lock.
324 **     <none>          - No locking available.  User beware!
325 */
326
327 /* Interface to the implementations in common-src/amflock-*.c */
328
329 #ifdef WANT_AMFLOCK_POSIX
330 extern amflock_impl_t amflock_posix_impl;
331 #endif
332 #ifdef WANT_AMFLOCK_FLOCK
333 extern amflock_impl_t amflock_flock_impl;
334 #endif
335 #ifdef WANT_AMFLOCK_LOCKF
336 extern amflock_impl_t amflock_lockf_impl;
337 #endif
338 #ifdef WANT_AMFLOCK_LNLOCK
339 extern amflock_impl_t amflock_lnlock_impl;
340 #endif
341
342 amflock_impl_t *amflock_impls[] = {
343 #ifdef WANT_AMFLOCK_POSIX
344     &amflock_posix_impl,
345 #endif
346 #ifdef WANT_AMFLOCK_FLOCK
347     &amflock_flock_impl,
348 #endif
349 #ifdef WANT_AMFLOCK_LOCKF
350     &amflock_lockf_impl,
351 #endif
352 #ifdef WANT_AMFLOCK_LNLOCK
353     &amflock_lnlock_impl,
354 #endif
355     NULL
356 };
357
358 /* Interface functions */
359 /* FIXME: for now, these just use the first non-NULL implementation
360  */
361
362 /* Get a file lock (for read/write files).
363 */
364 int
365 amflock(
366     int         fd,
367     char *      resource)
368 {
369     if (!amflock_impls[0]) return 0; /* no locking */
370     return amflock_impls[0]->amflock_impl(fd, resource);
371 }
372
373 /*
374  * Get a file lock (for read-only files).
375  */
376 int
377 amroflock(
378     int         fd,
379     char *      resource)
380 {
381     if (!amflock_impls[0]) return 0; /* no locking */
382     return amflock_impls[0]->amroflock_impl(fd, resource);
383 }
384
385 /*
386  * Release a file lock.
387  */
388 int
389 amfunlock(
390     int         fd,
391     char *      resource)
392 {
393     if (!amflock_impls[0]) return 0; /* no locking */
394     return amflock_impls[0]->amfunlock_impl(fd, resource);
395 }