1 /* alock.c - access lock library */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2005-2014 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 Emily 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
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 )
70 HANDLE hh = _get_osfhandle ( fd );
72 ov.Offset = ALOCK_SLOT_SIZE*slot;
74 res = LockFileEx( hh, LOCKFILE_EXCLUSIVE_LOCK, 0,
75 ALOCK_SLOT_SIZE, 0, &ov ) ? 0 : -1;
77 # error alock needs lockf, fcntl, or LockFile[Ex]
80 assert (errno != EDEADLK);
87 alock_release_lock ( int fd, int slot )
91 #if defined( HAVE_LOCKF )
92 res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
93 if (res == -1) return -1;
94 res = lockf (fd, F_ULOCK, (off_t) ALOCK_SLOT_SIZE);
95 if (res == -1) return -1;
96 #elif defined ( HAVE_FCNTL )
97 struct flock lock_info;
98 (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
100 lock_info.l_type = F_UNLCK;
101 lock_info.l_whence = SEEK_SET;
102 lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
103 lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
105 res = fcntl (fd, F_SETLKW, &lock_info);
106 if (res == -1) return -1;
107 #elif defined( _WIN32 )
108 HANDLE hh = _get_osfhandle ( fd );
109 if ( !UnlockFile ( hh, ALOCK_SLOT_SIZE*slot, 0,
110 ALOCK_SLOT_SIZE, 0 ))
113 # error alock needs lockf, fcntl, or LockFile[Ex]
120 alock_share_lock ( int fd, int slot )
124 #if defined( HAVE_LOCKF )
125 res = 0; /* lockf has no shared locks */
126 #elif defined ( HAVE_FCNTL )
127 struct flock lock_info;
128 (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
130 /* The shared lock replaces the existing lock */
131 lock_info.l_type = F_RDLCK;
132 lock_info.l_whence = SEEK_SET;
133 lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
134 lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
136 res = fcntl (fd, F_SETLK, &lock_info);
137 if (res == -1) return -1;
138 #elif defined( _WIN32 )
140 HANDLE hh = _get_osfhandle ( fd );
142 /* Windows locks are mandatory, not advisory.
143 * We must downgrade the lock to allow future
144 * callers to read the slot data.
146 * First acquire a shared lock. Unlock will
147 * release the existing exclusive lock.
150 ov.Offset = ALOCK_SLOT_SIZE*slot;
152 LockFileEx (hh, 0, 0, ALOCK_SLOT_SIZE, 0, &ov);
153 UnlockFile (hh, ALOCK_SLOT_SIZE*slot, 0, ALOCK_SLOT_SIZE, 0);
155 # error alock needs lockf, fcntl, or LockFile[Ex]
162 alock_test_lock ( int fd, int slot )
166 #if defined( HAVE_LOCKF )
167 res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
168 if (res == -1) return -1;
170 res = lockf (fd, F_TEST, (off_t) ALOCK_SLOT_SIZE);
172 if (errno == EACCES || errno == EAGAIN) {
178 #elif defined( HAVE_FCNTL )
179 struct flock lock_info;
180 (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
182 lock_info.l_type = F_WRLCK;
183 lock_info.l_whence = SEEK_SET;
184 lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
185 lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
187 res = fcntl (fd, F_GETLK, &lock_info);
188 if (res == -1) return -1;
190 if (lock_info.l_type != F_UNLCK) return ALOCK_LOCKED;
191 #elif defined( _WIN32 )
193 HANDLE hh = _get_osfhandle ( fd );
195 ov.Offset = ALOCK_SLOT_SIZE*slot;
198 LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY, 0,
199 ALOCK_SLOT_SIZE, 0, &ov )) {
200 int err = GetLastError();
201 if ( err == ERROR_LOCK_VIOLATION )
207 # error alock needs lockf, fcntl, or LockFile
213 /* Read a 64bit LE value */
214 static unsigned long int
215 alock_read_iattr ( unsigned char * bufptr )
217 unsigned long int val = 0;
220 assert (bufptr != NULL);
222 bufptr += sizeof (unsigned long int);
223 for (count=0; count <= (int) sizeof (unsigned long int); ++count) {
225 val += (unsigned long int) *bufptr--;
231 /* Write a 64bit LE value */
233 alock_write_iattr ( unsigned char * bufptr,
234 unsigned long int val )
238 assert (bufptr != NULL);
240 for (count=0; count < 8; ++count) {
241 *bufptr++ = (unsigned char) (val & 0xff);
247 alock_read_slot ( alock_info_t * info,
248 alock_slot_t * slot_data )
250 unsigned char slotbuf [ALOCK_SLOT_SIZE];
251 int res, size, size_total, err;
253 assert (info != NULL);
254 assert (slot_data != NULL);
255 assert (info->al_slot > 0);
257 res = lseek (info->al_fd,
258 (off_t) (ALOCK_SLOT_SIZE * info->al_slot),
260 if (res == -1) return -1;
263 while (size_total < ALOCK_SLOT_SIZE) {
264 size = read (info->al_fd,
265 slotbuf + size_total,
266 ALOCK_SLOT_SIZE - size_total);
267 if (size == 0) return -1;
270 if (err != EINTR && err != EAGAIN) return -1;
276 if (alock_read_iattr (slotbuf) != ALOCK_MAGIC) {
279 slot_data->al_lock = alock_read_iattr (slotbuf+8);
280 slot_data->al_stamp = alock_read_iattr (slotbuf+16);
281 slot_data->al_pid = alock_read_iattr (slotbuf+24);
283 if (slot_data->al_appname) ber_memfree (slot_data->al_appname);
284 slot_data->al_appname = ber_memcalloc (1, ALOCK_MAX_APPNAME);
285 if (slot_data->al_appname == NULL) {
288 strncpy (slot_data->al_appname, (char *)slotbuf+32, ALOCK_MAX_APPNAME-1);
289 (slot_data->al_appname) [ALOCK_MAX_APPNAME-1] = '\0';
295 alock_write_slot ( alock_info_t * info,
296 alock_slot_t * slot_data )
298 unsigned char slotbuf [ALOCK_SLOT_SIZE];
299 int res, size, size_total, err;
301 assert (info != NULL);
302 assert (slot_data != NULL);
303 assert (info->al_slot > 0);
305 (void) memset ((void *) slotbuf, 0, ALOCK_SLOT_SIZE);
307 alock_write_iattr (slotbuf, ALOCK_MAGIC);
308 assert (alock_read_iattr (slotbuf) == ALOCK_MAGIC);
309 alock_write_iattr (slotbuf+8, slot_data->al_lock);
310 alock_write_iattr (slotbuf+16, slot_data->al_stamp);
311 alock_write_iattr (slotbuf+24, slot_data->al_pid);
313 if (slot_data->al_appname)
314 strncpy ((char *)slotbuf+32, slot_data->al_appname, ALOCK_MAX_APPNAME-1);
315 slotbuf[ALOCK_SLOT_SIZE-1] = '\0';
317 res = lseek (info->al_fd,
318 (off_t) (ALOCK_SLOT_SIZE * info->al_slot),
320 if (res == -1) return -1;
323 while (size_total < ALOCK_SLOT_SIZE) {
324 size = write (info->al_fd,
325 slotbuf + size_total,
326 ALOCK_SLOT_SIZE - size_total);
327 if (size == 0) return -1;
330 if (err != EINTR && err != EAGAIN) return -1;
340 alock_query_slot ( alock_info_t * info )
343 alock_slot_t slot_data;
345 assert (info != NULL);
346 assert (info->al_slot > 0);
348 (void) memset ((void *) &slot_data, 0, sizeof (alock_slot_t));
349 alock_read_slot (info, &slot_data);
351 if (slot_data.al_appname != NULL) ber_memfree (slot_data.al_appname);
352 slot_data.al_appname = NULL;
354 nosave = slot_data.al_lock & ALOCK_NOSAVE;
356 if ((slot_data.al_lock & ALOCK_SMASK) == ALOCK_UNLOCKED)
357 return slot_data.al_lock;
359 res = alock_test_lock (info->al_fd, info->al_slot);
360 if (res < 0) return -1;
362 if ((slot_data.al_lock & ALOCK_SMASK) == ALOCK_UNIQUE) {
363 return slot_data.al_lock;
365 return ALOCK_LOCKED | nosave;
369 return ALOCK_DIRTY | nosave;
373 alock_open ( alock_info_t * info,
374 const char * appname,
379 alock_info_t scan_info;
380 alock_slot_t slot_data;
383 int dirty_count, live_count, nosave;
386 assert (info != NULL);
387 assert (appname != NULL);
388 assert (envdir != NULL);
389 assert ((locktype & ALOCK_SMASK) >= 1 && (locktype & ALOCK_SMASK) <= 2);
391 slot_data.al_lock = locktype;
392 slot_data.al_stamp = time(NULL);
393 slot_data.al_pid = getpid();
394 slot_data.al_appname = ber_memcalloc (1, ALOCK_MAX_APPNAME);
395 if (slot_data.al_appname == NULL) {
396 return ALOCK_UNSTABLE;
398 strncpy (slot_data.al_appname, appname, ALOCK_MAX_APPNAME-1);
399 slot_data.al_appname [ALOCK_MAX_APPNAME-1] = '\0';
401 filename = ber_memcalloc (1, strlen (envdir) + strlen ("/alock") + 1);
402 if (filename == NULL ) {
403 ber_memfree (slot_data.al_appname);
404 return ALOCK_UNSTABLE;
406 ptr = lutil_strcopy(filename, envdir);
407 lutil_strcopy(ptr, "/alock");
409 { HANDLE handle = CreateFile (filename, GENERIC_READ|GENERIC_WRITE,
410 FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,
411 FILE_ATTRIBUTE_NORMAL, NULL);
412 info->al_fd = _open_osfhandle (handle, 0);
415 info->al_fd = open (filename, O_CREAT|O_RDWR, 0666);
417 ber_memfree (filename);
418 if (info->al_fd < 0) {
419 ber_memfree (slot_data.al_appname);
420 return ALOCK_UNSTABLE;
424 res = alock_grab_lock (info->al_fd, 0);
427 ber_memfree (slot_data.al_appname);
428 return ALOCK_UNSTABLE;
431 res = fstat (info->al_fd, &statbuf);
434 ber_memfree (slot_data.al_appname);
435 return ALOCK_UNSTABLE;
438 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
442 scan_info.al_fd = info->al_fd;
443 for (scan_info.al_slot = 1;
444 scan_info.al_slot < max_slot;
445 ++ scan_info.al_slot) {
446 if (scan_info.al_slot != info->al_slot) {
447 res = alock_query_slot (&scan_info);
449 if (res & ALOCK_NOSAVE) {
450 nosave = ALOCK_NOSAVE;
453 if (res == ALOCK_UNLOCKED
454 && info->al_slot == 0) {
455 info->al_slot = scan_info.al_slot;
457 } else if (res == ALOCK_LOCKED) {
460 } else if (res == ALOCK_UNIQUE
461 && (( locktype & ALOCK_SMASK ) == ALOCK_UNIQUE
464 ber_memfree (slot_data.al_appname);
467 } else if (res == ALOCK_DIRTY) {
470 } else if (res == -1) {
472 ber_memfree (slot_data.al_appname);
473 return ALOCK_UNSTABLE;
479 if (dirty_count && live_count) {
481 ber_memfree (slot_data.al_appname);
482 return ALOCK_UNSTABLE;
485 if (info->al_slot == 0) info->al_slot = max_slot + 1;
486 res = alock_grab_lock (info->al_fd,
490 ber_memfree (slot_data.al_appname);
491 return ALOCK_UNSTABLE;
493 res = alock_write_slot (info, &slot_data);
494 ber_memfree (slot_data.al_appname);
497 return ALOCK_UNSTABLE;
499 alock_share_lock (info->al_fd, info->al_slot);
501 res = alock_release_lock (info->al_fd, 0);
504 return ALOCK_UNSTABLE;
507 if (dirty_count) return ALOCK_RECOVER | nosave;
508 return ALOCK_CLEAN | nosave;
512 alock_scan ( alock_info_t * info )
515 alock_info_t scan_info;
517 int dirty_count, live_count, nosave;
519 assert (info != NULL);
521 scan_info.al_fd = info->al_fd;
523 res = alock_grab_lock (info->al_fd, 0);
526 return ALOCK_UNSTABLE;
529 res = fstat (info->al_fd, &statbuf);
532 return ALOCK_UNSTABLE;
535 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
539 for (scan_info.al_slot = 1;
540 scan_info.al_slot < max_slot;
541 ++ scan_info.al_slot) {
542 if (scan_info.al_slot != info->al_slot) {
543 res = alock_query_slot (&scan_info);
545 if (res & ALOCK_NOSAVE) {
546 nosave = ALOCK_NOSAVE;
550 if (res == ALOCK_LOCKED) {
553 } else if (res == ALOCK_DIRTY) {
556 } else if (res == -1) {
558 return ALOCK_UNSTABLE;
564 res = alock_release_lock (info->al_fd, 0);
567 return ALOCK_UNSTABLE;
573 return ALOCK_UNSTABLE;
575 return ALOCK_RECOVER | nosave;
579 return ALOCK_CLEAN | nosave;
583 alock_close ( alock_info_t * info, int nosave )
585 alock_slot_t slot_data;
588 if ( !info->al_slot )
591 (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
593 res = alock_grab_lock (info->al_fd, 0);
596 /* Windows doesn't clean up locks immediately when a process exits.
597 * Make sure we release our locks, to prevent stale locks from
600 alock_release_lock (info->al_fd, 0);
602 return ALOCK_UNSTABLE;
605 /* mark our slot as clean */
606 res = alock_read_slot (info, &slot_data);
608 if (slot_data.al_appname != NULL)
609 ber_memfree (slot_data.al_appname);
612 slot_data.al_lock = ALOCK_UNLOCKED;
614 slot_data.al_lock |= ALOCK_NOSAVE;
615 /* since we have slot 0 locked, we don't need our slot lock */
616 res = alock_release_lock (info->al_fd, info->al_slot);
620 res = alock_write_slot (info, &slot_data);
622 if (slot_data.al_appname != NULL)
623 ber_memfree (slot_data.al_appname);
626 if (slot_data.al_appname != NULL) {
627 ber_memfree (slot_data.al_appname);
628 slot_data.al_appname = NULL;
631 res = alock_release_lock (info->al_fd, 0);
634 return ALOCK_UNSTABLE;
637 res = close (info->al_fd);
638 if (res == -1) return ALOCK_UNSTABLE;
644 alock_recover ( alock_info_t * info )
647 alock_slot_t slot_data;
648 alock_info_t scan_info;
651 assert (info != NULL);
653 scan_info.al_fd = info->al_fd;
655 (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
657 res = alock_grab_lock (info->al_fd, 0);
662 res = fstat (info->al_fd, &statbuf);
667 max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
668 for (scan_info.al_slot = 1;
669 scan_info.al_slot < max_slot;
670 ++ scan_info.al_slot) {
671 if (scan_info.al_slot != info->al_slot) {
672 res = alock_query_slot (&scan_info) & ~ALOCK_NOSAVE;
674 if (res == ALOCK_LOCKED
675 || res == ALOCK_UNIQUE) {
676 /* recovery attempt on an active db? */
679 } else if (res == ALOCK_DIRTY) {
681 res = alock_read_slot (&scan_info, &slot_data);
685 slot_data.al_lock = ALOCK_UNLOCKED;
686 res = alock_write_slot (&scan_info, &slot_data);
688 if (slot_data.al_appname != NULL)
689 ber_memfree (slot_data.al_appname);
692 if (slot_data.al_appname != NULL) {
693 ber_memfree (slot_data.al_appname);
694 slot_data.al_appname = NULL;
697 } else if (res == -1) {
704 res = alock_release_lock (info->al_fd, 0);
707 return ALOCK_UNSTABLE;
713 alock_release_lock (info->al_fd, 0);
715 return ALOCK_UNSTABLE;
718 #endif /* SLAPD_BDB || SLAPD_HDB */