]> git.sur5r.net Git - openldap/blob - servers/slapd/alock.c
19fbe4ad3548c4fd5a95181bb4ba867e261cbbfa
[openldap] / servers / slapd / alock.c
1 /* alock.c - access lock library */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2005-2009 The OpenLDAP Foundation.
6  * Portions Copyright 2004-2005 Symas Corporation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by Matthew Backes at Symas
19  * Corporation for inclusion in OpenLDAP Software.
20  */
21
22 #include "portable.h"
23
24 #if SLAPD_BDB || SLAPD_HDB
25
26 #include "alock.h"
27 #include "lutil.h"
28
29 #include <ac/stdlib.h>
30 #include <ac/string.h>
31 #include <ac/unistd.h>
32 #include <ac/errno.h>
33 #include <ac/assert.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #ifdef HAVE_SYS_FILE_H
37 #include <sys/file.h>
38 #endif
39 #include <fcntl.h>
40
41 #ifdef _WIN32
42 #include <stdio.h>
43 #include <io.h>
44 #include <sys/locking.h>
45 #endif
46
47
48 static int
49 alock_grab_lock ( int fd, int slot )
50 {
51         int res;
52         
53 #if defined( HAVE_LOCKF )
54         res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
55         if (res == -1) return -1;
56         res = lockf (fd, F_LOCK, (off_t) ALOCK_SLOT_SIZE);
57 #elif defined( HAVE_FCNTL )
58         struct flock lock_info;
59         (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
60
61         lock_info.l_type = F_WRLCK;
62         lock_info.l_whence = SEEK_SET;
63         lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
64         lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
65
66         res = fcntl (fd, F_SETLKW, &lock_info);
67 #elif defined( _WIN32 )
68         if( _lseek( fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET ) < 0 )
69                 return -1;
70         /*
71          * _lock will try for the lock once per second, returning EDEADLOCK
72          * after ten tries. We just loop until we either get the lock
73          * or some other error is returned.
74          */
75         while((res = _locking( fd, _LK_LOCK, ALOCK_SLOT_SIZE )) < 0 ) {
76                 if( errno != EDEADLOCK )
77                         break;
78         }
79 #else
80 #   error alock needs lockf, fcntl, or _locking
81 #endif
82         if (res == -1) {
83                 assert (errno != EDEADLK);
84                 return -1;
85         }
86         return 0;
87 }
88
89 static int
90 alock_release_lock ( int fd, int slot )
91 {
92         int res;
93         
94 #if defined( HAVE_LOCKF )
95         res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
96         if (res == -1) return -1;
97         res = lockf (fd, F_ULOCK, (off_t) ALOCK_SLOT_SIZE);
98         if (res == -1) return -1;
99 #elif defined ( HAVE_FCNTL )
100         struct flock lock_info;
101         (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
102
103         lock_info.l_type = F_UNLCK;
104         lock_info.l_whence = SEEK_SET;
105         lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
106         lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
107
108         res = fcntl (fd, F_SETLKW, &lock_info);
109         if (res == -1) return -1;
110 #elif defined( _WIN32 )
111         res = _lseek (fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET);
112         if (res == -1) return -1;
113         res = _locking( fd, _LK_UNLCK, ALOCK_SLOT_SIZE );
114         if (res == -1) return -1;
115 #else
116 #   error alock needs lockf, fcntl, or _locking
117 #endif
118
119         return 0;
120 }
121
122 static int
123 alock_test_lock ( int fd, int slot )
124 {
125         int res;
126
127 #if defined( HAVE_LOCKF )
128         res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
129         if (res == -1) return -1;
130
131         res = lockf (fd, F_TEST, (off_t) ALOCK_SLOT_SIZE);
132         if (res == -1) {
133                 if (errno == EACCES || errno == EAGAIN) { 
134                         return ALOCK_LOCKED;
135                 } else {
136                         return -1;
137                 }
138         }
139 #elif defined( HAVE_FCNTL )
140         struct flock lock_info;
141         (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
142
143         lock_info.l_type = F_WRLCK;
144         lock_info.l_whence = SEEK_SET;
145         lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
146         lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
147
148         res = fcntl (fd, F_GETLK, &lock_info);
149         if (res == -1) return -1;
150
151         if (lock_info.l_type != F_UNLCK) return ALOCK_LOCKED;
152 #elif defined( _WIN32 )
153         res = _lseek (fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET);
154         if (res == -1) return -1;
155         res = _locking( fd, _LK_NBLCK, ALOCK_SLOT_SIZE );
156         _locking( fd, _LK_UNLCK, ALOCK_SLOT_SIZE );
157         if (res == -1) {
158            if( errno == EACCES ) {
159                    return ALOCK_LOCKED;
160            } else {
161                    return -1;
162            }
163         }
164 #else
165 #   error alock needs lockf, fcntl, or _locking
166 #endif
167         
168         return 0;
169 }
170
171 /* Read a 64bit LE value */
172 static unsigned long int
173 alock_read_iattr ( unsigned char * bufptr )
174 {
175         unsigned long int val = 0;
176         int count;
177
178         assert (bufptr != NULL);
179
180         bufptr += sizeof (unsigned long int);
181         for (count=0; count <= (int) sizeof (unsigned long int); ++count) {
182                 val <<= 8;
183                 val += (unsigned long int) *bufptr--;
184         }
185
186         return val;
187 }
188
189 /* Write a 64bit LE value */
190 static void
191 alock_write_iattr ( unsigned char * bufptr,
192                     unsigned long int val )
193 {
194         int count;
195
196         assert (bufptr != NULL);
197
198         for (count=0; count < 8; ++count) {
199                 *bufptr++ = (unsigned char) (val & 0xff);
200                 val >>= 8;
201         }
202 }
203
204 static int
205 alock_read_slot ( alock_info_t * info,
206                   alock_slot_t * slot_data )
207 {
208         unsigned char slotbuf [ALOCK_SLOT_SIZE];
209         int res, size, size_total, err;
210
211         assert (info != NULL);
212         assert (slot_data != NULL);
213         assert (info->al_slot > 0);
214
215         res = lseek (info->al_fd, 
216                      (off_t) (ALOCK_SLOT_SIZE * info->al_slot), 
217                      SEEK_SET);
218         if (res == -1) return -1;
219
220         size_total = 0;
221         while (size_total < ALOCK_SLOT_SIZE) {
222                 size = read (info->al_fd, 
223                              slotbuf + size_total, 
224                              ALOCK_SLOT_SIZE - size_total);
225                 if (size == 0) return -1;
226                 if (size < 0) {
227                         err = errno;
228                         if (err != EINTR && err != EAGAIN) return -1;
229                 } else {
230                         size_total += size;
231                 }
232         }
233         
234         if (alock_read_iattr (slotbuf) != ALOCK_MAGIC) {
235                 return -1;
236         }
237         slot_data->al_lock  = alock_read_iattr (slotbuf+8);
238         slot_data->al_stamp = alock_read_iattr (slotbuf+16);
239         slot_data->al_pid   = alock_read_iattr (slotbuf+24);
240
241         if (slot_data->al_appname) free (slot_data->al_appname);
242         slot_data->al_appname = calloc (1, ALOCK_MAX_APPNAME);
243         if (slot_data->al_appname == NULL) {
244                 return -1;
245         }
246         strncpy (slot_data->al_appname, (char *)slotbuf+32, ALOCK_MAX_APPNAME-1);
247         (slot_data->al_appname) [ALOCK_MAX_APPNAME-1] = '\0';
248
249         return 0;
250 }
251
252 static int
253 alock_write_slot ( alock_info_t * info,
254                    alock_slot_t * slot_data )
255 {
256         unsigned char slotbuf [ALOCK_SLOT_SIZE];
257         int res, size, size_total, err;
258
259         assert (info != NULL);
260         assert (slot_data != NULL);
261         assert (info->al_slot > 0);
262
263         (void) memset ((void *) slotbuf, 0, ALOCK_SLOT_SIZE);
264         
265         alock_write_iattr (slotbuf,    ALOCK_MAGIC);
266         assert (alock_read_iattr (slotbuf) == ALOCK_MAGIC);
267         alock_write_iattr (slotbuf+8,  slot_data->al_lock);
268         alock_write_iattr (slotbuf+16, slot_data->al_stamp);
269         alock_write_iattr (slotbuf+24, slot_data->al_pid);
270
271         if (slot_data->al_appname)
272                 strncpy ((char *)slotbuf+32, slot_data->al_appname, ALOCK_MAX_APPNAME-1);
273         slotbuf[ALOCK_SLOT_SIZE-1] = '\0';
274
275         res = lseek (info->al_fd, 
276                      (off_t) (ALOCK_SLOT_SIZE * info->al_slot),
277                      SEEK_SET);
278         if (res == -1) return -1;
279
280         size_total = 0;
281         while (size_total < ALOCK_SLOT_SIZE) {
282                 size = write (info->al_fd, 
283                               slotbuf + size_total, 
284                               ALOCK_SLOT_SIZE - size_total);
285                 if (size == 0) return -1;
286                 if (size < 0) {
287                         err = errno;
288                         if (err != EINTR && err != EAGAIN) return -1;
289                 } else {
290                         size_total += size;
291                 }
292         }
293         
294         return 0;
295 }
296
297 static int
298 alock_query_slot ( alock_info_t * info )
299 {
300         int res, nosave;
301         alock_slot_t slot_data;
302
303         assert (info != NULL);
304         assert (info->al_slot > 0);
305         
306         (void) memset ((void *) &slot_data, 0, sizeof (alock_slot_t));
307         alock_read_slot (info, &slot_data);
308
309         if (slot_data.al_appname != NULL) free (slot_data.al_appname);
310         slot_data.al_appname = NULL;
311
312         nosave = slot_data.al_lock & ALOCK_NOSAVE;
313
314         if ((slot_data.al_lock & ALOCK_SMASK) == ALOCK_UNLOCKED)
315                 return slot_data.al_lock;
316
317         res = alock_test_lock (info->al_fd, info->al_slot);
318         if (res < 0) return -1;
319         if (res > 0) {
320                 if ((slot_data.al_lock & ALOCK_SMASK) == ALOCK_UNIQUE) {
321                         return slot_data.al_lock;
322                 } else {
323                         return ALOCK_LOCKED | nosave;
324                 }
325         }
326         
327         return ALOCK_DIRTY | nosave;
328 }
329
330 int 
331 alock_open ( alock_info_t * info,
332              const char * appname,
333              const char * envdir,
334              int locktype )
335 {
336         struct stat statbuf;
337         alock_info_t scan_info;
338         alock_slot_t slot_data;
339         char * filename;
340         int res, max_slot;
341         int dirty_count, live_count, nosave;
342         char *ptr;
343
344         assert (info != NULL);
345         assert (appname != NULL);
346         assert (envdir != NULL);
347         assert ((locktype & ALOCK_SMASK) >= 1 && (locktype & ALOCK_SMASK) <= 2);
348
349         slot_data.al_lock = locktype;
350         slot_data.al_stamp = time(NULL);
351         slot_data.al_pid = getpid();
352         slot_data.al_appname = calloc (1, ALOCK_MAX_APPNAME);
353         if (slot_data.al_appname == NULL) {
354                 return ALOCK_UNSTABLE;
355         }
356         strncpy (slot_data.al_appname, appname, ALOCK_MAX_APPNAME-1);
357         slot_data.al_appname [ALOCK_MAX_APPNAME-1] = '\0';
358
359         filename = calloc (1, strlen (envdir) + strlen ("/alock") + 1);
360         if (filename == NULL ) {
361                 free (slot_data.al_appname);
362                 return ALOCK_UNSTABLE;
363         }
364         ptr = lutil_strcopy(filename, envdir);
365         lutil_strcopy(ptr, "/alock");
366         info->al_fd = open (filename, O_CREAT|O_RDWR, 0666);
367         free (filename);
368         if (info->al_fd < 0) {
369                 free (slot_data.al_appname);
370                 return ALOCK_UNSTABLE;
371         }
372         info->al_slot = 0;
373
374         res = alock_grab_lock (info->al_fd, 0);
375         if (res == -1) { 
376                 close (info->al_fd);
377                 free (slot_data.al_appname);
378                 return ALOCK_UNSTABLE;
379         }
380
381         res = fstat (info->al_fd, &statbuf);
382         if (res == -1) { 
383                 close (info->al_fd);
384                 free (slot_data.al_appname);
385                 return ALOCK_UNSTABLE;
386         }
387
388         max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
389         dirty_count = 0;
390         live_count = 0;
391         nosave = 0;
392         scan_info.al_fd = info->al_fd;
393         for (scan_info.al_slot = 1; 
394              scan_info.al_slot < max_slot;
395              ++ scan_info.al_slot) {
396                 if (scan_info.al_slot != info->al_slot) {
397                         res = alock_query_slot (&scan_info);
398
399                         if (res & ALOCK_NOSAVE) {
400                                 nosave = ALOCK_NOSAVE;
401                                 res ^= ALOCK_NOSAVE;
402                         }
403                         if (res == ALOCK_UNLOCKED
404                             && info->al_slot == 0) {
405                                 info->al_slot = scan_info.al_slot;
406
407                         } else if (res == ALOCK_LOCKED) {
408                                 ++live_count;
409
410                         } else if (res == ALOCK_UNIQUE
411                                 && locktype == ALOCK_UNIQUE) {
412                                 close (info->al_fd);
413                                 free (slot_data.al_appname);
414                                 return ALOCK_BUSY;
415
416                         } else if (res == ALOCK_DIRTY) {
417                                 ++dirty_count;
418
419                         } else if (res == -1) {
420                                 close (info->al_fd);
421                                 free (slot_data.al_appname);
422                                 return ALOCK_UNSTABLE;
423
424                         }
425                 }
426         }
427
428         if (dirty_count && live_count) {
429                 close (info->al_fd);
430                 free (slot_data.al_appname);
431                 return ALOCK_UNSTABLE;
432         }
433         
434         if (info->al_slot == 0) info->al_slot = max_slot + 1;
435         res = alock_grab_lock (info->al_fd,
436                                info->al_slot);
437         if (res == -1) { 
438                 close (info->al_fd);
439                 free (slot_data.al_appname);
440                 return ALOCK_UNSTABLE;
441         }
442         res = alock_write_slot (info, &slot_data);
443         free (slot_data.al_appname);
444         if (res == -1) { 
445                 close (info->al_fd);
446                 return ALOCK_UNSTABLE;
447         }
448
449         res = alock_release_lock (info->al_fd, 0);
450         if (res == -1) { 
451                 close (info->al_fd);
452                 return ALOCK_UNSTABLE;
453         }
454         
455         if (dirty_count) return ALOCK_RECOVER | nosave;
456         return ALOCK_CLEAN | nosave;
457 }
458
459 int 
460 alock_scan ( alock_info_t * info )
461 {
462         struct stat statbuf;
463         alock_info_t scan_info;
464         int res, max_slot;
465         int dirty_count, live_count, nosave;
466
467         assert (info != NULL);
468
469         scan_info.al_fd = info->al_fd;
470
471         res = alock_grab_lock (info->al_fd, 0);
472         if (res == -1) {
473                 close (info->al_fd);
474                 return ALOCK_UNSTABLE;
475         }
476
477         res = fstat (info->al_fd, &statbuf);
478         if (res == -1) {
479                 close (info->al_fd);
480                 return ALOCK_UNSTABLE;
481         }
482
483         max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
484         dirty_count = 0;
485         live_count = 0;
486         nosave = 0;
487         for (scan_info.al_slot = 1; 
488              scan_info.al_slot < max_slot;
489              ++ scan_info.al_slot) {
490                 if (scan_info.al_slot != info->al_slot) {
491                         res = alock_query_slot (&scan_info);
492
493                         if (res & ALOCK_NOSAVE) {
494                                 nosave = ALOCK_NOSAVE;
495                                 res ^= ALOCK_NOSAVE;
496                         }
497
498                         if (res == ALOCK_LOCKED) {
499                                 ++live_count;
500                                 
501                         } else if (res == ALOCK_DIRTY) {
502                                 ++dirty_count;
503
504                         } else if (res == -1) {
505                                 close (info->al_fd);
506                                 return ALOCK_UNSTABLE;
507
508                         }
509                 }
510         }
511
512         res = alock_release_lock (info->al_fd, 0);
513         if (res == -1) {
514                 close (info->al_fd);
515                 return ALOCK_UNSTABLE;
516         }
517
518         if (dirty_count) {
519                 if (live_count) {
520                         close (info->al_fd);
521                         return ALOCK_UNSTABLE;
522                 } else {
523                         return ALOCK_RECOVER | nosave;
524                 }
525         }
526         
527         return ALOCK_CLEAN | nosave;
528 }
529
530 int
531 alock_close ( alock_info_t * info, int nosave )
532 {
533         alock_slot_t slot_data;
534         int res;
535
536         if ( !info->al_slot )
537                 return ALOCK_CLEAN;
538
539         (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
540
541         res = alock_grab_lock (info->al_fd, 0);
542         if (res == -1) {
543                 close (info->al_fd);
544                 return ALOCK_UNSTABLE;
545         }
546
547         /* mark our slot as clean */
548         res = alock_read_slot (info, &slot_data);
549         if (res == -1) {
550                 close (info->al_fd);
551                 if (slot_data.al_appname != NULL) 
552                         free (slot_data.al_appname);
553                 return ALOCK_UNSTABLE;
554         }
555         slot_data.al_lock = ALOCK_UNLOCKED;
556         if ( nosave )
557                 slot_data.al_lock |= ALOCK_NOSAVE;
558         res = alock_write_slot (info, &slot_data);
559         if (res == -1) {
560                 close (info->al_fd);
561                 if (slot_data.al_appname != NULL) 
562                         free (slot_data.al_appname);
563                 return ALOCK_UNSTABLE;
564         }
565         if (slot_data.al_appname != NULL) {
566                 free (slot_data.al_appname);
567                 slot_data.al_appname = NULL;
568         }
569
570         res = alock_release_lock (info->al_fd, info->al_slot);
571         if (res == -1) {
572                 close (info->al_fd);
573                 return ALOCK_UNSTABLE;
574         }
575         res = alock_release_lock (info->al_fd, 0);
576         if (res == -1) {
577                 close (info->al_fd);
578                 return ALOCK_UNSTABLE;
579         }
580
581         res = close (info->al_fd);
582         if (res == -1) return ALOCK_UNSTABLE;
583         
584         return ALOCK_CLEAN;
585 }
586
587 int 
588 alock_recover ( alock_info_t * info )
589 {
590         struct stat statbuf;
591         alock_slot_t slot_data;
592         alock_info_t scan_info;
593         int res, max_slot;
594
595         assert (info != NULL);
596
597         scan_info.al_fd = info->al_fd;
598
599         (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
600
601         res = alock_grab_lock (info->al_fd, 0);
602         if (res == -1) {
603                 close (info->al_fd);
604                 return ALOCK_UNSTABLE;
605         }
606
607         res = fstat (info->al_fd, &statbuf);
608         if (res == -1) {
609                 close (info->al_fd);
610                 return ALOCK_UNSTABLE;
611         }
612
613         max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
614         for (scan_info.al_slot = 1; 
615              scan_info.al_slot < max_slot;
616              ++ scan_info.al_slot) {
617                 if (scan_info.al_slot != info->al_slot) {
618                         res = alock_query_slot (&scan_info) & ~ALOCK_NOSAVE;
619
620                         if (res == ALOCK_LOCKED
621                             || res == ALOCK_UNIQUE) {
622                                 /* recovery attempt on an active db? */
623                                 close (info->al_fd);
624                                 return ALOCK_UNSTABLE;
625                                 
626                         } else if (res == ALOCK_DIRTY) {
627                                 /* mark it clean */
628                                 res = alock_read_slot (&scan_info, &slot_data);
629                                 if (res == -1) {
630                                         close (info->al_fd);
631                                         return ALOCK_UNSTABLE;
632                                 }
633                                 slot_data.al_lock = ALOCK_UNLOCKED;
634                                 res = alock_write_slot (&scan_info, &slot_data);
635                                 if (res == -1) {
636                                         close (info->al_fd);
637                                         if (slot_data.al_appname != NULL) 
638                                                 free (slot_data.al_appname);
639                                         return ALOCK_UNSTABLE;
640                                 }
641                                 if (slot_data.al_appname != NULL) {
642                                         free (slot_data.al_appname);
643                                         slot_data.al_appname = NULL;
644                                 }
645                                 
646                         } else if (res == -1) {
647                                 close (info->al_fd);
648                                 return ALOCK_UNSTABLE;
649
650                         }
651                 }
652         }
653
654         res = alock_release_lock (info->al_fd, 0);
655         if (res == -1) {
656                 close (info->al_fd);
657                 return ALOCK_UNSTABLE;
658         }
659
660         return ALOCK_CLEAN;
661 }
662
663 #endif /* SLAPD_BDB || SLAPD_HDB */