1 /* alock.c - access lock library */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2005-2010 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
30 #include <ac/stdlib.h>
31 #include <ac/string.h>
32 #include <ac/unistd.h>
34 #include <ac/assert.h>
35 #include <sys/types.h>
37 #ifdef HAVE_SYS_FILE_H
45 #include <sys/locking.h>
50 alock_grab_lock ( int fd, int slot )
54 #if defined( HAVE_LOCKF )
55 res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
56 if (res == -1) return -1;
57 res = lockf (fd, F_LOCK, (off_t) ALOCK_SLOT_SIZE);
58 #elif defined( HAVE_FCNTL )
59 struct flock lock_info;
60 (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
62 lock_info.l_type = F_WRLCK;
63 lock_info.l_whence = SEEK_SET;
64 lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
65 lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
67 res = fcntl (fd, F_SETLKW, &lock_info);
68 #elif defined( _WIN32 )
69 if( _lseek( fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET ) < 0 )
72 * _lock will try for the lock once per second, returning EDEADLOCK
73 * after ten tries. We just loop until we either get the lock
74 * or some other error is returned.
76 while((res = _locking( fd, _LK_LOCK, ALOCK_SLOT_SIZE )) < 0 ) {
77 if( errno != EDEADLOCK )
81 # error alock needs lockf, fcntl, or _locking
84 assert (errno != EDEADLK);
91 alock_release_lock ( int fd, int slot )
95 #if defined( HAVE_LOCKF )
96 res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
97 if (res == -1) return -1;
98 res = lockf (fd, F_ULOCK, (off_t) ALOCK_SLOT_SIZE);
99 if (res == -1) return -1;
100 #elif defined ( HAVE_FCNTL )
101 struct flock lock_info;
102 (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
104 lock_info.l_type = F_UNLCK;
105 lock_info.l_whence = SEEK_SET;
106 lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
107 lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
109 res = fcntl (fd, F_SETLKW, &lock_info);
110 if (res == -1) return -1;
111 #elif defined( _WIN32 )
112 res = _lseek (fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET);
113 if (res == -1) return -1;
114 res = _locking( fd, _LK_UNLCK, ALOCK_SLOT_SIZE );
115 if (res == -1) return -1;
117 # error alock needs lockf, fcntl, or _locking
124 alock_test_lock ( int fd, int slot )
128 #if defined( HAVE_LOCKF )
129 res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
130 if (res == -1) return -1;
132 res = lockf (fd, F_TEST, (off_t) ALOCK_SLOT_SIZE);
134 if (errno == EACCES || errno == EAGAIN) {
140 #elif defined( HAVE_FCNTL )
141 struct flock lock_info;
142 (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
144 lock_info.l_type = F_WRLCK;
145 lock_info.l_whence = SEEK_SET;
146 lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
147 lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
149 res = fcntl (fd, F_GETLK, &lock_info);
150 if (res == -1) return -1;
152 if (lock_info.l_type != F_UNLCK) return ALOCK_LOCKED;
153 #elif defined( _WIN32 )
154 res = _lseek (fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET);
155 if (res == -1) return -1;
156 res = _locking( fd, _LK_NBLCK, ALOCK_SLOT_SIZE );
157 _locking( fd, _LK_UNLCK, ALOCK_SLOT_SIZE );
159 if( errno == EACCES ) {
166 # error alock needs lockf, fcntl, or _locking
172 /* Read a 64bit LE value */
173 static unsigned long int
174 alock_read_iattr ( unsigned char * bufptr )
176 unsigned long int val = 0;
179 assert (bufptr != NULL);
181 bufptr += sizeof (unsigned long int);
182 for (count=0; count <= (int) sizeof (unsigned long int); ++count) {
184 val += (unsigned long int) *bufptr--;
190 /* Write a 64bit LE value */
192 alock_write_iattr ( unsigned char * bufptr,
193 unsigned long int val )
197 assert (bufptr != NULL);
199 for (count=0; count < 8; ++count) {
200 *bufptr++ = (unsigned char) (val & 0xff);
206 alock_read_slot ( alock_info_t * info,
207 alock_slot_t * slot_data )
209 unsigned char slotbuf [ALOCK_SLOT_SIZE];
210 int res, size, size_total, err;
212 assert (info != NULL);
213 assert (slot_data != NULL);
214 assert (info->al_slot > 0);
216 res = lseek (info->al_fd,
217 (off_t) (ALOCK_SLOT_SIZE * info->al_slot),
219 if (res == -1) return -1;
222 while (size_total < ALOCK_SLOT_SIZE) {
223 size = read (info->al_fd,
224 slotbuf + size_total,
225 ALOCK_SLOT_SIZE - size_total);
226 if (size == 0) return -1;
229 if (err != EINTR && err != EAGAIN) return -1;
235 if (alock_read_iattr (slotbuf) != ALOCK_MAGIC) {
238 slot_data->al_lock = alock_read_iattr (slotbuf+8);
239 slot_data->al_stamp = alock_read_iattr (slotbuf+16);
240 slot_data->al_pid = alock_read_iattr (slotbuf+24);
242 if (slot_data->al_appname) ber_memfree (slot_data->al_appname);
243 slot_data->al_appname = ber_memcalloc (1, ALOCK_MAX_APPNAME);
244 if (slot_data->al_appname == NULL) {
247 strncpy (slot_data->al_appname, (char *)slotbuf+32, ALOCK_MAX_APPNAME-1);
248 (slot_data->al_appname) [ALOCK_MAX_APPNAME-1] = '\0';
254 alock_write_slot ( alock_info_t * info,
255 alock_slot_t * slot_data )
257 unsigned char slotbuf [ALOCK_SLOT_SIZE];
258 int res, size, size_total, err;
260 assert (info != NULL);
261 assert (slot_data != NULL);
262 assert (info->al_slot > 0);
264 (void) memset ((void *) slotbuf, 0, ALOCK_SLOT_SIZE);
266 alock_write_iattr (slotbuf, ALOCK_MAGIC);
267 assert (alock_read_iattr (slotbuf) == ALOCK_MAGIC);
268 alock_write_iattr (slotbuf+8, slot_data->al_lock);
269 alock_write_iattr (slotbuf+16, slot_data->al_stamp);
270 alock_write_iattr (slotbuf+24, slot_data->al_pid);
272 if (slot_data->al_appname)
273 strncpy ((char *)slotbuf+32, slot_data->al_appname, ALOCK_MAX_APPNAME-1);
274 slotbuf[ALOCK_SLOT_SIZE-1] = '\0';
276 res = lseek (info->al_fd,
277 (off_t) (ALOCK_SLOT_SIZE * info->al_slot),
279 if (res == -1) return -1;
282 while (size_total < ALOCK_SLOT_SIZE) {
283 size = write (info->al_fd,
284 slotbuf + size_total,
285 ALOCK_SLOT_SIZE - size_total);
286 if (size == 0) return -1;
289 if (err != EINTR && err != EAGAIN) return -1;
299 alock_query_slot ( alock_info_t * info )
302 alock_slot_t slot_data;
304 assert (info != NULL);
305 assert (info->al_slot > 0);
307 (void) memset ((void *) &slot_data, 0, sizeof (alock_slot_t));
308 alock_read_slot (info, &slot_data);
310 if (slot_data.al_appname != NULL) ber_memfree (slot_data.al_appname);
311 slot_data.al_appname = NULL;
313 nosave = slot_data.al_lock & ALOCK_NOSAVE;
315 if ((slot_data.al_lock & ALOCK_SMASK) == ALOCK_UNLOCKED)
316 return slot_data.al_lock;
318 res = alock_test_lock (info->al_fd, info->al_slot);
319 if (res < 0) return -1;
321 if ((slot_data.al_lock & ALOCK_SMASK) == ALOCK_UNIQUE) {
322 return slot_data.al_lock;
324 return ALOCK_LOCKED | nosave;
328 return ALOCK_DIRTY | nosave;
332 alock_open ( alock_info_t * info,
333 const char * appname,
338 alock_info_t scan_info;
339 alock_slot_t slot_data;
342 int dirty_count, live_count, nosave;
345 assert (info != NULL);
346 assert (appname != NULL);
347 assert (envdir != NULL);
348 assert ((locktype & ALOCK_SMASK) >= 1 && (locktype & ALOCK_SMASK) <= 2);
350 slot_data.al_lock = locktype;
351 slot_data.al_stamp = time(NULL);
352 slot_data.al_pid = getpid();
353 slot_data.al_appname = ber_memcalloc (1, ALOCK_MAX_APPNAME);
354 if (slot_data.al_appname == NULL) {
355 return ALOCK_UNSTABLE;
357 strncpy (slot_data.al_appname, appname, ALOCK_MAX_APPNAME-1);
358 slot_data.al_appname [ALOCK_MAX_APPNAME-1] = '\0';
360 filename = ber_memcalloc (1, strlen (envdir) + strlen ("/alock") + 1);
361 if (filename == NULL ) {
362 ber_memfree (slot_data.al_appname);
363 return ALOCK_UNSTABLE;
365 ptr = lutil_strcopy(filename, envdir);
366 lutil_strcopy(ptr, "/alock");
367 info->al_fd = open (filename, O_CREAT|O_RDWR, 0666);
368 ber_memfree (filename);
369 if (info->al_fd < 0) {
370 ber_memfree (slot_data.al_appname);
371 return ALOCK_UNSTABLE;
375 res = alock_grab_lock (info->al_fd, 0);
378 ber_memfree (slot_data.al_appname);
379 return ALOCK_UNSTABLE;
382 res = fstat (info->al_fd, &statbuf);
385 ber_memfree (slot_data.al_appname);
386 return ALOCK_UNSTABLE;
389 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
393 scan_info.al_fd = info->al_fd;
394 for (scan_info.al_slot = 1;
395 scan_info.al_slot < max_slot;
396 ++ scan_info.al_slot) {
397 if (scan_info.al_slot != info->al_slot) {
398 res = alock_query_slot (&scan_info);
400 if (res & ALOCK_NOSAVE) {
401 nosave = ALOCK_NOSAVE;
404 if (res == ALOCK_UNLOCKED
405 && info->al_slot == 0) {
406 info->al_slot = scan_info.al_slot;
408 } else if (res == ALOCK_LOCKED) {
411 } else if (res == ALOCK_UNIQUE
412 && (( locktype & ALOCK_SMASK ) == ALOCK_UNIQUE
415 ber_memfree (slot_data.al_appname);
418 } else if (res == ALOCK_DIRTY) {
421 } else if (res == -1) {
423 ber_memfree (slot_data.al_appname);
424 return ALOCK_UNSTABLE;
430 if (dirty_count && live_count) {
432 ber_memfree (slot_data.al_appname);
433 return ALOCK_UNSTABLE;
436 if (info->al_slot == 0) info->al_slot = max_slot + 1;
437 res = alock_grab_lock (info->al_fd,
441 ber_memfree (slot_data.al_appname);
442 return ALOCK_UNSTABLE;
444 res = alock_write_slot (info, &slot_data);
445 ber_memfree (slot_data.al_appname);
448 return ALOCK_UNSTABLE;
451 res = alock_release_lock (info->al_fd, 0);
454 return ALOCK_UNSTABLE;
457 if (dirty_count) return ALOCK_RECOVER | nosave;
458 return ALOCK_CLEAN | nosave;
462 alock_scan ( alock_info_t * info )
465 alock_info_t scan_info;
467 int dirty_count, live_count, nosave;
469 assert (info != NULL);
471 scan_info.al_fd = info->al_fd;
473 res = alock_grab_lock (info->al_fd, 0);
476 return ALOCK_UNSTABLE;
479 res = fstat (info->al_fd, &statbuf);
482 return ALOCK_UNSTABLE;
485 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
489 for (scan_info.al_slot = 1;
490 scan_info.al_slot < max_slot;
491 ++ scan_info.al_slot) {
492 if (scan_info.al_slot != info->al_slot) {
493 res = alock_query_slot (&scan_info);
495 if (res & ALOCK_NOSAVE) {
496 nosave = ALOCK_NOSAVE;
500 if (res == ALOCK_LOCKED) {
503 } else if (res == ALOCK_DIRTY) {
506 } else if (res == -1) {
508 return ALOCK_UNSTABLE;
514 res = alock_release_lock (info->al_fd, 0);
517 return ALOCK_UNSTABLE;
523 return ALOCK_UNSTABLE;
525 return ALOCK_RECOVER | nosave;
529 return ALOCK_CLEAN | nosave;
533 alock_close ( alock_info_t * info, int nosave )
535 alock_slot_t slot_data;
538 if ( !info->al_slot )
541 (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
543 res = alock_grab_lock (info->al_fd, 0);
546 return ALOCK_UNSTABLE;
549 /* mark our slot as clean */
550 res = alock_read_slot (info, &slot_data);
553 if (slot_data.al_appname != NULL)
554 ber_memfree (slot_data.al_appname);
555 return ALOCK_UNSTABLE;
557 slot_data.al_lock = ALOCK_UNLOCKED;
559 slot_data.al_lock |= ALOCK_NOSAVE;
560 res = alock_write_slot (info, &slot_data);
563 if (slot_data.al_appname != NULL)
564 ber_memfree (slot_data.al_appname);
565 return ALOCK_UNSTABLE;
567 if (slot_data.al_appname != NULL) {
568 ber_memfree (slot_data.al_appname);
569 slot_data.al_appname = NULL;
572 res = alock_release_lock (info->al_fd, info->al_slot);
575 return ALOCK_UNSTABLE;
577 res = alock_release_lock (info->al_fd, 0);
580 return ALOCK_UNSTABLE;
583 res = close (info->al_fd);
584 if (res == -1) return ALOCK_UNSTABLE;
590 alock_recover ( alock_info_t * info )
593 alock_slot_t slot_data;
594 alock_info_t scan_info;
597 assert (info != NULL);
599 scan_info.al_fd = info->al_fd;
601 (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
603 res = alock_grab_lock (info->al_fd, 0);
606 return ALOCK_UNSTABLE;
609 res = fstat (info->al_fd, &statbuf);
612 return ALOCK_UNSTABLE;
615 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
616 for (scan_info.al_slot = 1;
617 scan_info.al_slot < max_slot;
618 ++ scan_info.al_slot) {
619 if (scan_info.al_slot != info->al_slot) {
620 res = alock_query_slot (&scan_info) & ~ALOCK_NOSAVE;
622 if (res == ALOCK_LOCKED
623 || res == ALOCK_UNIQUE) {
624 /* recovery attempt on an active db? */
626 return ALOCK_UNSTABLE;
628 } else if (res == ALOCK_DIRTY) {
630 res = alock_read_slot (&scan_info, &slot_data);
633 return ALOCK_UNSTABLE;
635 slot_data.al_lock = ALOCK_UNLOCKED;
636 res = alock_write_slot (&scan_info, &slot_data);
639 if (slot_data.al_appname != NULL)
640 ber_memfree (slot_data.al_appname);
641 return ALOCK_UNSTABLE;
643 if (slot_data.al_appname != NULL) {
644 ber_memfree (slot_data.al_appname);
645 slot_data.al_appname = NULL;
648 } else if (res == -1) {
650 return ALOCK_UNSTABLE;
656 res = alock_release_lock (info->al_fd, 0);
659 return ALOCK_UNSTABLE;
665 #endif /* SLAPD_BDB || SLAPD_HDB */