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