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