File lock fixes for Windows
[idzebra-moved-to-github.git] / util / flock.c
1 /* $Id: flock.c,v 1.9 2006-06-27 12:24:14 adam Exp $
2    Copyright (C) 1995-2006
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23
24 #include <stdio.h>
25 #include <assert.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <sys/types.h>
30 #ifdef WIN32
31 #include <io.h>
32 #include <sys/locking.h>
33 #endif
34 #if HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37
38 #include <idzebra/flock.h>
39 #include <zebra-lock.h>
40 #include <yaz/xmalloc.h>
41 #include <yaz/log.h>
42
43 /** have this module (mutex) been initialized? */
44 static int initialized = 0;
45
46 /** mutex for lock_list below */
47 Zebra_mutex lock_list_mutex;
48
49 /** our list of file locked files */
50 static struct zebra_lock_info *lock_list = 0;
51
52 /** the internal handle, with a pointer to each lock file info */
53 struct zebra_lock_handle {
54 #ifndef WIN32
55     /** so we can call zebra_lock_rdwr_wunlock or zebra_lock_lock_runlock */
56     int write_flag;
57 #endif
58     struct zebra_lock_info *p;
59 };
60
61 struct zebra_lock_info {
62     /** file descriptor */
63     int fd;
64     /** full path (xmalloc'ed) */
65     char *fname;
66     /** reference counter: number of zebra_lock_handles pointing to us */
67     int ref_count;
68 #ifndef WIN32
69     /** number of file write locks/read locks */
70     int no_file_write_lock;
71     int no_file_read_lock;
72     Zebra_lock_rdwr rdwr_lock;
73     Zebra_mutex file_mutex;
74 #endif
75     /** next in lock list */
76     struct zebra_lock_info *next;
77 };
78
79 static int log_level = 0;
80
81 char *zebra_mk_fname(const char *dir, const char *name)
82 {
83     int dlen = dir ? strlen(dir) : 0;
84     char *fname = xmalloc(dlen + strlen(name) + 3);
85     
86 #ifdef WIN32
87     if (dlen)
88     {
89         int last_one = dir[dlen-1];
90         
91         if (!strchr("/\\:", last_one))
92             sprintf(fname, "%s\\%s", dir, name);
93         else
94             sprintf(fname, "%s%s", dir, name);
95     }
96     else
97         sprintf(fname, "%s", name);
98 #else
99     if (dlen)
100     {
101         int last_one = dir[dlen-1];
102
103         if (!strchr("/", last_one))
104             sprintf(fname, "%s/%s", dir, name);
105         else
106             sprintf(fname, "%s%s", dir, name);
107     }
108     else
109         sprintf(fname, "%s", name);
110 #endif
111     return fname;
112 }
113
114 ZebraLockHandle zebra_lock_create(const char *dir, const char *name)
115 {
116     char *fname = zebra_mk_fname(dir, name);
117     struct zebra_lock_info *p = 0;
118     ZebraLockHandle h = 0;
119
120     assert(initialized);
121
122     zebra_mutex_lock(&lock_list_mutex);
123 #ifndef WIN32 
124     for (p = lock_list; p ; p = p->next)
125         if (!strcmp(p->fname, fname))
126             break;
127 #endif
128     if (!p)
129     {
130         p = (struct zebra_lock_info *) xmalloc(sizeof(*p));
131         
132         p->ref_count = 0;
133 #ifdef WIN32
134         p->fd = open(name, O_BINARY|O_RDONLY);
135         if (p->fd == -1)
136             p->fd = open(fname, (O_BINARY|O_CREAT|O_RDWR), 0666);
137 #else
138         p->fd = open(fname, (O_BINARY|O_CREAT|O_RDWR), 0666);
139 #endif
140         if (p->fd == -1)
141         {
142             xfree(p);
143             yaz_log(YLOG_WARN | YLOG_ERRNO, 
144                     "zebra_lock_create fail fname=%s", fname);
145             p = 0;
146         }
147         else
148         {
149             p->fname = fname;
150             fname = 0;  /* fname buffer now owned by p->fname */
151             yaz_log(log_level, "zebra_lock_create fd=%d p=%p fname=%s",
152                     p->fd, p, p->fname);
153 #ifndef WIN32
154             zebra_lock_rdwr_init(&p->rdwr_lock);
155             zebra_mutex_init(&p->file_mutex);
156             p->no_file_write_lock = 0;
157             p->no_file_read_lock = 0;
158 #endif
159             p->next = lock_list;
160             lock_list = p;
161         }
162     }
163     if (p)
164     {
165         p->ref_count++;
166         h = (ZebraLockHandle) xmalloc(sizeof(*h));
167         h->p = p;
168 #ifndef WIN32
169         h->write_flag = 0;
170 #endif
171     }
172     zebra_mutex_unlock(&lock_list_mutex);
173     xfree(fname); /* free it - if it's still there */
174
175     return h;
176 }
177
178 void zebra_lock_destroy(ZebraLockHandle h)
179 {
180     if (!h)
181         return;
182     yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s",
183             h->p->fd, h, h->p->fname);
184     zebra_mutex_lock(&lock_list_mutex);
185     yaz_log(log_level, "zebra_lock_destroy fd=%d p=%p fname=%s 1",
186             h->p->fd, h, h->p->fname);
187     assert(h->p->ref_count > 0);
188     --(h->p->ref_count);
189     if (h->p->ref_count == 0)
190     {
191         struct zebra_lock_info **hp = &lock_list;
192         while (*hp)
193         {
194             if (*hp == h->p)
195             {
196                 *hp = h->p->next;
197                 break;
198             }
199             else
200                 hp = &(*hp)->next;
201         }
202 #ifndef WIN32
203         zebra_lock_rdwr_destroy(&h->p->rdwr_lock);
204         zebra_mutex_destroy(&h->p->file_mutex);
205 #endif
206         if (h->p->fd != -1)
207             close(h->p->fd);
208         xfree(h->p->fname);
209         xfree(h->p);
210     }
211     xfree(h);
212     zebra_mutex_unlock(&lock_list_mutex);
213 }
214
215 #ifndef WIN32
216 static int unixLock(int fd, int type, int cmd)
217 {
218     struct flock area;
219     area.l_type = type;
220     area.l_whence = SEEK_SET;
221     area.l_len = area.l_start = 0L;
222     return fcntl(fd, cmd, &area);
223 }
224 #endif
225
226 int zebra_lock_w(ZebraLockHandle h)
227 {
228     int r;
229     yaz_log(log_level, "zebra_lock_w fd=%d p=%p fname=%s", 
230             h->p->fd, h, h->p->fname);
231     
232 #ifdef WIN32
233     while ((r = _locking(h->p->fd, _LK_LOCK, 1)))
234         ;
235 #else
236     zebra_mutex_lock(&h->p->file_mutex);
237     if (h->p->no_file_write_lock == 0)
238     {
239         /* if there is already a read lock.. upgrade to write lock */
240         r = unixLock(h->p->fd, F_WRLCK, F_SETLKW);
241     }
242     h->p->no_file_write_lock++;
243     zebra_mutex_unlock(&h->p->file_mutex);
244
245     zebra_lock_rdwr_wlock(&h->p->rdwr_lock);
246     h->write_flag = 1;
247 #endif
248     return r;
249 }
250
251 int zebra_lock_r(ZebraLockHandle h)
252 {
253     int r;
254     yaz_log(log_level, "zebra_lock_r fd=%d p=%p fname=%s", 
255             h->p->fd, h, h->p->fname);
256 #ifdef WIN32
257     while ((r = _locking(h->p->fd, _LK_LOCK, 1)))
258         ;
259 #else
260     zebra_mutex_lock(&h->p->file_mutex);
261     if (h->p->no_file_read_lock == 0 && h->p->no_file_write_lock == 0)
262     {
263         /* only read lock if no write locks already */
264         r = unixLock(h->p->fd, F_RDLCK, F_SETLKW);
265     }
266     h->p->no_file_read_lock++;
267     zebra_mutex_unlock(&h->p->file_mutex);
268
269     zebra_lock_rdwr_rlock(&h->p->rdwr_lock);
270     h->write_flag = 0;
271 #endif
272     return r;
273 }
274
275 int zebra_unlock(ZebraLockHandle h)
276 {
277     int r = 0;
278     yaz_log(log_level, "zebra_unlock fd=%d p=%p fname=%s",
279             h->p->fd, h, h->p->fname);
280 #ifdef WIN32
281     r = _locking(h->p->fd, _LK_UNLCK, 1);
282 #else
283     if (h->write_flag)
284         zebra_lock_rdwr_wunlock(&h->p->rdwr_lock);
285     else
286         zebra_lock_rdwr_runlock(&h->p->rdwr_lock);
287
288     zebra_mutex_lock(&h->p->file_mutex);
289     if (h->write_flag)
290         h->p->no_file_write_lock--;
291     else
292         h->p->no_file_read_lock--;
293     if (h->p->no_file_read_lock == 0 && h->p->no_file_write_lock == 0)
294     {
295         r = unixLock(h->p->fd, F_UNLCK, F_SETLKW);
296     }
297     zebra_mutex_unlock(&h->p->file_mutex);
298 #endif
299     return r;
300 }
301
302 void zebra_flock_init()
303 {
304     if (!initialized)
305     {
306         log_level = yaz_log_module_level("flock");
307         initialized = 1;
308         zebra_mutex_init(&lock_list_mutex);
309     }
310     yaz_log(log_level, "zebra_flock_init");
311 }
312
313 /*
314  * Local variables:
315  * c-basic-offset: 4
316  * indent-tabs-mode: nil
317  * End:
318  * vim: shiftwidth=4 tabstop=8 expandtab
319  */
320