4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
25 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
28 * Copyright 2015 RackTop Systems.
29 * Copyright (c) 2016 by Delphix. All rights reserved.
35 #include <stdlib.h> /* srand48() */
38 #include "db_headers.h"
40 #include "db_pickle.h" /* for dump and load */
44 #include "ldap_parse.h"
45 #include "ldap_util.h"
48 #include "nis_hashitem.h"
49 #include "nisdb_ldap.h"
50 #include "nis_parse_ldap_conf.h"
52 static time_t maxTimeT
;
55 * Find the largest (positive) value of time_t.
57 * If time_t is unsigned, the largest possible value is just ~0.
58 * However, if it's signed, then ~0 is negative. Since lint (for
59 * sure), and perhaps the compiler too, dislike comparing an
60 * unsigned quantity to see if it's less than zero, we compare
61 * to one instead. If negative, the largest possible value is
62 * th inverse of 2**(N-1), where N is the number of bits in a
69 unsigned char b
[sizeof (time_t)];
72 /* Compute ~0 for an unknown length integer */
73 for (i
= 0; i
< sizeof (time_t); i
++) {
76 /* Set maxTimeT to ~0 of appropriate length */
77 (void) memcpy(&maxTimeT
, b
, sizeof (time_t));
80 maxTimeT
= ~(1L<<((8*sizeof (maxTimeT
))-1));
82 #pragma init(__setMaxTimeT)
85 /* How much to grow table by */
86 #define DB_TABLE_GROWTH_INCREMENT 1024
88 /* 0'th not used; might be confusing. */
89 #define DB_TABLE_START 1
91 /* prevents wrap around numbers from being passed */
92 #define CALLOC_LIMIT 536870911
94 /* Initial table sizes to use before using 1K increments. */
95 /* This helps conserve memory usage when there are lots of small tables. */
96 static int tabsizes
[] = {
100 DB_TABLE_GROWTH_INCREMENT
,
104 /* Returns the next size to use for table */
106 get_new_table_size(long unsigned oldsize
)
108 long unsigned newsize
= 0, n
;
110 newsize
= tabsizes
[0];
112 for (n
= 0; newsize
= tabsizes
[n
++]; )
113 if (oldsize
== newsize
) {
114 newsize
= tabsizes
[n
]; /* get next size */
118 newsize
= oldsize
+ DB_TABLE_GROWTH_INCREMENT
;
125 db_free_list::~db_free_list()
127 WRITELOCKV(this, "w db_free_list::~db_free_list");
128 reset(); /* free list entries */
129 DESTROYRW(free_list
);
133 db_free_list::reset()
135 db_free_entry
*current
, *nextentry
;
137 WRITELOCKV(this, "w db_free_list::reset");
138 for (current
= head
; current
!= NULL
; ) {
139 nextentry
= current
->next
;
145 WRITEUNLOCKV(this, "wu db_free_list::reset");
148 /* Returns the location of a free entry, or 0, if there aren't any. */
152 WRITELOCK(this, 0, "w db_free_list::pop");
154 WRITEUNLOCK(this, 0, "wu db_free_list::pop");
157 db_free_entry
* old_head
= head
;
158 entryp found
= head
->where
;
162 WRITEUNLOCK(this, found
, "wu db_free_list::pop");
167 * Adds given location to the free list.
168 * Returns TRUE if successful, FALSE otherwise (when out of memory).
171 db_free_list::push(entryp tabloc
)
173 db_free_entry
* newentry
= new db_free_entry
;
175 WRITELOCK(this, FALSE
, "w db_free_list::push");
176 if (newentry
== NULL
) {
177 WRITEUNLOCK(this, FALSE
, "wu db_free_list::push");
178 FATAL3("db_free_list::push: cannot allocation space",
179 DB_MEMORY_LIMIT
, FALSE
);
181 newentry
->where
= tabloc
;
182 newentry
->next
= head
;
185 WRITEUNLOCK(this, TRUE
, "wu db_free_list::push");
190 * Returns in a vector the information in the free list.
191 * Vector returned is of form: [n free cells][n1][n2][loc1], ..[locn].
192 * Leave the first 'n' cells free.
193 * n1 is the number of entries that should be in the freelist.
194 * n2 is the number of entries actually found in the freelist.
195 * [loc1...locn] are the entries. n2 <= n1 because we never count beyond n1.
196 * It is up to the caller to free the returned vector when it is through.
199 db_free_list::stats(int nslots
)
203 liststart
= nslots
, // start of freelist
204 listend
= nslots
+count
+2; // end of freelist
205 db_free_entry_p current
= head
;
207 READLOCK(this, NULL
, "r db_free_list::stats");
209 long *answer
= (long *)malloc((int)(listend
)*sizeof (long));
211 READUNLOCK(this, NULL
, "ru db_free_list::stats");
212 FATAL3("db_free_list::stats: cannot allocation space",
213 DB_MEMORY_LIMIT
, NULL
);
216 answer
[liststart
] = count
; /* size of freelist */
218 for (i
= liststart
+2; i
< listend
&& current
!= NULL
; i
++) {
219 answer
[i
] = current
->where
;
220 current
= current
->next
;
224 answer
[liststart
+1] = realcount
;
225 READUNLOCK(this, answer
, "ru db_free_list::stats");
230 /* Set default values for the mapping structure */
232 db_table::initMappingStruct(__nisdb_table_mapping_t
*m
) {
236 m
->initTtlLo
= (ldapDBTableMapping
.initTtlLo
> 0) ?
237 ldapDBTableMapping
.initTtlLo
: (3600-1800);
238 m
->initTtlHi
= (ldapDBTableMapping
.initTtlHi
> 0) ?
239 ldapDBTableMapping
.initTtlHi
: (3600+1800);
240 m
->ttl
= (ldapDBTableMapping
.ttl
> 0) ?
241 ldapDBTableMapping
.ttl
: 3600;
246 m
->retrieveError
= ldapDBTableMapping
.retrieveError
;
247 m
->retrieveErrorRetry
.attempts
=
248 ldapDBTableMapping
.retrieveErrorRetry
.attempts
;
249 m
->retrieveErrorRetry
.timeout
=
250 ldapDBTableMapping
.retrieveErrorRetry
.timeout
;
251 m
->storeError
= ldapDBTableMapping
.storeError
;
252 m
->storeErrorRetry
.attempts
=
253 ldapDBTableMapping
.storeErrorRetry
.attempts
;
254 m
->storeErrorRetry
.timeout
=
255 ldapDBTableMapping
.storeErrorRetry
.timeout
;
256 m
->storeErrorDisp
= ldapDBTableMapping
.storeErrorDisp
;
257 m
->refreshError
= ldapDBTableMapping
.refreshError
;
258 m
->refreshErrorRetry
.attempts
=
259 ldapDBTableMapping
.refreshErrorRetry
.attempts
;
260 m
->refreshErrorRetry
.timeout
=
261 ldapDBTableMapping
.refreshErrorRetry
.timeout
;
262 m
->matchFetch
= ldapDBTableMapping
.matchFetch
;
264 if (mapping
.expire
!= 0)
265 free(mapping
.expire
);
273 * The 'objType' field obviously indicates the type of object.
274 * However, we also use it to tell us if we've retrieved mapping
275 * data from LDAP or not; in the latter case, 'objType' is
276 * NIS_BOGUS_OBJ. For purposes of maintaining expiration times,
277 * we may need to know if the object is a table or a directory
278 * _before_ we've retrieved any mapping data. Hence the 'expireType'
279 * field, which starts as NIS_BOGUS_OBJ (meaning, don't know, assume
280 * directory for now), and later is set to NIS_DIRECTORY_OBJ
281 * (always keep expiration data, in case one of the dir entries
282 * is mapped) or NIS_TABLE_OBJ (only need expiration data if
283 * tha table is mapped).
285 m
->objType
= NIS_BOGUS_OBJ
;
286 m
->expireType
= NIS_BOGUS_OBJ
;
293 db_table::db_table_ldap_init(void) {
305 mapping
.isDeferredTable
= FALSE
;
306 (void) mutex_init(&mapping
.enumLock
, 0, 0);
308 mapping
.enumStat
= -1;
309 mapping
.enumDeferred
= 0;
310 mapping
.enumEntries
= 0;
311 mapping
.enumTime
= 0;
314 /* db_table constructor */
315 db_table::db_table() : freelist()
322 db_table_ldap_init();
323 initMappingStruct(&mapping
);
329 * db_table destructor:
330 * 1. Get rid of contents of freelist
331 * 2. delete all entries hanging off table
332 * 3. get rid of table itself
334 db_table::~db_table()
336 WRITELOCKV(this, "w db_table::~db_table");
341 /* reset size and pointers */
347 WRITELOCKV(this, "w db_table::reset");
350 /* Add sanity check in case of table corruption */
353 i
<= last_used
&& i
< table_size
&& done
< count
;
363 table_size
= last_used
= count
= 0;
365 sfree(mapping
.expire
);
366 mapping
.expire
= NULL
;
367 mapping
.objType
= NIS_BOGUS_OBJ
;
368 mapping
.expireType
= NIS_BOGUS_OBJ
;
369 sfree(mapping
.objName
);
371 /* Leave other values of the mapping structure unchanged */
374 sfree(enumIndex
.ptr
);
376 sfree(enumArray
.ptr
);
378 WRITEUNLOCKV(this, "wu db_table::reset");
382 db_table::allocateExpire(long oldSize
, long newSize
) {
385 newExpire
= (time_t *)realloc(mapping
.expire
,
386 newSize
* sizeof (mapping
.expire
[0]));
387 if (newExpire
!= NULL
) {
388 /* Initialize new portion */
389 (void) memset(&newExpire
[oldSize
], 0,
390 (newSize
-oldSize
) * sizeof (newExpire
[0]));
391 mapping
.expire
= newExpire
;
393 return (DB_MEMORY_LIMIT
);
400 db_table::allocateEnumArray(long oldSize
, long newSize
) {
401 entry_object
**newEnumArray
;
402 const char *myself
= "db_table::allocateEnumArray";
404 if (enumCount
.flag
> 0) {
405 if (enumIndex
.ptr
== 0) {
406 enumIndex
.ptr
= (entryp
*)am(myself
, enumCount
.flag
*
408 if (enumIndex
.ptr
== 0)
409 return (DB_MEMORY_LIMIT
);
412 newSize
= enumCount
.flag
;
414 newEnumArray
= (entry_object
**)realloc(enumArray
.ptr
,
415 newSize
* sizeof (entry_object
*));
416 if (newEnumArray
!= 0 && newSize
> oldSize
) {
417 (void) memcpy(&newEnumArray
[oldSize
], &tab
[oldSize
],
418 (newSize
-oldSize
) * sizeof (entry_object
*));
419 enumArray
.ptr
= newEnumArray
;
420 } else if (newEnumArray
== 0) {
421 return (DB_MEMORY_LIMIT
);
427 /* Expand the table. Fatal error if insufficient memory. */
431 WRITELOCKV(this, "w db_table::grow");
432 long oldsize
= table_size
;
433 entry_object_p
*oldtab
= tab
;
436 table_size
= get_new_table_size(oldsize
);
439 fprintf(stderr
, "db_table GROWING to %d\n", table_size
);
442 if (table_size
> CALLOC_LIMIT
) {
443 table_size
= oldsize
;
444 WRITEUNLOCKV(this, "wu db_table::grow");
445 FATAL("db_table::grow: table size exceeds calloc limit",
449 // if ((tab = new entry_object_p[table_size]) == NULL)
450 if ((tab
= (entry_object_p
*)
451 calloc((unsigned int) table_size
,
452 sizeof (entry_object_p
))) == NULL
) {
453 tab
= oldtab
; // restore previous table info
454 table_size
= oldsize
;
455 WRITEUNLOCKV(this, "wu db_table::grow");
456 FATAL("db_table::grow: cannot allocate space", DB_MEMORY_LIMIT
);
460 * For directories, we may need the expire time array even if the
461 * directory itself isn't mapped. If the objType and expireType both
462 * are bogus, we don't know yet if this is a table or a directory,
463 * and must proceed accordingly.
465 if (mapping
.objType
== NIS_DIRECTORY_OBJ
||
466 mapping
.expireType
!= NIS_TABLE_OBJ
||
468 db_status stat
= allocateExpire(oldsize
, table_size
);
469 if (stat
!= DB_SUCCESS
) {
472 table_size
= oldsize
;
473 WRITEUNLOCKV(this, "wu db_table::grow expire");
475 "db_table::grow: cannot allocate space for expire", stat
);
479 if (oldtab
!= NULL
) {
480 for (i
= 0; i
< oldsize
; i
++) { // transfer old to new
487 db_status stat
= allocateEnumArray(oldsize
, table_size
);
488 if (stat
!= DB_SUCCESS
) {
491 table_size
= oldsize
;
492 WRITEUNLOCKV(this, "wu db_table::grow enumArray");
494 "db_table::grow: cannot allocate space for enumArray", stat
);
498 WRITEUNLOCKV(this, "wu db_table::grow");
502 * Return the first entry in table, also return its position in
503 * 'where'. Return NULL in both if no next entry is found.
506 db_table::first_entry(entryp
* where
)
509 if (count
== 0 || tab
== NULL
) { /* empty table */
514 for (i
= DB_TABLE_START
;
515 i
< table_size
&& i
<= last_used
; i
++) {
516 if (tab
[i
] != NULL
) {
527 * Return the next entry in table from 'prev', also return its position in
528 * 'newentry'. Return NULL in both if no next entry is found.
531 db_table::next_entry(entryp prev
, entryp
* newentry
)
536 if (prev
>= table_size
|| tab
== NULL
|| tab
[prev
] == NULL
)
538 for (i
= prev
+1; i
< table_size
&& i
<= last_used
; i
++) {
539 if (tab
[i
] != NULL
) {
548 /* Return entry at location 'where', NULL if location is invalid. */
550 db_table::get_entry(entryp where
)
553 if (where
< table_size
&& tab
!= NULL
&& tab
[where
] != NULL
)
560 db_table::setEntryExp(entryp where
, entry_obj
*obj
, int initialLoad
) {
562 const char *myself
= "db_table::setEntryExp";
565 * If we don't know what type of object this is yet, we
566 * can find out now. If it's a directory, the pseudo-object
567 * in column zero will have the type "IN_DIRECTORY";
568 * otherwise, it's a table object.
570 if (mapping
.expireType
== NIS_BOGUS_OBJ
) {
572 if (obj
->en_type
!= 0 &&
573 strcmp(obj
->en_type
, "IN_DIRECTORY") == 0) {
574 mapping
.expireType
= NIS_DIRECTORY_OBJ
;
576 mapping
.expireType
= NIS_TABLE_OBJ
;
577 if (!mapping
.fromLDAP
) {
578 free(mapping
.expire
);
585 /* Set the entry TTL */
586 if (mapping
.expire
!= NULL
) {
590 (void) gettimeofday(&now
, NULL
);
591 if (mapping
.expireType
== NIS_TABLE_OBJ
) {
592 lo
= mapping
.initTtlLo
;
593 hi
= mapping
.initTtlHi
;
595 /* TTL == 0 means always expired */
599 __nis_table_mapping_t
*t
= 0;
601 o
= unmakePseudoEntryObj(obj
, 0);
603 __nis_buffer_t b
= {0, 0};
605 bp2buf(myself
, &b
, "%s.%s",
606 o
->zo_name
, o
->zo_domain
);
607 t
= getObjMapping(b
.buf
, 0, 1, 0, 0);
609 nis_destroy_object(o
);
616 /* TTL == 0 means always expired */
621 * No expiration time initialization
622 * data. Cook up values that will
623 * result in mapping.expire[where]
626 hi
= lo
= ttl
= maxTimeT
- now
.tv_sec
;
631 int interval
= hi
- lo
+ 1;
633 mapping
.expire
[where
] = now
.tv_sec
+ lo
;
636 mapping
.expire
[where
] = now
.tv_sec
+
637 (lrand48() % interval
);
639 if (mapping
.enumExpire
== 0 ||
640 mapping
.expire
[where
] <
642 mapping
.enumExpire
= mapping
.expire
[where
];
644 mapping
.expire
[where
] = now
.tv_sec
+ ttl
;
650 * Add given entry to table in first available slot (either look in freelist
651 * or add to end of table) and return the the position of where the record
652 * is placed. 'count' is incremented if entry is added. Table may grow
653 * as a side-effect of the addition. Copy is made of input.
656 db_table::add_entry(entry_object
*obj
, int initialLoad
) {
658 * We're returning an index of the table array, so the caller
659 * should hold a lock until done with the index. To save us
660 * the bother of upgrading to a write lock, it might as well
661 * be a write lock to begin with.
664 entryp where
= freelist
.pop();
665 if (where
== 0) { /* empty freelist */
666 if (last_used
>= (table_size
-1)) /* full (> is for 0) */
672 setEntryExp(where
, obj
, initialLoad
);
676 tab
[where
] = new_entry(obj
);
684 * Replaces object at specified location by given entry.
685 * Returns TRUE if replacement successful; FALSE otherwise.
686 * There must something already at the specified location, otherwise,
687 * replacement fails. Copy is not made of the input.
688 * The pre-existing entry is freed.
691 db_table::replace_entry(entryp where
, entry_object
* obj
)
694 if (where
< DB_TABLE_START
|| where
>= table_size
||
695 tab
== NULL
|| tab
[where
] == NULL
)
697 /* (Re-)set the entry TTL */
698 setEntryExp(where
, obj
, 0);
702 free_entry(tab
[where
]);
708 * Deletes entry at specified location. Returns TRUE if location is valid;
709 * FALSE if location is invalid, or the freed location cannot be added to
710 * the freelist. 'count' is decremented if the deletion occurs. The object
711 * at that location is freed.
714 db_table::delete_entry(entryp where
)
719 if (where
< DB_TABLE_START
|| where
>= table_size
||
720 tab
== NULL
|| tab
[where
] == NULL
)
722 if (mapping
.expire
!= NULL
) {
723 mapping
.expire
[where
] = 0;
727 free_entry(tab
[where
]);
728 tab
[where
] = NULL
; /* very important to set it to null */
730 if (where
== last_used
) { /* simple case, deleting from end */
734 return (freelist
.push(where
));
740 * Returns statistics of table.
741 * [vector_size][table_size][last_used][count][freelist].
742 * It is up to the caller to free the returned vector when it is through.
743 * The free list is included if 'fl' is TRUE.
746 db_table::stats(bool_t include_freelist
)
750 READLOCK(this, NULL
, "r db_table::stats");
751 if (include_freelist
)
752 answer
= freelist
.stats(3);
754 answer
= (long *)malloc(3*sizeof (long));
758 answer
[0] = table_size
;
759 answer
[1] = last_used
;
762 READUNLOCK(this, answer
, "ru db_table::stats");
767 db_table::configure(char *tablePath
) {
770 const char *myself
= "db_table::configure";
772 (void) gettimeofday(&now
, NULL
);
774 WRITELOCK(this, FALSE
, "db_table::configure w");
776 /* (Re-)initialize from global info */
777 initMappingStruct(&mapping
);
779 /* Retrieve table mapping for this table */
780 mapping
.tm
= (__nis_table_mapping_t
*)__nis_find_item_mt(
781 tablePath
, &ldapMappingList
, 0, 0);
782 if (mapping
.tm
!= 0) {
783 __nis_object_dn_t
*odn
= mapping
.tm
->objectDN
;
786 * The mapping.fromLDAP and mapping.toLDAP fields serve as
787 * quick-references that tell us if mapping is enabled.
788 * Hence, initialize them appropriately from the table
791 while (odn
!= 0 && (!mapping
.fromLDAP
|| !mapping
.toLDAP
)) {
792 if (odn
->read
.scope
!= LDAP_SCOPE_UNKNOWN
)
793 mapping
.fromLDAP
= TRUE
;
794 if (odn
->write
.scope
!= LDAP_SCOPE_UNKNOWN
)
795 mapping
.toLDAP
= TRUE
;
796 odn
= (__nis_object_dn_t
*)odn
->next
;
799 /* Set the timeout values */
800 mapping
.initTtlLo
= mapping
.tm
->initTtlLo
;
801 mapping
.initTtlHi
= mapping
.tm
->initTtlHi
;
802 mapping
.ttl
= mapping
.tm
->ttl
;
804 mapping
.objName
= sdup(myself
, T
, mapping
.tm
->objName
);
805 if (mapping
.objName
== 0 && mapping
.tm
->objName
!= 0) {
806 WRITEUNLOCK(this, FALSE
,
807 "db_table::configure wu objName");
808 FATAL3("db_table::configure objName",
809 DB_MEMORY_LIMIT
, FALSE
);
814 * In order to initialize the expiration times, we need to know
815 * if 'this' represents a table or a directory. To that end, we
816 * find an entry in the table, and invoke setEntryExp() on it.
817 * As a side effect, setEntryExp() will examine the pseudo-object
818 * in the entry, and set the expireType accordingly.
821 for (i
= 0; i
<= last_used
; i
++) {
822 if (tab
[i
] != NULL
) {
823 setEntryExp(i
, tab
[i
], 1);
830 * If mapping from an LDAP repository, make sure we have the
831 * expiration time array.
833 if ((mapping
.expireType
!= NIS_TABLE_OBJ
|| mapping
.fromLDAP
) &&
834 mapping
.expire
== NULL
&& table_size
> 0 && tab
!= 0) {
835 db_status stat
= allocateExpire(0, table_size
);
836 if (stat
!= DB_SUCCESS
) {
837 WRITEUNLOCK(this, FALSE
,
838 "db_table::configure wu expire");
839 FATAL3("db_table::configure expire",
842 } else if (mapping
.expireType
== NIS_TABLE_OBJ
&& !mapping
.fromLDAP
&&
843 mapping
.expire
!= NULL
) {
844 /* Not using expiration times */
845 free(mapping
.expire
);
846 mapping
.expire
= NULL
;
850 * Set initial expire times for entries that don't already have one.
851 * Establish the enumeration expiration time to be the minimum of
852 * all expiration times in the table, though no larger than current
853 * time plus initTtlHi.
855 if (mapping
.expire
!= NULL
) {
856 int interval
= mapping
.initTtlHi
- mapping
.initTtlLo
+ 1;
857 time_t enumXp
= now
.tv_sec
+ mapping
.initTtlHi
;
861 for (i
= 0; i
<= last_used
; i
++) {
862 if (tab
[i
] != NULL
&& mapping
.expire
[i
] == 0) {
863 if (mapping
.expireType
== NIS_TABLE_OBJ
) {
867 (lrand48() % interval
);
873 setEntryExp(i
, tab
[i
], 1);
876 if (enumXp
> mapping
.expire
[i
])
877 enumXp
= mapping
.expire
[i
];
879 mapping
.enumExpire
= enumXp
;
882 WRITEUNLOCK(this, FALSE
, "db_table::configure wu");
887 /* Return TRUE if the entry at 'loc' hasn't expired */
889 db_table::cacheValid(entryp loc
) {
893 (void) gettimeofday(&now
, 0);
895 READLOCK(this, FALSE
, "db_table::cacheValid r");
897 if (loc
< 0 || loc
>= table_size
|| tab
== 0 || tab
[loc
] == 0)
899 else if (mapping
.expire
== 0 || mapping
.expire
[loc
] >= now
.tv_sec
)
904 READUNLOCK(this, ret
, "db_table::cacheValid ru");
910 * If the supplied object has the same content as the one at 'loc',
911 * update the expiration time for the latter, and return TRUE.
914 db_table::dupEntry(entry_object
*obj
, entryp loc
) {
915 if (obj
== 0 || loc
< 0 || loc
>= table_size
|| tab
== 0 ||
919 if (sameEntry(obj
, tab
[loc
])) {
920 setEntryExp(loc
, tab
[loc
], 0);
922 if (enumMode
.flag
> 0)
931 * If enumeration mode is enabled, we keep a shadow array that initially
932 * starts out the same as 'tab'. Any update activity (add, remove, replace,
933 * or update timestamp) for an entry in the table means we delete the shadow
934 * array pointer. When ending enumeration mode, we return the shadow array.
935 * Any non-NULL entries in the array have not been updated since the start
938 * The indended use is for enumeration of an LDAP container, where we
939 * will update all entries that currently exist in LDAP. The entries we
940 * don't update are those that don't exist in LDAP, and thus should be
943 * Note that any LDAP query strictly speaking can be a partial enumeration
944 * (i.e., return more than one match). Since the query might also have
945 * matched multiple local DB entries, we need to do the same work as for
946 * enumeration for any query. In order to avoid having to work on the
947 * whole 'tab' array for simple queries (which we expect usually will
948 * match just one or at most a few entries), we have a "reduced" enum mode,
949 * where the caller supplies a count of the number of DB entries (derived
950 * from db_mindex::satisfy_query() or similar), and then uses enumSetup()
951 * to specify which 'tab' entries we're interested in.
954 db_table::setEnumMode(long enumNum
) {
955 const char *myself
= "setEnumMode";
958 if (enumMode
.flag
== 1) {
963 else if (enumNum
>= table_size
)
964 enumNum
= table_size
;
966 enumCount
.flag
= enumNum
;
968 stat
= allocateEnumArray(0, table_size
);
970 if (stat
!= DB_SUCCESS
) {
973 logmsg(MSG_NOTIMECHECK
, LOG_ERR
,
974 "%s: No memory for enum check array; entry removal disabled",
981 db_table::clearEnumMode(void) {
982 if (enumMode
.flag
> 0) {
984 if (enumMode
.flag
== 0) {
985 sfree(enumArray
.ptr
);
987 if (enumCount
.flag
> 0) {
988 sfree(enumIndex
.ptr
);
997 db_table::endEnumMode(long *numEa
) {
998 if (enumMode
.flag
> 0) {
1000 if (enumMode
.flag
== 0) {
1001 entry_obj
**ea
= (entry_object
**)enumArray
.ptr
;
1006 if (enumCount
.flag
> 0) {
1007 nea
= enumCount
.flag
;
1009 sfree(enumIndex
.ptr
);
1029 * Set the appropriate entry in the enum array to NULL.
1032 db_table::enumTouch(entryp loc
) {
1033 if (loc
< 0 || loc
>= table_size
)
1036 if (enumMode
.flag
> 0) {
1037 if (enumCount
.flag
< 1) {
1038 ((entry_object
**)enumArray
.ptr
)[loc
] = 0;
1042 for (i
= 0; i
< enumCount
.flag
; i
++) {
1043 if (loc
== ((entryp
*)enumIndex
.ptr
)[i
]) {
1044 ((entry_object
**)enumArray
.ptr
)[i
] = 0;
1053 * Add the entry indicated by 'loc' to the enumIndex array, at 'index'.
1056 db_table::enumSetup(entryp loc
, long index
) {
1057 if (enumMode
.flag
== 0 || loc
< 0 || loc
>= table_size
||
1058 index
< 0 || index
>= enumCount
.flag
)
1061 ((entryp
*)enumIndex
.ptr
)[index
] = loc
;
1062 ((entry_object
**)enumArray
.ptr
)[index
] = tab
[loc
];
1066 * Touch, i.e., update the expiration time for the entry. Also, if enum
1067 * mode is in effect, mark the entry used for enum purposes.
1070 db_table::touchEntry(entryp loc
) {
1071 if (loc
< 0 || loc
>= table_size
|| tab
== 0 || tab
[loc
] == 0)
1074 setEntryExp(loc
, tab
[loc
], 0);
1079 /* ************************* pickle_table ********************* */
1080 /* Does the actual writing to/from file specific for db_table structure. */
1082 * This was a static earlier with the func name being transfer_aux. The
1083 * backup and restore project needed this to copy files over.
1086 transfer_aux_table(XDR
* x
, pptr dp
)
1088 return (xdr_db_table(x
, (db_table
*) dp
));
1091 class pickle_table
: public pickle_file
{
1093 pickle_table(char *f
, pickle_mode m
) : pickle_file(f
, m
) {}
1095 /* Transfers db_table structure pointed to by dp to/from file. */
1096 int transfer(db_table
* dp
)
1097 { return (pickle_file::transfer((pptr
) dp
, &transfer_aux_table
)); }
1101 * Writes the contents of table, including the all the entries, into the
1102 * specified file in XDR format. May need to change this to use APPEND
1106 db_table::dump(char *file
)
1109 READLOCK(this, -1, "r db_table::dump");
1110 pickle_table
f(file
, PICKLE_WRITE
); /* may need to use APPEND mode */
1111 int status
= f
.transfer(this);
1117 READUNLOCK(this, ret
, "ru db_table::dump");
1120 /* Constructor that loads in the table from the given file */
1121 db_table::db_table(char *file
) : freelist()
1123 pickle_table
f(file
, PICKLE_READ
);
1125 table_size
= last_used
= count
= 0;
1128 if (f
.transfer(this) < 0) {
1129 /* fell through, something went wrong, initialize to null */
1131 table_size
= last_used
= count
= 0;
1135 db_table_ldap_init();
1136 initMappingStruct(&mapping
);
1139 /* Returns whether location is valid. */
1140 bool_t
db_table::entry_exists_p(entryp i
) {
1142 READLOCK(this, FALSE
, "r db_table::entry_exists_p");
1143 if (tab
!= NULL
&& i
< table_size
)
1144 ret
= tab
[i
] != NULL
;
1145 READUNLOCK(this, ret
, "ru db_table::entry_exists_p");
1149 /* Empty free list */
1150 void db_free_list::init() {
1151 WRITELOCKV(this, "w db_free_list::init");
1154 WRITEUNLOCKV(this, "wu db_free_list::init");