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