1 /* alock.c - access lock library */
2 /* $OpenLDAP: pkg/ldap/servers/slapd/alock.c,v 1.5.2.7 2008/02/11 23:26:43 kurt Exp $ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2005-2008 The OpenLDAP Foundation.
6 * Portions Copyright 2004-2005 Symas Corporation.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
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>.
18 * This work was initially developed by Matthew Backes at Symas
19 * Corporation for inclusion in OpenLDAP Software.
24 #if SLAPD_BDB || SLAPD_HDB
28 #include <ac/stdlib.h>
29 #include <ac/string.h>
30 #include <ac/unistd.h>
32 #include <ac/assert.h>
33 #include <sys/types.h>
35 #ifdef HAVE_SYS_FILE_H
43 #include <sys/locking.h>
48 alock_grab_lock ( int fd
, int slot
)
52 #if defined( HAVE_LOCKF )
53 res
= lseek (fd
, (off_t
) (ALOCK_SLOT_SIZE
* slot
), SEEK_SET
);
54 if (res
== -1) return -1;
55 res
= lockf (fd
, F_LOCK
, (off_t
) ALOCK_SLOT_SIZE
);
56 #elif defined( HAVE_FCNTL )
57 struct flock lock_info
;
58 (void) memset ((void *) &lock_info
, 0, sizeof (struct flock
));
60 lock_info
.l_type
= F_WRLCK
;
61 lock_info
.l_whence
= SEEK_SET
;
62 lock_info
.l_start
= (off_t
) (ALOCK_SLOT_SIZE
* slot
);
63 lock_info
.l_len
= (off_t
) ALOCK_SLOT_SIZE
;
65 res
= fcntl (fd
, F_SETLKW
, &lock_info
);
66 #elif defined( _WIN32 )
67 if( _lseek( fd
, (ALOCK_SLOT_SIZE
* slot
), SEEK_SET
) < 0 )
70 * _lock will try for the lock once per second, returning EDEADLOCK
71 * after ten tries. We just loop until we either get the lock
72 * or some other error is returned.
74 while((res
= _locking( fd
, _LK_LOCK
, ALOCK_SLOT_SIZE
)) < 0 ) {
75 if( errno
!= EDEADLOCK
)
79 # error alock needs lockf, fcntl, or _locking
82 assert (errno
!= EDEADLK
);
89 alock_release_lock ( int fd
, int slot
)
93 #if defined( HAVE_LOCKF )
94 res
= lseek (fd
, (off_t
) (ALOCK_SLOT_SIZE
* slot
), SEEK_SET
);
95 if (res
== -1) return -1;
96 res
= lockf (fd
, F_ULOCK
, (off_t
) ALOCK_SLOT_SIZE
);
97 if (res
== -1) return -1;
98 #elif defined ( HAVE_FCNTL )
99 struct flock lock_info
;
100 (void) memset ((void *) &lock_info
, 0, sizeof (struct flock
));
102 lock_info
.l_type
= F_UNLCK
;
103 lock_info
.l_whence
= SEEK_SET
;
104 lock_info
.l_start
= (off_t
) (ALOCK_SLOT_SIZE
* slot
);
105 lock_info
.l_len
= (off_t
) ALOCK_SLOT_SIZE
;
107 res
= fcntl (fd
, F_SETLKW
, &lock_info
);
108 if (res
== -1) return -1;
109 #elif defined( _WIN32 )
110 res
= _lseek (fd
, (ALOCK_SLOT_SIZE
* slot
), SEEK_SET
);
111 if (res
== -1) return -1;
112 res
= _locking( fd
, _LK_UNLCK
, ALOCK_SLOT_SIZE
);
113 if (res
== -1) return -1;
115 # error alock needs lockf, fcntl, or _locking
122 alock_test_lock ( int fd
, int slot
)
126 #if defined( HAVE_LOCKF )
127 res
= lseek (fd
, (off_t
) (ALOCK_SLOT_SIZE
* slot
), SEEK_SET
);
128 if (res
== -1) return -1;
130 res
= lockf (fd
, F_TEST
, (off_t
) ALOCK_SLOT_SIZE
);
132 if (errno
== EACCES
|| errno
== EAGAIN
) {
138 #elif defined( HAVE_FCNTL )
139 struct flock lock_info
;
140 (void) memset ((void *) &lock_info
, 0, sizeof (struct flock
));
142 lock_info
.l_type
= F_WRLCK
;
143 lock_info
.l_whence
= SEEK_SET
;
144 lock_info
.l_start
= (off_t
) (ALOCK_SLOT_SIZE
* slot
);
145 lock_info
.l_len
= (off_t
) ALOCK_SLOT_SIZE
;
147 res
= fcntl (fd
, F_GETLK
, &lock_info
);
148 if (res
== -1) return -1;
150 if (lock_info
.l_type
!= F_UNLCK
) return ALOCK_LOCKED
;
151 #elif defined( _WIN32 )
152 res
= _lseek (fd
, (ALOCK_SLOT_SIZE
* slot
), SEEK_SET
);
153 if (res
== -1) return -1;
154 res
= _locking( fd
, _LK_NBLCK
, ALOCK_SLOT_SIZE
);
155 _locking( fd
, _LK_UNLCK
, ALOCK_SLOT_SIZE
);
157 if( errno
== EACCES
) {
164 # error alock needs lockf, fcntl, or _locking
170 /* Read a 64bit LE value */
171 static unsigned long int
172 alock_read_iattr ( unsigned char * bufptr
)
174 unsigned long int val
= 0;
177 assert (bufptr
!= NULL
);
179 bufptr
+= sizeof (unsigned long int);
180 for (count
=0; count
<= sizeof (unsigned long int); ++count
) {
182 val
+= (unsigned long int) *bufptr
--;
188 /* Write a 64bit LE value */
190 alock_write_iattr ( unsigned char * bufptr
,
191 unsigned long int val
)
195 assert (bufptr
!= NULL
);
197 for (count
=0; count
< 8; ++count
) {
198 *bufptr
++ = (unsigned char) (val
& 0xff);
204 alock_read_slot ( alock_info_t
* info
,
205 alock_slot_t
* slot_data
)
207 unsigned char slotbuf
[ALOCK_SLOT_SIZE
];
208 int res
, size
, size_total
, err
;
210 assert (info
!= NULL
);
211 assert (slot_data
!= NULL
);
212 assert (info
->al_slot
> 0);
214 res
= lseek (info
->al_fd
,
215 (off_t
) (ALOCK_SLOT_SIZE
* info
->al_slot
),
217 if (res
== -1) return -1;
220 while (size_total
< ALOCK_SLOT_SIZE
) {
221 size
= read (info
->al_fd
,
222 slotbuf
+ size_total
,
223 ALOCK_SLOT_SIZE
- size_total
);
224 if (size
== 0) return -1;
227 if (err
!= EINTR
&& err
!= EAGAIN
) return -1;
233 if (alock_read_iattr (slotbuf
) != ALOCK_MAGIC
) {
236 slot_data
->al_lock
= alock_read_iattr (slotbuf
+8);
237 slot_data
->al_stamp
= alock_read_iattr (slotbuf
+16);
238 slot_data
->al_pid
= alock_read_iattr (slotbuf
+24);
240 if (slot_data
->al_appname
) free (slot_data
->al_appname
);
241 slot_data
->al_appname
= calloc (1, ALOCK_MAX_APPNAME
);
242 strncpy (slot_data
->al_appname
, (char *)slotbuf
+32, ALOCK_MAX_APPNAME
-1);
243 (slot_data
->al_appname
) [ALOCK_MAX_APPNAME
-1] = '\0';
249 alock_write_slot ( alock_info_t
* info
,
250 alock_slot_t
* slot_data
)
252 unsigned char slotbuf
[ALOCK_SLOT_SIZE
];
253 int res
, size
, size_total
, err
;
255 assert (info
!= NULL
);
256 assert (slot_data
!= NULL
);
257 assert (info
->al_slot
> 0);
259 (void) memset ((void *) slotbuf
, 0, ALOCK_SLOT_SIZE
);
261 alock_write_iattr (slotbuf
, ALOCK_MAGIC
);
262 assert (alock_read_iattr (slotbuf
) == ALOCK_MAGIC
);
263 alock_write_iattr (slotbuf
+8, slot_data
->al_lock
);
264 alock_write_iattr (slotbuf
+16, slot_data
->al_stamp
);
265 alock_write_iattr (slotbuf
+24, slot_data
->al_pid
);
267 if (slot_data
->al_appname
)
268 strncpy ((char *)slotbuf
+32, slot_data
->al_appname
, ALOCK_MAX_APPNAME
-1);
269 slotbuf
[ALOCK_SLOT_SIZE
-1] = '\0';
271 res
= lseek (info
->al_fd
,
272 (off_t
) (ALOCK_SLOT_SIZE
* info
->al_slot
),
274 if (res
== -1) return -1;
277 while (size_total
< ALOCK_SLOT_SIZE
) {
278 size
= write (info
->al_fd
,
279 slotbuf
+ size_total
,
280 ALOCK_SLOT_SIZE
- size_total
);
281 if (size
== 0) return -1;
284 if (err
!= EINTR
&& err
!= EAGAIN
) return -1;
294 alock_query_slot ( alock_info_t
* info
)
297 alock_slot_t slot_data
;
299 assert (info
!= NULL
);
300 assert (info
->al_slot
> 0);
302 (void) memset ((void *) &slot_data
, 0, sizeof (alock_slot_t
));
303 alock_read_slot (info
, &slot_data
);
305 if (slot_data
.al_appname
!= NULL
) free (slot_data
.al_appname
);
306 slot_data
.al_appname
= NULL
;
308 nosave
= slot_data
.al_lock
& ALOCK_NOSAVE
;
310 if ((slot_data
.al_lock
& ALOCK_SMASK
) == ALOCK_UNLOCKED
)
311 return slot_data
.al_lock
;
313 res
= alock_test_lock (info
->al_fd
, info
->al_slot
);
314 if (res
< 0) return -1;
316 if ((slot_data
.al_lock
& ALOCK_SMASK
) == ALOCK_UNIQUE
) {
317 return slot_data
.al_lock
;
319 return ALOCK_LOCKED
| nosave
;
323 return ALOCK_DIRTY
| nosave
;
327 alock_open ( alock_info_t
* info
,
328 const char * appname
,
333 alock_info_t scan_info
;
334 alock_slot_t slot_data
;
337 int dirty_count
, live_count
, nosave
;
339 assert (info
!= NULL
);
340 assert (appname
!= NULL
);
341 assert (envdir
!= NULL
);
342 assert ((locktype
& ALOCK_SMASK
) >= 1 && (locktype
& ALOCK_SMASK
) <= 2);
344 slot_data
.al_lock
= locktype
;
345 slot_data
.al_stamp
= time(NULL
);
346 slot_data
.al_pid
= getpid();
347 slot_data
.al_appname
= calloc (1, ALOCK_MAX_APPNAME
);
348 strncpy (slot_data
.al_appname
, appname
, ALOCK_MAX_APPNAME
-1);
349 slot_data
.al_appname
[ALOCK_MAX_APPNAME
-1] = '\0';
351 filename
= calloc (1, strlen (envdir
) + strlen ("/alock") + 1);
352 strcpy (filename
, envdir
);
353 strcat (filename
, "/alock");
354 info
->al_fd
= open (filename
, O_CREAT
|O_RDWR
, 0666);
356 if (info
->al_fd
< 0) {
357 free (slot_data
.al_appname
);
358 return ALOCK_UNSTABLE
;
362 res
= alock_grab_lock (info
->al_fd
, 0);
365 free (slot_data
.al_appname
);
366 return ALOCK_UNSTABLE
;
369 res
= fstat (info
->al_fd
, &statbuf
);
372 free (slot_data
.al_appname
);
373 return ALOCK_UNSTABLE
;
376 max_slot
= (statbuf
.st_size
+ ALOCK_SLOT_SIZE
- 1) / ALOCK_SLOT_SIZE
;
380 scan_info
.al_fd
= info
->al_fd
;
381 for (scan_info
.al_slot
= 1;
382 scan_info
.al_slot
< max_slot
;
383 ++ scan_info
.al_slot
) {
384 if (scan_info
.al_slot
!= info
->al_slot
) {
385 res
= alock_query_slot (&scan_info
);
387 if (res
& ALOCK_NOSAVE
) {
388 nosave
= ALOCK_NOSAVE
;
391 if (res
== ALOCK_UNLOCKED
392 && info
->al_slot
== 0) {
393 info
->al_slot
= scan_info
.al_slot
;
395 } else if (res
== ALOCK_LOCKED
) {
398 } else if (res
== ALOCK_UNIQUE
399 && locktype
== ALOCK_UNIQUE
) {
401 free (slot_data
.al_appname
);
404 } else if (res
== ALOCK_DIRTY
) {
407 } else if (res
== -1) {
409 free (slot_data
.al_appname
);
410 return ALOCK_UNSTABLE
;
416 if (dirty_count
&& live_count
) {
418 free (slot_data
.al_appname
);
419 return ALOCK_UNSTABLE
;
422 if (info
->al_slot
== 0) info
->al_slot
= max_slot
+ 1;
423 res
= alock_grab_lock (info
->al_fd
,
427 free (slot_data
.al_appname
);
428 return ALOCK_UNSTABLE
;
430 res
= alock_write_slot (info
, &slot_data
);
431 free (slot_data
.al_appname
);
434 return ALOCK_UNSTABLE
;
437 res
= alock_release_lock (info
->al_fd
, 0);
440 return ALOCK_UNSTABLE
;
443 if (dirty_count
) return ALOCK_RECOVER
| nosave
;
444 return ALOCK_CLEAN
| nosave
;
448 alock_scan ( alock_info_t
* info
)
451 alock_info_t scan_info
;
453 int dirty_count
, live_count
, nosave
;
455 assert (info
!= NULL
);
457 scan_info
.al_fd
= info
->al_fd
;
459 res
= alock_grab_lock (info
->al_fd
, 0);
462 return ALOCK_UNSTABLE
;
465 res
= fstat (info
->al_fd
, &statbuf
);
468 return ALOCK_UNSTABLE
;
471 max_slot
= (statbuf
.st_size
+ ALOCK_SLOT_SIZE
- 1) / ALOCK_SLOT_SIZE
;
475 for (scan_info
.al_slot
= 1;
476 scan_info
.al_slot
< max_slot
;
477 ++ scan_info
.al_slot
) {
478 if (scan_info
.al_slot
!= info
->al_slot
) {
479 res
= alock_query_slot (&scan_info
);
481 if (res
& ALOCK_NOSAVE
) {
482 nosave
= ALOCK_NOSAVE
;
486 if (res
== ALOCK_LOCKED
) {
489 } else if (res
== ALOCK_DIRTY
) {
492 } else if (res
== -1) {
494 return ALOCK_UNSTABLE
;
500 res
= alock_release_lock (info
->al_fd
, 0);
503 return ALOCK_UNSTABLE
;
509 return ALOCK_UNSTABLE
;
511 return ALOCK_RECOVER
| nosave
;
515 return ALOCK_CLEAN
| nosave
;
519 alock_close ( alock_info_t
* info
, int nosave
)
521 alock_slot_t slot_data
;
524 if ( !info
->al_slot
)
527 (void) memset ((void *) &slot_data
, 0, sizeof(alock_slot_t
));
529 res
= alock_grab_lock (info
->al_fd
, 0);
532 return ALOCK_UNSTABLE
;
535 /* mark our slot as clean */
536 res
= alock_read_slot (info
, &slot_data
);
539 if (slot_data
.al_appname
!= NULL
)
540 free (slot_data
.al_appname
);
541 return ALOCK_UNSTABLE
;
543 slot_data
.al_lock
= ALOCK_UNLOCKED
;
545 slot_data
.al_lock
|= ALOCK_NOSAVE
;
546 res
= alock_write_slot (info
, &slot_data
);
549 if (slot_data
.al_appname
!= NULL
)
550 free (slot_data
.al_appname
);
551 return ALOCK_UNSTABLE
;
553 if (slot_data
.al_appname
!= NULL
) {
554 free (slot_data
.al_appname
);
555 slot_data
.al_appname
= NULL
;
558 res
= alock_release_lock (info
->al_fd
, info
->al_slot
);
561 return ALOCK_UNSTABLE
;
563 res
= alock_release_lock (info
->al_fd
, 0);
566 return ALOCK_UNSTABLE
;
569 res
= close (info
->al_fd
);
570 if (res
== -1) return ALOCK_UNSTABLE
;
576 alock_recover ( alock_info_t
* info
)
579 alock_slot_t slot_data
;
580 alock_info_t scan_info
;
583 assert (info
!= NULL
);
585 scan_info
.al_fd
= info
->al_fd
;
587 (void) memset ((void *) &slot_data
, 0, sizeof(alock_slot_t
));
589 res
= alock_grab_lock (info
->al_fd
, 0);
592 return ALOCK_UNSTABLE
;
595 res
= fstat (info
->al_fd
, &statbuf
);
598 return ALOCK_UNSTABLE
;
601 max_slot
= (statbuf
.st_size
+ ALOCK_SLOT_SIZE
- 1) / ALOCK_SLOT_SIZE
;
602 for (scan_info
.al_slot
= 1;
603 scan_info
.al_slot
< max_slot
;
604 ++ scan_info
.al_slot
) {
605 if (scan_info
.al_slot
!= info
->al_slot
) {
606 res
= alock_query_slot (&scan_info
) & ~ALOCK_NOSAVE
;
608 if (res
== ALOCK_LOCKED
609 || res
== ALOCK_UNIQUE
) {
610 /* recovery attempt on an active db? */
612 return ALOCK_UNSTABLE
;
614 } else if (res
== ALOCK_DIRTY
) {
616 res
= alock_read_slot (&scan_info
, &slot_data
);
619 return ALOCK_UNSTABLE
;
621 slot_data
.al_lock
= ALOCK_UNLOCKED
;
622 res
= alock_write_slot (&scan_info
, &slot_data
);
625 if (slot_data
.al_appname
!= NULL
)
626 free (slot_data
.al_appname
);
627 return ALOCK_UNSTABLE
;
629 if (slot_data
.al_appname
!= NULL
) {
630 free (slot_data
.al_appname
);
631 slot_data
.al_appname
= NULL
;
634 } else if (res
== -1) {
636 return ALOCK_UNSTABLE
;
642 res
= alock_release_lock (info
->al_fd
, 0);
645 return ALOCK_UNSTABLE
;
651 #endif /* SLAPD_BDB || SLAPD_HDB */