Back to index

openldap  2.4.31
alock.c
Go to the documentation of this file.
00001 /* alock.c - access lock library */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 2005-2012 The OpenLDAP Foundation.
00006  * Portions Copyright 2004-2005 Symas Corporation.
00007  * All rights reserved.
00008  *
00009  * Redistribution and use in source and binary forms, with or without
00010  * modification, are permitted only as authorized by the OpenLDAP
00011  * Public License.
00012  *
00013  * A copy of this license is available in the file LICENSE in the
00014  * top-level directory of the distribution or, alternatively, at
00015  * <http://www.OpenLDAP.org/license.html>.
00016  */
00017 /* ACKNOWLEDGEMENTS:
00018  * This work was initially developed by Matthew Backes at Symas
00019  * Corporation for inclusion in OpenLDAP Software.
00020  */
00021 
00022 #include "portable.h"
00023 
00024 #if SLAPD_BDB || SLAPD_HDB
00025 
00026 #include <lber.h>
00027 #include "alock.h"
00028 #include "lutil.h"
00029 
00030 #include <ac/stdlib.h>
00031 #include <ac/string.h>
00032 #include <ac/unistd.h>
00033 #include <ac/errno.h>
00034 #include <ac/assert.h>
00035 #include <sys/types.h>
00036 #include <sys/stat.h>
00037 #ifdef HAVE_SYS_FILE_H
00038 #include <sys/file.h>
00039 #endif
00040 #include <fcntl.h>
00041 
00042 #ifdef _WIN32
00043 #include <stdio.h>
00044 #include <io.h>
00045 #include <sys/locking.h>
00046 #endif
00047 
00048 
00049 static int
00050 alock_grab_lock ( int fd, int slot )
00051 {
00052        int res;
00053        
00054 #if defined( HAVE_LOCKF )
00055        res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
00056        if (res == -1) return -1;
00057        res = lockf (fd, F_LOCK, (off_t) ALOCK_SLOT_SIZE);
00058 #elif defined( HAVE_FCNTL )
00059        struct flock lock_info;
00060        (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
00061 
00062        lock_info.l_type = F_WRLCK;
00063        lock_info.l_whence = SEEK_SET;
00064        lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
00065        lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
00066 
00067        res = fcntl (fd, F_SETLKW, &lock_info);
00068 #elif defined( _WIN32 )
00069        if( _lseek( fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET ) < 0 )
00070               return -1;
00071        /*
00072         * _lock will try for the lock once per second, returning EDEADLOCK
00073         * after ten tries. We just loop until we either get the lock
00074         * or some other error is returned.
00075         */
00076        while((res = _locking( fd, _LK_LOCK, ALOCK_SLOT_SIZE )) < 0 ) {
00077               if( errno != EDEADLOCK )
00078                      break;
00079        }
00080 #else
00081 #   error alock needs lockf, fcntl, or _locking
00082 #endif
00083        if (res == -1) {
00084               assert (errno != EDEADLK);
00085               return -1;
00086        }
00087        return 0;
00088 }
00089 
00090 static int
00091 alock_release_lock ( int fd, int slot )
00092 {
00093        int res;
00094        
00095 #if defined( HAVE_LOCKF )
00096        res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
00097        if (res == -1) return -1;
00098        res = lockf (fd, F_ULOCK, (off_t) ALOCK_SLOT_SIZE);
00099        if (res == -1) return -1;
00100 #elif defined ( HAVE_FCNTL )
00101        struct flock lock_info;
00102        (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
00103 
00104        lock_info.l_type = F_UNLCK;
00105        lock_info.l_whence = SEEK_SET;
00106        lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
00107        lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
00108 
00109        res = fcntl (fd, F_SETLKW, &lock_info);
00110        if (res == -1) return -1;
00111 #elif defined( _WIN32 )
00112        res = _lseek (fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET);
00113        if (res == -1) return -1;
00114        res = _locking( fd, _LK_UNLCK, ALOCK_SLOT_SIZE );
00115        if (res == -1) return -1;
00116 #else
00117 #   error alock needs lockf, fcntl, or _locking
00118 #endif
00119 
00120        return 0;
00121 }
00122 
00123 static int
00124 alock_test_lock ( int fd, int slot )
00125 {
00126        int res;
00127 
00128 #if defined( HAVE_LOCKF )
00129        res = lseek (fd, (off_t) (ALOCK_SLOT_SIZE * slot), SEEK_SET);
00130        if (res == -1) return -1;
00131 
00132        res = lockf (fd, F_TEST, (off_t) ALOCK_SLOT_SIZE);
00133        if (res == -1) {
00134               if (errno == EACCES || errno == EAGAIN) { 
00135                      return ALOCK_LOCKED;
00136               } else {
00137                      return -1;
00138               }
00139        }
00140 #elif defined( HAVE_FCNTL )
00141        struct flock lock_info;
00142        (void) memset ((void *) &lock_info, 0, sizeof (struct flock));
00143 
00144        lock_info.l_type = F_WRLCK;
00145        lock_info.l_whence = SEEK_SET;
00146        lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot);
00147        lock_info.l_len = (off_t) ALOCK_SLOT_SIZE;
00148 
00149        res = fcntl (fd, F_GETLK, &lock_info);
00150        if (res == -1) return -1;
00151 
00152        if (lock_info.l_type != F_UNLCK) return ALOCK_LOCKED;
00153 #elif defined( _WIN32 )
00154        res = _lseek (fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET);
00155        if (res == -1) return -1;
00156        res = _locking( fd, _LK_NBLCK, ALOCK_SLOT_SIZE );
00157        _locking( fd, _LK_UNLCK, ALOCK_SLOT_SIZE );
00158        if (res == -1) {
00159           if( errno == EACCES ) {
00160                  return ALOCK_LOCKED;
00161           } else {
00162                  return -1;
00163           }
00164        }
00165 #else
00166 #   error alock needs lockf, fcntl, or _locking
00167 #endif
00168        
00169        return 0;
00170 }
00171 
00172 /* Read a 64bit LE value */
00173 static unsigned long int
00174 alock_read_iattr ( unsigned char * bufptr )
00175 {
00176        unsigned long int val = 0;
00177        int count;
00178 
00179        assert (bufptr != NULL);
00180 
00181        bufptr += sizeof (unsigned long int);
00182        for (count=0; count <= (int) sizeof (unsigned long int); ++count) {
00183               val <<= 8;
00184               val += (unsigned long int) *bufptr--;
00185        }
00186 
00187        return val;
00188 }
00189 
00190 /* Write a 64bit LE value */
00191 static void
00192 alock_write_iattr ( unsigned char * bufptr,
00193                   unsigned long int val )
00194 {
00195        int count;
00196 
00197        assert (bufptr != NULL);
00198 
00199        for (count=0; count < 8; ++count) {
00200               *bufptr++ = (unsigned char) (val & 0xff);
00201               val >>= 8;
00202        }
00203 }
00204 
00205 static int
00206 alock_read_slot ( alock_info_t * info,
00207                 alock_slot_t * slot_data )
00208 {
00209        unsigned char slotbuf [ALOCK_SLOT_SIZE];
00210        int res, size, size_total, err;
00211 
00212        assert (info != NULL);
00213        assert (slot_data != NULL);
00214        assert (info->al_slot > 0);
00215 
00216        res = lseek (info->al_fd, 
00217                    (off_t) (ALOCK_SLOT_SIZE * info->al_slot), 
00218                    SEEK_SET);
00219        if (res == -1) return -1;
00220 
00221        size_total = 0;
00222        while (size_total < ALOCK_SLOT_SIZE) {
00223               size = read (info->al_fd, 
00224                           slotbuf + size_total, 
00225                           ALOCK_SLOT_SIZE - size_total);
00226               if (size == 0) return -1;
00227               if (size < 0) {
00228                      err = errno;
00229                      if (err != EINTR && err != EAGAIN) return -1;
00230               } else {
00231                      size_total += size;
00232               }
00233        }
00234        
00235        if (alock_read_iattr (slotbuf) != ALOCK_MAGIC) {
00236               return -1;
00237        }
00238        slot_data->al_lock  = alock_read_iattr (slotbuf+8);
00239        slot_data->al_stamp = alock_read_iattr (slotbuf+16);
00240        slot_data->al_pid   = alock_read_iattr (slotbuf+24);
00241 
00242        if (slot_data->al_appname) ber_memfree (slot_data->al_appname);
00243        slot_data->al_appname = ber_memcalloc (1, ALOCK_MAX_APPNAME);
00244        if (slot_data->al_appname == NULL) {
00245               return -1;
00246        }
00247        strncpy (slot_data->al_appname, (char *)slotbuf+32, ALOCK_MAX_APPNAME-1);
00248        (slot_data->al_appname) [ALOCK_MAX_APPNAME-1] = '\0';
00249 
00250        return 0;
00251 }
00252 
00253 static int
00254 alock_write_slot ( alock_info_t * info,
00255                  alock_slot_t * slot_data )
00256 {
00257        unsigned char slotbuf [ALOCK_SLOT_SIZE];
00258        int res, size, size_total, err;
00259 
00260        assert (info != NULL);
00261        assert (slot_data != NULL);
00262        assert (info->al_slot > 0);
00263 
00264        (void) memset ((void *) slotbuf, 0, ALOCK_SLOT_SIZE);
00265        
00266        alock_write_iattr (slotbuf,    ALOCK_MAGIC);
00267        assert (alock_read_iattr (slotbuf) == ALOCK_MAGIC);
00268        alock_write_iattr (slotbuf+8,  slot_data->al_lock);
00269        alock_write_iattr (slotbuf+16, slot_data->al_stamp);
00270        alock_write_iattr (slotbuf+24, slot_data->al_pid);
00271 
00272        if (slot_data->al_appname)
00273               strncpy ((char *)slotbuf+32, slot_data->al_appname, ALOCK_MAX_APPNAME-1);
00274        slotbuf[ALOCK_SLOT_SIZE-1] = '\0';
00275 
00276        res = lseek (info->al_fd, 
00277                    (off_t) (ALOCK_SLOT_SIZE * info->al_slot),
00278                    SEEK_SET);
00279        if (res == -1) return -1;
00280 
00281        size_total = 0;
00282        while (size_total < ALOCK_SLOT_SIZE) {
00283               size = write (info->al_fd, 
00284                            slotbuf + size_total, 
00285                            ALOCK_SLOT_SIZE - size_total);
00286               if (size == 0) return -1;
00287               if (size < 0) {
00288                      err = errno;
00289                      if (err != EINTR && err != EAGAIN) return -1;
00290               } else {
00291                      size_total += size;
00292               }
00293        }
00294        
00295        return 0;
00296 }
00297 
00298 static int
00299 alock_query_slot ( alock_info_t * info )
00300 {
00301        int res, nosave;
00302        alock_slot_t slot_data;
00303 
00304        assert (info != NULL);
00305        assert (info->al_slot > 0);
00306        
00307        (void) memset ((void *) &slot_data, 0, sizeof (alock_slot_t));
00308        alock_read_slot (info, &slot_data);
00309 
00310        if (slot_data.al_appname != NULL) ber_memfree (slot_data.al_appname);
00311        slot_data.al_appname = NULL;
00312 
00313        nosave = slot_data.al_lock & ALOCK_NOSAVE;
00314 
00315        if ((slot_data.al_lock & ALOCK_SMASK) == ALOCK_UNLOCKED)
00316               return slot_data.al_lock;
00317 
00318        res = alock_test_lock (info->al_fd, info->al_slot);
00319        if (res < 0) return -1;
00320        if (res > 0) {
00321               if ((slot_data.al_lock & ALOCK_SMASK) == ALOCK_UNIQUE) {
00322                      return slot_data.al_lock;
00323               } else {
00324                      return ALOCK_LOCKED | nosave;
00325               }
00326        }
00327        
00328        return ALOCK_DIRTY | nosave;
00329 }
00330 
00331 int 
00332 alock_open ( alock_info_t * info,
00333             const char * appname,
00334             const char * envdir,
00335             int locktype )
00336 {
00337        struct stat statbuf;
00338        alock_info_t scan_info;
00339        alock_slot_t slot_data;
00340        char * filename;
00341        int res, max_slot;
00342        int dirty_count, live_count, nosave;
00343        char *ptr;
00344 
00345        assert (info != NULL);
00346        assert (appname != NULL);
00347        assert (envdir != NULL);
00348        assert ((locktype & ALOCK_SMASK) >= 1 && (locktype & ALOCK_SMASK) <= 2);
00349 
00350        slot_data.al_lock = locktype;
00351        slot_data.al_stamp = time(NULL);
00352        slot_data.al_pid = getpid();
00353        slot_data.al_appname = ber_memcalloc (1, ALOCK_MAX_APPNAME);
00354        if (slot_data.al_appname == NULL) {
00355               return ALOCK_UNSTABLE;
00356        }
00357        strncpy (slot_data.al_appname, appname, ALOCK_MAX_APPNAME-1);
00358        slot_data.al_appname [ALOCK_MAX_APPNAME-1] = '\0';
00359 
00360        filename = ber_memcalloc (1, strlen (envdir) + strlen ("/alock") + 1);
00361        if (filename == NULL ) {
00362               ber_memfree (slot_data.al_appname);
00363               return ALOCK_UNSTABLE;
00364        }
00365        ptr = lutil_strcopy(filename, envdir);
00366        lutil_strcopy(ptr, "/alock");
00367        info->al_fd = open (filename, O_CREAT|O_RDWR, 0666);
00368        ber_memfree (filename);
00369        if (info->al_fd < 0) {
00370               ber_memfree (slot_data.al_appname);
00371               return ALOCK_UNSTABLE;
00372        }
00373        info->al_slot = 0;
00374 
00375        res = alock_grab_lock (info->al_fd, 0);
00376        if (res == -1) { 
00377               close (info->al_fd);
00378               ber_memfree (slot_data.al_appname);
00379               return ALOCK_UNSTABLE;
00380        }
00381 
00382        res = fstat (info->al_fd, &statbuf);
00383        if (res == -1) { 
00384               close (info->al_fd);
00385               ber_memfree (slot_data.al_appname);
00386               return ALOCK_UNSTABLE;
00387        }
00388 
00389        max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
00390        dirty_count = 0;
00391        live_count = 0;
00392        nosave = 0;
00393        scan_info.al_fd = info->al_fd;
00394        for (scan_info.al_slot = 1; 
00395             scan_info.al_slot < max_slot;
00396             ++ scan_info.al_slot) {
00397               if (scan_info.al_slot != info->al_slot) {
00398                      res = alock_query_slot (&scan_info);
00399 
00400                      if (res & ALOCK_NOSAVE) {
00401                             nosave = ALOCK_NOSAVE;
00402                             res ^= ALOCK_NOSAVE;
00403                      }
00404                      if (res == ALOCK_UNLOCKED
00405                          && info->al_slot == 0) {
00406                             info->al_slot = scan_info.al_slot;
00407 
00408                      } else if (res == ALOCK_LOCKED) {
00409                             ++live_count;
00410 
00411                      } else if (res == ALOCK_UNIQUE
00412                             && (( locktype & ALOCK_SMASK ) == ALOCK_UNIQUE
00413                             || nosave )) {
00414                             close (info->al_fd);
00415                             ber_memfree (slot_data.al_appname);
00416                             return ALOCK_BUSY;
00417 
00418                      } else if (res == ALOCK_DIRTY) {
00419                             ++dirty_count;
00420 
00421                      } else if (res == -1) {
00422                             close (info->al_fd);
00423                             ber_memfree (slot_data.al_appname);
00424                             return ALOCK_UNSTABLE;
00425 
00426                      }
00427               }
00428        }
00429 
00430        if (dirty_count && live_count) {
00431               close (info->al_fd);
00432               ber_memfree (slot_data.al_appname);
00433               return ALOCK_UNSTABLE;
00434        }
00435        
00436        if (info->al_slot == 0) info->al_slot = max_slot + 1;
00437        res = alock_grab_lock (info->al_fd,
00438                             info->al_slot);
00439        if (res == -1) { 
00440               close (info->al_fd);
00441               ber_memfree (slot_data.al_appname);
00442               return ALOCK_UNSTABLE;
00443        }
00444        res = alock_write_slot (info, &slot_data);
00445        ber_memfree (slot_data.al_appname);
00446        if (res == -1) { 
00447               close (info->al_fd);
00448               return ALOCK_UNSTABLE;
00449        }
00450 
00451        res = alock_release_lock (info->al_fd, 0);
00452        if (res == -1) { 
00453               close (info->al_fd);
00454               return ALOCK_UNSTABLE;
00455        }
00456        
00457        if (dirty_count) return ALOCK_RECOVER | nosave;
00458        return ALOCK_CLEAN | nosave;
00459 }
00460 
00461 int 
00462 alock_scan ( alock_info_t * info )
00463 {
00464        struct stat statbuf;
00465        alock_info_t scan_info;
00466        int res, max_slot;
00467        int dirty_count, live_count, nosave;
00468 
00469        assert (info != NULL);
00470 
00471        scan_info.al_fd = info->al_fd;
00472 
00473        res = alock_grab_lock (info->al_fd, 0);
00474        if (res == -1) {
00475               close (info->al_fd);
00476               return ALOCK_UNSTABLE;
00477        }
00478 
00479        res = fstat (info->al_fd, &statbuf);
00480        if (res == -1) {
00481               close (info->al_fd);
00482               return ALOCK_UNSTABLE;
00483        }
00484 
00485        max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
00486        dirty_count = 0;
00487        live_count = 0;
00488        nosave = 0;
00489        for (scan_info.al_slot = 1; 
00490             scan_info.al_slot < max_slot;
00491             ++ scan_info.al_slot) {
00492               if (scan_info.al_slot != info->al_slot) {
00493                      res = alock_query_slot (&scan_info);
00494 
00495                      if (res & ALOCK_NOSAVE) {
00496                             nosave = ALOCK_NOSAVE;
00497                             res ^= ALOCK_NOSAVE;
00498                      }
00499 
00500                      if (res == ALOCK_LOCKED) {
00501                             ++live_count;
00502                             
00503                      } else if (res == ALOCK_DIRTY) {
00504                             ++dirty_count;
00505 
00506                      } else if (res == -1) {
00507                             close (info->al_fd);
00508                             return ALOCK_UNSTABLE;
00509 
00510                      }
00511               }
00512        }
00513 
00514        res = alock_release_lock (info->al_fd, 0);
00515        if (res == -1) {
00516               close (info->al_fd);
00517               return ALOCK_UNSTABLE;
00518        }
00519 
00520        if (dirty_count) {
00521               if (live_count) {
00522                      close (info->al_fd);
00523                      return ALOCK_UNSTABLE;
00524               } else {
00525                      return ALOCK_RECOVER | nosave;
00526               }
00527        }
00528        
00529        return ALOCK_CLEAN | nosave;
00530 }
00531 
00532 int
00533 alock_close ( alock_info_t * info, int nosave )
00534 {
00535        alock_slot_t slot_data;
00536        int res;
00537 
00538        if ( !info->al_slot )
00539               return ALOCK_CLEAN;
00540 
00541        (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
00542 
00543        res = alock_grab_lock (info->al_fd, 0);
00544        if (res == -1) {
00545               close (info->al_fd);
00546               return ALOCK_UNSTABLE;
00547        }
00548 
00549        /* mark our slot as clean */
00550        res = alock_read_slot (info, &slot_data);
00551        if (res == -1) {
00552               close (info->al_fd);
00553               if (slot_data.al_appname != NULL) 
00554                      ber_memfree (slot_data.al_appname);
00555               return ALOCK_UNSTABLE;
00556        }
00557        slot_data.al_lock = ALOCK_UNLOCKED;
00558        if ( nosave )
00559               slot_data.al_lock |= ALOCK_NOSAVE;
00560        res = alock_write_slot (info, &slot_data);
00561        if (res == -1) {
00562               close (info->al_fd);
00563               if (slot_data.al_appname != NULL) 
00564                      ber_memfree (slot_data.al_appname);
00565               return ALOCK_UNSTABLE;
00566        }
00567        if (slot_data.al_appname != NULL) {
00568               ber_memfree (slot_data.al_appname);
00569               slot_data.al_appname = NULL;
00570        }
00571 
00572        res = alock_release_lock (info->al_fd, info->al_slot);
00573        if (res == -1) {
00574               close (info->al_fd);
00575               return ALOCK_UNSTABLE;
00576        }
00577        res = alock_release_lock (info->al_fd, 0);
00578        if (res == -1) {
00579               close (info->al_fd);
00580               return ALOCK_UNSTABLE;
00581        }
00582 
00583        res = close (info->al_fd);
00584        if (res == -1) return ALOCK_UNSTABLE;
00585        
00586        return ALOCK_CLEAN;
00587 }
00588 
00589 int 
00590 alock_recover ( alock_info_t * info )
00591 {
00592        struct stat statbuf;
00593        alock_slot_t slot_data;
00594        alock_info_t scan_info;
00595        int res, max_slot;
00596 
00597        assert (info != NULL);
00598 
00599        scan_info.al_fd = info->al_fd;
00600 
00601        (void) memset ((void *) &slot_data, 0, sizeof(alock_slot_t));
00602 
00603        res = alock_grab_lock (info->al_fd, 0);
00604        if (res == -1) {
00605               close (info->al_fd);
00606               return ALOCK_UNSTABLE;
00607        }
00608 
00609        res = fstat (info->al_fd, &statbuf);
00610        if (res == -1) {
00611               close (info->al_fd);
00612               return ALOCK_UNSTABLE;
00613        }
00614 
00615        max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE;
00616        for (scan_info.al_slot = 1; 
00617             scan_info.al_slot < max_slot;
00618             ++ scan_info.al_slot) {
00619               if (scan_info.al_slot != info->al_slot) {
00620                      res = alock_query_slot (&scan_info) & ~ALOCK_NOSAVE;
00621 
00622                      if (res == ALOCK_LOCKED
00623                          || res == ALOCK_UNIQUE) {
00624                             /* recovery attempt on an active db? */
00625                             close (info->al_fd);
00626                             return ALOCK_UNSTABLE;
00627                             
00628                      } else if (res == ALOCK_DIRTY) {
00629                             /* mark it clean */
00630                             res = alock_read_slot (&scan_info, &slot_data);
00631                             if (res == -1) {
00632                                    close (info->al_fd);
00633                                    return ALOCK_UNSTABLE;
00634                             }
00635                             slot_data.al_lock = ALOCK_UNLOCKED;
00636                             res = alock_write_slot (&scan_info, &slot_data);
00637                             if (res == -1) {
00638                                    close (info->al_fd);
00639                                    if (slot_data.al_appname != NULL) 
00640                                           ber_memfree (slot_data.al_appname);
00641                                    return ALOCK_UNSTABLE;
00642                             }
00643                             if (slot_data.al_appname != NULL) {
00644                                    ber_memfree (slot_data.al_appname);
00645                                    slot_data.al_appname = NULL;
00646                             }
00647                             
00648                      } else if (res == -1) {
00649                             close (info->al_fd);
00650                             return ALOCK_UNSTABLE;
00651 
00652                      }
00653               }
00654        }
00655 
00656        res = alock_release_lock (info->al_fd, 0);
00657        if (res == -1) {
00658               close (info->al_fd);
00659               return ALOCK_UNSTABLE;
00660        }
00661 
00662        return ALOCK_CLEAN;
00663 }
00664 
00665 #endif /* SLAPD_BDB || SLAPD_HDB */