From: Howard Chu Date: Tue, 21 Aug 2012 11:55:03 +0000 (-0700) Subject: ITS#7361 fix Windows lock handling X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=9d5af712141c0d9db0a6609c7f2d9f69ef73377a;p=openldap ITS#7361 fix Windows lock handling --- diff --git a/servers/slapd/alock.c b/servers/slapd/alock.c index 342182f770..f293dec5cf 100644 --- a/servers/slapd/alock.c +++ b/servers/slapd/alock.c @@ -40,9 +40,9 @@ #include #ifdef _WIN32 +#include #include #include -#include #endif @@ -66,19 +66,15 @@ alock_grab_lock ( int fd, int slot ) res = fcntl (fd, F_SETLKW, &lock_info); #elif defined( _WIN32 ) - if( _lseek( fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET ) < 0 ) - return -1; - /* - * _lock will try for the lock once per second, returning EDEADLOCK - * after ten tries. We just loop until we either get the lock - * or some other error is returned. - */ - while((res = _locking( fd, _LK_LOCK, ALOCK_SLOT_SIZE )) < 0 ) { - if( errno != EDEADLOCK ) - break; - } + OVERLAPPED ov; + HANDLE hh = _get_osfhandle ( fd ); + ov.hEvent = 0; + ov.Offset = ALOCK_SLOT_SIZE*slot; + ov.OffsetHigh = 0; + res = LockFileEx( hh, LOCKFILE_EXCLUSIVE_LOCK, 0, + ALOCK_SLOT_SIZE, 0, &ov ) ? 0 : -1; #else -# error alock needs lockf, fcntl, or _locking +# error alock needs lockf, fcntl, or LockFile[Ex] #endif if (res == -1) { assert (errno != EDEADLK); @@ -109,12 +105,54 @@ alock_release_lock ( int fd, int slot ) res = fcntl (fd, F_SETLKW, &lock_info); if (res == -1) return -1; #elif defined( _WIN32 ) - res = _lseek (fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET); - if (res == -1) return -1; - res = _locking( fd, _LK_UNLCK, ALOCK_SLOT_SIZE ); + HANDLE hh = _get_osfhandle ( fd ); + if ( !UnlockFile ( hh, ALOCK_SLOT_SIZE*slot, 0, + ALOCK_SLOT_SIZE, 0 )) + return -1; +#else +# error alock needs lockf, fcntl, or LockFile[Ex] +#endif + + return 0; +} + +static int +alock_share_lock ( int fd, int slot ) +{ + int res; + +#if defined( HAVE_LOCKF ) + res = 0; /* lockf has no shared locks */ +#elif defined ( HAVE_FCNTL ) + struct flock lock_info; + (void) memset ((void *) &lock_info, 0, sizeof (struct flock)); + + /* The shared lock replaces the existing lock */ + lock_info.l_type = F_RDLCK; + lock_info.l_whence = SEEK_SET; + lock_info.l_start = (off_t) (ALOCK_SLOT_SIZE * slot); + lock_info.l_len = (off_t) ALOCK_SLOT_SIZE; + + res = fcntl (fd, F_SETLK, &lock_info); if (res == -1) return -1; +#elif defined( _WIN32 ) + OVERLAPPED ov; + HANDLE hh = _get_osfhandle ( fd ); + + /* Windows locks are mandatory, not advisory. + * We must downgrade the lock to allow future + * callers to read the slot data. + * + * First acquire a shared lock. Unlock will + * release the existing exclusive lock. + */ + ov.hEvent = 0; + ov.Offset = ALOCK_SLOT_SIZE*slot; + ov.OffsetHigh = 0; + LockFileEx (hh, 0, 0, ALOCK_SLOT_SIZE, 0, &ov); + UnlockFile (hh, ALOCK_SLOT_SIZE*slot, 0, ALOCK_SLOT_SIZE, 0); #else -# error alock needs lockf, fcntl, or _locking +# error alock needs lockf, fcntl, or LockFile[Ex] #endif return 0; @@ -151,19 +189,22 @@ alock_test_lock ( int fd, int slot ) if (lock_info.l_type != F_UNLCK) return ALOCK_LOCKED; #elif defined( _WIN32 ) - res = _lseek (fd, (ALOCK_SLOT_SIZE * slot), SEEK_SET); - if (res == -1) return -1; - res = _locking( fd, _LK_NBLCK, ALOCK_SLOT_SIZE ); - _locking( fd, _LK_UNLCK, ALOCK_SLOT_SIZE ); - if (res == -1) { - if( errno == EACCES ) { - return ALOCK_LOCKED; - } else { - return -1; - } + OVERLAPPED ov; + HANDLE hh = _get_osfhandle ( fd ); + ov.hEvent = 0; + ov.Offset = ALOCK_SLOT_SIZE*slot; + ov.OffsetHigh = 0; + if( !LockFileEx( hh, + LOCKFILE_EXCLUSIVE_LOCK|LOCKFILE_FAIL_IMMEDIATELY, 0, + ALOCK_SLOT_SIZE, 0, &ov )) { + int err = GetLastError(); + if ( err == ERROR_LOCK_VIOLATION ) + return ALOCK_LOCKED; + else + return -1; } #else -# error alock needs lockf, fcntl, or _locking +# error alock needs lockf, fcntl, or LockFile #endif return 0; @@ -364,7 +405,15 @@ alock_open ( alock_info_t * info, } ptr = lutil_strcopy(filename, envdir); lutil_strcopy(ptr, "/alock"); +#ifdef _WIN32 + { HANDLE handle = CreateFile (filename, GENERIC_READ|GENERIC_WRITE, + FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + info->al_fd = _open_osfhandle (handle, 0); + } +#else info->al_fd = open (filename, O_CREAT|O_RDWR, 0666); +#endif ber_memfree (filename); if (info->al_fd < 0) { ber_memfree (slot_data.al_appname); @@ -447,6 +496,7 @@ alock_open ( alock_info_t * info, close (info->al_fd); return ALOCK_UNSTABLE; } + alock_share_lock (info->al_fd, info->al_slot); res = alock_release_lock (info->al_fd, 0); if (res == -1) { @@ -542,6 +592,12 @@ alock_close ( alock_info_t * info, int nosave ) res = alock_grab_lock (info->al_fd, 0); if (res == -1) { +fail: + /* Windows doesn't clean up locks immediately when a process exits. + * Make sure we release our locks, to prevent stale locks from + * hanging around. + */ + alock_release_lock (info->al_fd, 0); close (info->al_fd); return ALOCK_UNSTABLE; } @@ -549,31 +605,29 @@ alock_close ( alock_info_t * info, int nosave ) /* mark our slot as clean */ res = alock_read_slot (info, &slot_data); if (res == -1) { - close (info->al_fd); if (slot_data.al_appname != NULL) ber_memfree (slot_data.al_appname); - return ALOCK_UNSTABLE; + goto fail; } slot_data.al_lock = ALOCK_UNLOCKED; if ( nosave ) slot_data.al_lock |= ALOCK_NOSAVE; + /* since we have slot 0 locked, we don't need our slot lock */ + res = alock_release_lock (info->al_fd, info->al_slot); + if (res == -1) { + goto fail; + } res = alock_write_slot (info, &slot_data); if (res == -1) { - close (info->al_fd); if (slot_data.al_appname != NULL) ber_memfree (slot_data.al_appname); - return ALOCK_UNSTABLE; + goto fail; } if (slot_data.al_appname != NULL) { ber_memfree (slot_data.al_appname); slot_data.al_appname = NULL; } - res = alock_release_lock (info->al_fd, info->al_slot); - if (res == -1) { - close (info->al_fd); - return ALOCK_UNSTABLE; - } res = alock_release_lock (info->al_fd, 0); if (res == -1) { close (info->al_fd); @@ -602,14 +656,12 @@ alock_recover ( alock_info_t * info ) res = alock_grab_lock (info->al_fd, 0); if (res == -1) { - close (info->al_fd); - return ALOCK_UNSTABLE; + goto fail; } res = fstat (info->al_fd, &statbuf); if (res == -1) { - close (info->al_fd); - return ALOCK_UNSTABLE; + goto fail; } max_slot = (statbuf.st_size + ALOCK_SLOT_SIZE - 1) / ALOCK_SLOT_SIZE; @@ -622,23 +674,20 @@ alock_recover ( alock_info_t * info ) if (res == ALOCK_LOCKED || res == ALOCK_UNIQUE) { /* recovery attempt on an active db? */ - close (info->al_fd); - return ALOCK_UNSTABLE; + goto fail; } else if (res == ALOCK_DIRTY) { /* mark it clean */ res = alock_read_slot (&scan_info, &slot_data); if (res == -1) { - close (info->al_fd); - return ALOCK_UNSTABLE; + goto fail; } slot_data.al_lock = ALOCK_UNLOCKED; res = alock_write_slot (&scan_info, &slot_data); if (res == -1) { - close (info->al_fd); if (slot_data.al_appname != NULL) ber_memfree (slot_data.al_appname); - return ALOCK_UNSTABLE; + goto fail; } if (slot_data.al_appname != NULL) { ber_memfree (slot_data.al_appname); @@ -646,8 +695,7 @@ alock_recover ( alock_info_t * info ) } } else if (res == -1) { - close (info->al_fd); - return ALOCK_UNSTABLE; + goto fail; } } @@ -660,6 +708,11 @@ alock_recover ( alock_info_t * info ) } return ALOCK_CLEAN; + +fail: + alock_release_lock (info->al_fd, 0); + close (info->al_fd); + return ALOCK_UNSTABLE; } #endif /* SLAPD_BDB || SLAPD_HDB */