858c2885594a4403b948669f8c3d0b42df84f552
[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 static GStaticMutex lock_lock = G_STATIC_MUTEX_INIT;
40 static GHashTable *locally_locked_files = NULL;
41 static int lock_rw_rd(file_lock *lock, short l_type);
42
43 file_lock *
44 file_lock_new(
45     const char *filename)
46 {
47     file_lock *lock = g_new0(file_lock, 1);
48     lock->filename = g_strdup(filename);
49     lock->fd = -1;
50
51     return lock;
52 }
53
54 void
55 file_lock_free(
56     file_lock *lock)
57 {
58     g_static_mutex_lock(&lock_lock);
59     if (locally_locked_files) {
60         g_hash_table_remove(locally_locked_files,
61                             lock->filename);
62     }
63
64     if (lock->data)
65         g_free(lock->data);
66     if (lock->filename)
67         g_free(lock->filename);
68
69     if (lock->fd != -1)
70         close(lock->fd);
71
72     g_static_mutex_unlock(&lock_lock);
73 }
74
75 int
76 file_lock_lock(
77     file_lock *lock)
78 {
79     int rv = -2;
80     int fd = -1;
81     int saved_errno;
82     struct flock lock_buf;
83     struct stat stat_buf;
84
85     g_assert(!lock->locked);
86
87     /* protect from overlapping lock operations within a process */
88     g_static_mutex_lock(&lock_lock);
89     if (!locally_locked_files) {
90         locally_locked_files = g_hash_table_new(g_str_hash, g_str_equal);
91     }
92
93     /* if this filename is in the hash table, then some other thread in this
94      * process has locked it */
95     if (g_hash_table_lookup(locally_locked_files, lock->filename)) {
96         rv = 1;
97         goto done;
98     }
99
100     /* The locks are advisory, so an error here never means the lock is already
101      * taken. */
102     lock->fd = fd = open(lock->filename, O_CREAT|O_RDWR, 0666);
103     if (fd < 0) {
104         rv = -1;
105         goto done;
106     }
107
108     /* now try locking it */
109     lock_buf.l_type = F_WRLCK;
110     lock_buf.l_start = 0;
111     lock_buf.l_whence = SEEK_SET;
112     lock_buf.l_len = 0; /* to EOF */
113     if (fcntl(fd, F_SETLK, &lock_buf) < 0) {
114         if (errno == EACCES || errno == EAGAIN)
115             rv = 1;
116         else
117             rv = -1;
118         goto done;
119     }
120
121     /* and read the file in its entirety */
122     if (fstat(fd, &stat_buf) < 0) {
123         rv = -1;
124         goto done;
125     }
126
127     if (!(stat_buf.st_mode & S_IFREG)) {
128         rv = -1;
129         errno = EINVAL;
130         goto done;
131     }
132
133     if (stat_buf.st_size) {
134         lock->data = g_malloc(stat_buf.st_size);
135         lock->len = stat_buf.st_size;
136         if (full_read(fd, lock->data, lock->len) < lock->len) {
137             rv = -1;
138             goto done;
139         }
140     }
141
142     fd = -1; /* we'll keep the file now */
143     lock->locked = TRUE;
144
145     /* the lock is acquired; record this in the hash table */
146     g_hash_table_insert(locally_locked_files, lock->filename, lock->filename);
147
148     rv = 0;
149
150 done:
151     saved_errno = errno;
152     g_static_mutex_unlock(&lock_lock);
153     if (fd >= 0) /* close and unlock if an error occurred */
154         close(fd);
155     errno = saved_errno;
156     return rv;
157 }
158
159 static int
160 lock_rw_rd(
161     file_lock *lock,
162     short      l_type)
163 {
164     int rv = -2;
165     int fd = -1;
166     int saved_errno;
167     struct flock lock_buf;
168     struct stat stat_buf;
169
170     g_assert(!lock->locked);
171
172     /* protect from overlapping lock operations within a process */
173     g_static_mutex_lock(&lock_lock);
174
175     /* The locks are advisory, so an error here never means the lock is already
176      * taken. */
177     lock->fd = fd = open(lock->filename, O_CREAT|O_RDWR, 0666);
178     if (fd < 0) {
179         rv = -1;
180         goto done;
181     }
182
183     /* now try locking it */
184     lock_buf.l_type = l_type;
185     lock_buf.l_start = 0;
186     lock_buf.l_whence = SEEK_SET;
187     lock_buf.l_len = 0; /* to EOF */
188     if (fcntl(fd, F_SETLK, &lock_buf) < 0) {
189         if (errno == EACCES || errno == EAGAIN)
190             rv = 1;
191         else
192             rv = -1;
193         goto done;
194     }
195
196     /* and read the file in its entirety */
197     if (fstat(fd, &stat_buf) < 0) {
198         rv = -1;
199         goto done;
200     }
201
202     if (!(stat_buf.st_mode & S_IFREG)) {
203         rv = -1;
204         errno = EINVAL;
205         goto done;
206     }
207
208     fd = -1; /* we'll keep the file now */
209     lock->locked = TRUE;
210
211     rv = 0;
212
213 done:
214     saved_errno = errno;
215     g_static_mutex_unlock(&lock_lock);
216     if (fd >= 0) /* close and unlock if an error occurred */
217         close(fd);
218     errno = saved_errno;
219     return rv;
220 }
221
222 int
223 file_lock_lock_wr(
224     file_lock *lock)
225 {
226     return lock_rw_rd(lock, F_WRLCK);
227 }
228
229 int
230 file_lock_lock_rd(
231     file_lock *lock)
232 {
233     return lock_rw_rd(lock, F_RDLCK);
234 }
235
236 int
237 file_lock_locked(
238     file_lock *lock)
239 {
240     return lock->locked;
241 }
242
243 int
244 file_lock_write(
245     file_lock *lock,
246     const char *data,
247     size_t len)
248 {
249     int fd = lock->fd;
250
251     g_assert(lock->locked);
252
253     /* seek to position 0, rewrite, and truncate */
254     if (lseek(fd, 0, SEEK_SET) < 0)
255         return -1;
256
257     /* from here on out, any errors have corrupted the datafile.. */
258     if (full_write(fd, data, len) < len)
259         return -1;
260
261     if (lock->len > len) {
262         if (ftruncate(fd, len) < 0)
263             return -1;
264     }
265
266     if (lock->data)
267         g_free(lock->data);
268     lock->data = g_strdup(data);
269     lock->len = len;
270
271     return 0;
272 }
273
274 int
275 file_lock_unlock(
276     file_lock *lock)
277 {
278     g_assert(lock->locked);
279
280     g_static_mutex_lock(&lock_lock);
281
282     /* relase the filesystem-level lock */
283     close(lock->fd);
284
285     /* and the hash table entry */
286     if (locally_locked_files) {
287         g_hash_table_remove(locally_locked_files, lock->filename);
288     }
289
290     g_static_mutex_unlock(&lock_lock);
291
292     if (lock->data)
293         g_free(lock->data);
294     lock->data = NULL;
295     lock->len = 0;
296     lock->fd = -1;
297     lock->locked = FALSE;
298
299     return 0;
300 }
301
302 /*
303  * Old Implementation
304  */
305
306 /*
307 **
308 ** Notes:
309 ** - These are "best effort" routines.
310 ** - "configure" has four variables that are used to determine which type of
311 **   locking to use:
312 **     USE_POSIX_FCNTL - use fcntl().  The full job.
313 **     USE_FLOCK       - use flock().  Does just as well.
314 **     USE_LOCKF       - use lockf().  Only handles advisory, exclusive,
315 **                       blocking file locks as used by Amanda.
316 **     USE_LNLOCK      - Home brew exclusive, blocking file lock.
317 **     <none>          - No locking available.  User beware!
318 */
319
320 /* Interface to the implementations in common-src/amflock-*.c */
321
322 #ifdef WANT_AMFLOCK_POSIX
323 extern amflock_impl_t amflock_posix_impl;
324 #endif
325 #ifdef WANT_AMFLOCK_FLOCK
326 extern amflock_impl_t amflock_flock_impl;
327 #endif
328 #ifdef WANT_AMFLOCK_LOCKF
329 extern amflock_impl_t amflock_lockf_impl;
330 #endif
331 #ifdef WANT_AMFLOCK_LNLOCK
332 extern amflock_impl_t amflock_lnlock_impl;
333 #endif
334
335 amflock_impl_t *amflock_impls[] = {
336 #ifdef WANT_AMFLOCK_POSIX
337     &amflock_posix_impl,
338 #endif
339 #ifdef WANT_AMFLOCK_FLOCK
340     &amflock_flock_impl,
341 #endif
342 #ifdef WANT_AMFLOCK_LOCKF
343     &amflock_lockf_impl,
344 #endif
345 #ifdef WANT_AMFLOCK_LNLOCK
346     &amflock_lnlock_impl,
347 #endif
348     NULL
349 };
350
351 /* Interface functions */
352 /* FIXME: for now, these just use the first non-NULL implementation
353  */
354
355 /* Get a file lock (for read/write files).
356 */
357 int
358 amflock(
359     int         fd,
360     char *      resource)
361 {
362     if (!amflock_impls[0]) return 0; /* no locking */
363     return amflock_impls[0]->amflock_impl(fd, resource);
364 }
365
366 /*
367  * Get a file lock (for read-only files).
368  */
369 int
370 amroflock(
371     int         fd,
372     char *      resource)
373 {
374     if (!amflock_impls[0]) return 0; /* no locking */
375     return amflock_impls[0]->amroflock_impl(fd, resource);
376 }
377
378 /*
379  * Release a file lock.
380  */
381 int
382 amfunlock(
383     int         fd,
384     char *      resource)
385 {
386     if (!amflock_impls[0]) return 0; /* no locking */
387     return amflock_impls[0]->amfunlock_impl(fd, resource);
388 }