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