1 /* alock.c - access lock library */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2005-2009 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
29 #include <ac/stdlib.h>
30 #include <ac/string.h>
31 #include <ac/unistd.h>
33 #include <ac/assert.h>
34 #include <sys/types.h>
36 #ifdef HAVE_SYS_FILE_H
44 #include <sys/locking.h>
49 alock_grab_lock ( int fd, int slot )
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));
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;
66 res = fcntl (fd, F_SETLKW, &lock_info);
67 #elif defined( _WIN32 )
68 if( _lseek( fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET ) < 0 )
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.
75 while((res = _locking( fd, _LK_LOCK, ALOCK_SLOT_SIZE )) < 0 ) {
76 if( errno != EDEADLOCK )
80 # error alock needs lockf, fcntl, or _locking
83 assert (errno != EDEADLK);
90 alock_release_lock ( int fd, int slot )
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));
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;
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;
116 # error alock needs lockf, fcntl, or _locking
123 alock_test_lock ( int fd, int slot )
127 #if defined( HAVE_LOCKF )
128 res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
129 if (res == -1) return -1;
131 res = lockf (fd, F_TEST, (off_t) ALOCK_SLOT_SIZE);
133 if (errno == EACCES || errno == EAGAIN) {
139 #elif defined( HAVE_FCNTL )
140 struct flock lock_info;
141 (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
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;
148 res = fcntl (fd, F_GETLK, &lock_info);
149 if (res == -1) return -1;
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 );
158 if( errno == EACCES ) {
165 # error alock needs lockf, fcntl, or _locking
171 /* Read a 64bit LE value */
172 static unsigned long int
173 alock_read_iattr ( unsigned char * bufptr )
175 unsigned long int val = 0;
178 assert (bufptr != NULL);
180 bufptr += sizeof (unsigned long int);
181 for (count=0; count <= (int) sizeof (unsigned long int); ++count) {
183 val += (unsigned long int) *bufptr--;
189 /* Write a 64bit LE value */
191 alock_write_iattr ( unsigned char * bufptr,
192 unsigned long int val )
196 assert (bufptr != NULL);
198 for (count=0; count < 8; ++count) {
199 *bufptr++ = (unsigned char) (val & 0xff);
205 alock_read_slot ( alock_info_t * info,
206 alock_slot_t * slot_data )
208 unsigned char slotbuf [ALOCK_SLOT_SIZE];
209 int res, size, size_total, err;
211 assert (info != NULL);
212 assert (slot_data != NULL);
213 assert (info->al_slot > 0);
215 res = lseek (info->al_fd,
216 (off_t) (ALOCK_SLOT_SIZE * info->al_slot),
218 if (res == -1) return -1;
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;
228 if (err != EINTR && err != EAGAIN) return -1;
234 if (alock_read_iattr (slotbuf) != ALOCK_MAGIC) {
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);
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) {
246 strncpy (slot_data->al_appname, (char *)slotbuf+32, ALOCK_MAX_APPNAME-1);
247 (slot_data->al_appname) [ALOCK_MAX_APPNAME-1] = '\0';
253 alock_write_slot ( alock_info_t * info,
254 alock_slot_t * slot_data )
256 unsigned char slotbuf [ALOCK_SLOT_SIZE];
257 int res, size, size_total, err;
259 assert (info != NULL);
260 assert (slot_data != NULL);
261 assert (info->al_slot > 0);
263 (void) memset ((void *) slotbuf, 0, ALOCK_SLOT_SIZE);
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);
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';
275 res = lseek (info->al_fd,
276 (off_t) (ALOCK_SLOT_SIZE * info->al_slot),
278 if (res == -1) return -1;
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;
288 if (err != EINTR && err != EAGAIN) return -1;
298 alock_query_slot ( alock_info_t * info )
301 alock_slot_t slot_data;
303 assert (info != NULL);
304 assert (info->al_slot > 0);
306 (void) memset ((void *) &slot_data, 0, sizeof (alock_slot_t));
307 alock_read_slot (info, &slot_data);
309 if (slot_data.al_appname != NULL) free (slot_data.al_appname);
310 slot_data.al_appname = NULL;
312 nosave = slot_data.al_lock & ALOCK_NOSAVE;
314 if ((slot_data.al_lock & ALOCK_SMASK) == ALOCK_UNLOCKED)
315 return slot_data.al_lock;
317 res = alock_test_lock (info->al_fd, info->al_slot);
318 if (res < 0) return -1;
320 if ((slot_data.al_lock & ALOCK_SMASK) == ALOCK_UNIQUE) {
321 return slot_data.al_lock;
323 return ALOCK_LOCKED | nosave;
327 return ALOCK_DIRTY | nosave;
331 alock_open ( alock_info_t * info,
332 const char * appname,
337 alock_info_t scan_info;
338 alock_slot_t slot_data;
341 int dirty_count, live_count, nosave;
344 assert (info != NULL);
345 assert (appname != NULL);
346 assert (envdir != NULL);
347 assert ((locktype & ALOCK_SMASK) >= 1 && (locktype & ALOCK_SMASK) <= 2);
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;
356 strncpy (slot_data.al_appname, appname, ALOCK_MAX_APPNAME-1);
357 slot_data.al_appname [ALOCK_MAX_APPNAME-1] = '\0';
359 filename = calloc (1, strlen (envdir) + strlen ("/alock") + 1);
360 if (filename == NULL ) {
361 free (slot_data.al_appname);
362 return ALOCK_UNSTABLE;
364 ptr = lutil_strcopy(filename, envdir);
365 lutil_strcopy(ptr, "/alock");
366 info->al_fd = open (filename, O_CREAT|O_RDWR, 0666);
368 if (info->al_fd < 0) {
369 free (slot_data.al_appname);
370 return ALOCK_UNSTABLE;
374 res = alock_grab_lock (info->al_fd, 0);
377 free (slot_data.al_appname);
378 return ALOCK_UNSTABLE;
381 res = fstat (info->al_fd, &statbuf);
384 free (slot_data.al_appname);
385 return ALOCK_UNSTABLE;
388 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
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);
399 if (res & ALOCK_NOSAVE) {
400 nosave = ALOCK_NOSAVE;
403 if (res == ALOCK_UNLOCKED
404 && info->al_slot == 0) {
405 info->al_slot = scan_info.al_slot;
407 } else if (res == ALOCK_LOCKED) {
410 } else if (res == ALOCK_UNIQUE
411 && locktype == ALOCK_UNIQUE) {
413 free (slot_data.al_appname);
416 } else if (res == ALOCK_DIRTY) {
419 } else if (res == -1) {
421 free (slot_data.al_appname);
422 return ALOCK_UNSTABLE;
428 if (dirty_count && live_count) {
430 free (slot_data.al_appname);
431 return ALOCK_UNSTABLE;
434 if (info->al_slot == 0) info->al_slot = max_slot + 1;
435 res = alock_grab_lock (info->al_fd,
439 free (slot_data.al_appname);
440 return ALOCK_UNSTABLE;
442 res = alock_write_slot (info, &slot_data);
443 free (slot_data.al_appname);
446 return ALOCK_UNSTABLE;
449 res = alock_release_lock (info->al_fd, 0);
452 return ALOCK_UNSTABLE;
455 if (dirty_count) return ALOCK_RECOVER | nosave;
456 return ALOCK_CLEAN | nosave;
460 alock_scan ( alock_info_t * info )
463 alock_info_t scan_info;
465 int dirty_count, live_count, nosave;
467 assert (info != NULL);
469 scan_info.al_fd = info->al_fd;
471 res = alock_grab_lock (info->al_fd, 0);
474 return ALOCK_UNSTABLE;
477 res = fstat (info->al_fd, &statbuf);
480 return ALOCK_UNSTABLE;
483 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
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);
493 if (res & ALOCK_NOSAVE) {
494 nosave = ALOCK_NOSAVE;
498 if (res == ALOCK_LOCKED) {
501 } else if (res == ALOCK_DIRTY) {
504 } else if (res == -1) {
506 return ALOCK_UNSTABLE;
512 res = alock_release_lock (info->al_fd, 0);
515 return ALOCK_UNSTABLE;
521 return ALOCK_UNSTABLE;
523 return ALOCK_RECOVER | nosave;
527 return ALOCK_CLEAN | nosave;
531 alock_close ( alock_info_t * info, int nosave )
533 alock_slot_t slot_data;
536 if ( !info->al_slot )
539 (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
541 res = alock_grab_lock (info->al_fd, 0);
544 return ALOCK_UNSTABLE;
547 /* mark our slot as clean */
548 res = alock_read_slot (info, &slot_data);
551 if (slot_data.al_appname != NULL)
552 free (slot_data.al_appname);
553 return ALOCK_UNSTABLE;
555 slot_data.al_lock = ALOCK_UNLOCKED;
557 slot_data.al_lock |= ALOCK_NOSAVE;
558 res = alock_write_slot (info, &slot_data);
561 if (slot_data.al_appname != NULL)
562 free (slot_data.al_appname);
563 return ALOCK_UNSTABLE;
565 if (slot_data.al_appname != NULL) {
566 free (slot_data.al_appname);
567 slot_data.al_appname = NULL;
570 res = alock_release_lock (info->al_fd, info->al_slot);
573 return ALOCK_UNSTABLE;
575 res = alock_release_lock (info->al_fd, 0);
578 return ALOCK_UNSTABLE;
581 res = close (info->al_fd);
582 if (res == -1) return ALOCK_UNSTABLE;
588 alock_recover ( alock_info_t * info )
591 alock_slot_t slot_data;
592 alock_info_t scan_info;
595 assert (info != NULL);
597 scan_info.al_fd = info->al_fd;
599 (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
601 res = alock_grab_lock (info->al_fd, 0);
604 return ALOCK_UNSTABLE;
607 res = fstat (info->al_fd, &statbuf);
610 return ALOCK_UNSTABLE;
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;
620 if (res == ALOCK_LOCKED
621 || res == ALOCK_UNIQUE) {
622 /* recovery attempt on an active db? */
624 return ALOCK_UNSTABLE;
626 } else if (res == ALOCK_DIRTY) {
628 res = alock_read_slot (&scan_info, &slot_data);
631 return ALOCK_UNSTABLE;
633 slot_data.al_lock = ALOCK_UNLOCKED;
634 res = alock_write_slot (&scan_info, &slot_data);
637 if (slot_data.al_appname != NULL)
638 free (slot_data.al_appname);
639 return ALOCK_UNSTABLE;
641 if (slot_data.al_appname != NULL) {
642 free (slot_data.al_appname);
643 slot_data.al_appname = NULL;
646 } else if (res == -1) {
648 return ALOCK_UNSTABLE;
654 res = alock_release_lock (info->al_fd, 0);
657 return ALOCK_UNSTABLE;
663 #endif /* SLAPD_BDB || SLAPD_HDB */