Print this page
5910 libnisdb won't build with modern GCC
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/lib/libnisdb/db_table.cc
+++ new/usr/src/lib/libnisdb/db_table.cc
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License, Version 1.0 only
6 6 * (the "License"). You may not use this file except in compliance
7 7 * with the License.
8 8 *
9 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 10 * or http://www.opensolaris.org/os/licensing.
11 11 * See the License for the specific language governing permissions
12 12 * and limitations under the License.
13 13 *
14 14 * When distributing Covered Code, include this CDDL HEADER in each
15 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 16 * If applicable, add the following below this CDDL HEADER, with the
↓ open down ↓ |
16 lines elided |
↑ open up ↑ |
17 17 * fields enclosed by brackets "[]" replaced with your own identifying
18 18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 19 *
20 20 * CDDL HEADER END
21 21 */
22 22 /*
23 23 * db_table.cc
24 24 *
25 25 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
26 26 * Use is subject to license terms.
27 + *
28 + * Copyright 2015 RackTop Systems.
27 29 */
28 30
29 31 #include <stdio.h>
30 32 #include <malloc.h>
31 33 #include <string.h>
32 34 #include <stdlib.h> /* srand48() */
33 35 #include <lber.h>
34 36 #include <ldap.h>
35 37 #include "db_headers.h"
36 38 #include "db_table.h"
37 39 #include "db_pickle.h" /* for dump and load */
38 40 #include "db_entry.h"
39 41 #include "nisdb_mt.h"
40 42
41 43 #include "ldap_parse.h"
42 44 #include "ldap_util.h"
43 45 #include "ldap_map.h"
44 46 #include "ldap_xdr.h"
45 47 #include "nis_hashitem.h"
46 48 #include "nisdb_ldap.h"
47 49 #include "nis_parse_ldap_conf.h"
48 50
49 51 static time_t maxTimeT;
50 52
51 53 /*
52 54 * Find the largest (positive) value of time_t.
53 55 *
54 56 * If time_t is unsigned, the largest possible value is just ~0.
55 57 * However, if it's signed, then ~0 is negative. Since lint (for
56 58 * sure), and perhaps the compiler too, dislike comparing an
57 59 * unsigned quantity to see if it's less than zero, we compare
58 60 * to one instead. If negative, the largest possible value is
59 61 * th inverse of 2**(N-1), where N is the number of bits in a
60 62 * time_t.
61 63 */
62 64 extern "C" {
63 65 static void
64 66 __setMaxTimeT(void)
65 67 {
66 68 unsigned char b[sizeof (time_t)];
↓ open down ↓ |
30 lines elided |
↑ open up ↑ |
67 69 int i;
68 70
69 71 /* Compute ~0 for an unknown length integer */
70 72 for (i = 0; i < sizeof (time_t); i++) {
71 73 b[i] = 0xff;
72 74 }
73 75 /* Set maxTimeT to ~0 of appropriate length */
74 76 (void) memcpy(&maxTimeT, b, sizeof (time_t));
75 77
76 78 if (maxTimeT < 1)
77 - maxTimeT = ~(1<<((8*sizeof (maxTimeT))-1));
79 + maxTimeT = ~(1L<<((8*sizeof (maxTimeT))-1));
78 80 }
79 81 #pragma init(__setMaxTimeT)
80 82 }
81 83
82 84 /* How much to grow table by */
83 85 #define DB_TABLE_GROWTH_INCREMENT 1024
84 86
85 87 /* 0'th not used; might be confusing. */
86 88 #define DB_TABLE_START 1
87 89
88 90 /* prevents wrap around numbers from being passed */
89 91 #define CALLOC_LIMIT 536870911
90 92
91 93 /* Initial table sizes to use before using 1K increments. */
92 94 /* This helps conserve memory usage when there are lots of small tables. */
93 95 static int tabsizes[] = {
94 96 16,
95 97 128,
96 98 512,
97 99 DB_TABLE_GROWTH_INCREMENT,
98 100 0
99 101 };
100 102
101 103 /* Returns the next size to use for table */
102 104 static long unsigned
103 105 get_new_table_size(long unsigned oldsize)
104 106 {
105 107 long unsigned newsize = 0, n;
106 108 if (oldsize == 0)
107 109 newsize = tabsizes[0];
108 110 else {
109 111 for (n = 0; newsize = tabsizes[n++]; )
110 112 if (oldsize == newsize) {
111 113 newsize = tabsizes[n]; /* get next size */
112 114 break;
113 115 }
114 116 if (newsize == 0)
115 117 newsize = oldsize + DB_TABLE_GROWTH_INCREMENT;
116 118 }
117 119 return (newsize);
118 120 }
119 121
120 122
121 123 /* destructor */
122 124 db_free_list::~db_free_list()
123 125 {
124 126 WRITELOCKV(this, "w db_free_list::~db_free_list");
125 127 reset(); /* free list entries */
126 128 DESTROYRW(free_list);
127 129 }
128 130
129 131 void
130 132 db_free_list::reset()
131 133 {
132 134 db_free_entry *current, *nextentry;
133 135
134 136 WRITELOCKV(this, "w db_free_list::reset");
135 137 for (current = head; current != NULL; ) {
136 138 nextentry = current->next;
137 139 delete current;
138 140 current = nextentry;
139 141 }
140 142 head = NULL;
141 143 count = 0;
142 144 WRITEUNLOCKV(this, "wu db_free_list::reset");
143 145 }
144 146
145 147 /* Returns the location of a free entry, or NULL, if there aren't any. */
146 148 entryp
147 149 db_free_list::pop()
148 150 {
149 151 WRITELOCK(this, NULL, "w db_free_list::pop");
150 152 if (head == NULL) {
151 153 WRITEUNLOCK(this, NULL, "wu db_free_list::pop");
152 154 return (NULL);
153 155 }
154 156 db_free_entry* old_head = head;
155 157 entryp found = head->where;
156 158 head = head->next;
157 159 delete old_head;
158 160 --count;
159 161 WRITEUNLOCK(this, found, "wu db_free_list::pop");
160 162 return (found);
161 163 }
162 164
163 165 /*
164 166 * Adds given location to the free list.
165 167 * Returns TRUE if successful, FALSE otherwise (when out of memory).
166 168 */
167 169 bool_t
168 170 db_free_list::push(entryp tabloc)
169 171 {
170 172 db_free_entry * newentry = new db_free_entry;
171 173
172 174 WRITELOCK(this, FALSE, "w db_free_list::push");
173 175 if (newentry == NULL) {
174 176 WRITEUNLOCK(this, FALSE, "wu db_free_list::push");
175 177 FATAL3("db_free_list::push: cannot allocation space",
176 178 DB_MEMORY_LIMIT, FALSE);
177 179 }
178 180 newentry->where = tabloc;
179 181 newentry->next = head;
180 182 head = newentry;
181 183 ++count;
182 184 WRITEUNLOCK(this, TRUE, "wu db_free_list::push");
183 185 return (TRUE);
184 186 }
185 187
186 188 /*
187 189 * Returns in a vector the information in the free list.
188 190 * Vector returned is of form: [n free cells][n1][n2][loc1], ..[locn].
189 191 * Leave the first 'n' cells free.
190 192 * n1 is the number of entries that should be in the freelist.
191 193 * n2 is the number of entries actually found in the freelist.
192 194 * [loc1...locn] are the entries. n2 <= n1 because we never count beyond n1.
193 195 * It is up to the caller to free the returned vector when he is through.
194 196 */
195 197 long *
196 198 db_free_list::stats(int nslots)
197 199 {
198 200 long realcount = 0,
199 201 i,
200 202 liststart = nslots, // start of freelist
201 203 listend = nslots+count+2; // end of freelist
202 204 db_free_entry_p current = head;
203 205
204 206 READLOCK(this, NULL, "r db_free_list::stats");
205 207
206 208 long *answer = (long *)malloc((int)(listend)*sizeof (long));
207 209 if (answer == 0) {
208 210 READUNLOCK(this, NULL, "ru db_free_list::stats");
209 211 FATAL3("db_free_list::stats: cannot allocation space",
210 212 DB_MEMORY_LIMIT, NULL);
211 213 }
212 214
213 215 answer[liststart] = count; /* size of freelist */
214 216
215 217 for (i = liststart+2; i < listend && current != NULL; i++) {
216 218 answer[i] = current->where;
217 219 current = current->next;
218 220 ++realcount;
219 221 }
220 222
221 223 answer[liststart+1] = realcount;
222 224 READUNLOCK(this, answer, "ru db_free_list::stats");
223 225 return (answer);
224 226 }
225 227
226 228
227 229 /* Set default values for the mapping structure */
228 230 void
229 231 db_table::initMappingStruct(__nisdb_table_mapping_t *m) {
230 232 if (m == 0)
231 233 return;
232 234
233 235 m->initTtlLo = (ldapDBTableMapping.initTtlLo > 0) ?
234 236 ldapDBTableMapping.initTtlLo : (3600-1800);
235 237 m->initTtlHi = (ldapDBTableMapping.initTtlHi > 0) ?
236 238 ldapDBTableMapping.initTtlHi : (3600+1800);
237 239 m->ttl = (ldapDBTableMapping.ttl > 0) ?
238 240 ldapDBTableMapping.ttl : 3600;
239 241 m->enumExpire = 0;
240 242 m->fromLDAP = FALSE;
241 243 m->toLDAP = FALSE;
242 244 m->isMaster = FALSE;
243 245 m->retrieveError = ldapDBTableMapping.retrieveError;
244 246 m->retrieveErrorRetry.attempts =
245 247 ldapDBTableMapping.retrieveErrorRetry.attempts;
246 248 m->retrieveErrorRetry.timeout =
247 249 ldapDBTableMapping.retrieveErrorRetry.timeout;
248 250 m->storeError = ldapDBTableMapping.storeError;
249 251 m->storeErrorRetry.attempts =
250 252 ldapDBTableMapping.storeErrorRetry.attempts;
251 253 m->storeErrorRetry.timeout =
252 254 ldapDBTableMapping.storeErrorRetry.timeout;
253 255 m->storeErrorDisp = ldapDBTableMapping.storeErrorDisp;
254 256 m->refreshError = ldapDBTableMapping.refreshError;
255 257 m->refreshErrorRetry.attempts =
256 258 ldapDBTableMapping.refreshErrorRetry.attempts;
257 259 m->refreshErrorRetry.timeout =
258 260 ldapDBTableMapping.refreshErrorRetry.timeout;
259 261 m->matchFetch = ldapDBTableMapping.matchFetch;
260 262
261 263 if (mapping.expire != 0)
262 264 free(mapping.expire);
263 265 m->expire = 0;
264 266
265 267 if (m->tm != 0)
266 268 free(m->tm);
267 269 m->tm = 0;
268 270
269 271 /*
270 272 * The 'objType' field obviously indicates the type of object.
271 273 * However, we also use it to tell us if we've retrieved mapping
272 274 * data from LDAP or not; in the latter case, 'objType' is
273 275 * NIS_BOGUS_OBJ. For purposes of maintaining expiration times,
274 276 * we may need to know if the object is a table or a directory
275 277 * _before_ we've retrieved any mapping data. Hence the 'expireType'
276 278 * field, which starts as NIS_BOGUS_OBJ (meaning, don't know, assume
277 279 * directory for now), and later is set to NIS_DIRECTORY_OBJ
278 280 * (always keep expiration data, in case one of the dir entries
279 281 * is mapped) or NIS_TABLE_OBJ (only need expiration data if
280 282 * tha table is mapped).
281 283 */
282 284 m->objType = NIS_BOGUS_OBJ;
283 285 m->expireType = NIS_BOGUS_OBJ;
284 286 if (m->objName != 0)
285 287 free(m->objName);
286 288 m->objName = 0;
287 289 }
288 290
289 291 void
290 292 db_table::db_table_ldap_init(void) {
291 293
292 294 INITRW(table);
293 295
294 296 enumMode.flag = 0;
295 297 enumCount.flag = 0;
296 298 enumIndex.ptr = 0;
297 299 enumArray.ptr = 0;
298 300
299 301 mapping.expire = 0;
300 302 mapping.tm = 0;
301 303 mapping.objName = 0;
302 304 mapping.isDeferredTable = FALSE;
303 305 (void) mutex_init(&mapping.enumLock, 0, 0);
304 306 mapping.enumTid = 0;
305 307 mapping.enumStat = -1;
306 308 mapping.enumDeferred = 0;
307 309 mapping.enumEntries = 0;
308 310 mapping.enumTime = 0;
309 311 }
310 312
311 313 /* db_table constructor */
312 314 db_table::db_table() : freelist()
313 315 {
314 316 tab = NULL;
315 317 table_size = 0;
316 318 last_used = 0;
317 319 count = 0;
318 320
319 321 db_table_ldap_init();
320 322 initMappingStruct(&mapping);
321 323
322 324 /* grow(); */
323 325 }
324 326
325 327 /*
326 328 * db_table destructor:
327 329 * 1. Get rid of contents of freelist
328 330 * 2. delete all entries hanging off table
329 331 * 3. get rid of table itself
330 332 */
331 333 db_table::~db_table()
332 334 {
333 335 WRITELOCKV(this, "w db_table::~db_table");
334 336 reset();
335 337 DESTROYRW(table);
336 338 }
337 339
338 340 /* reset size and pointers */
339 341 void
340 342 db_table::reset()
341 343 {
342 344 int i, done = 0;
343 345
344 346 WRITELOCKV(this, "w db_table::reset");
345 347 freelist.reset();
346 348
347 349 /* Add sanity check in case of table corruption */
348 350 if (tab != NULL) {
349 351 for (i = 0;
350 352 i <= last_used && i < table_size && done < count;
351 353 i++) {
352 354 if (tab[i]) {
353 355 free_entry(tab[i]);
354 356 ++done;
355 357 }
356 358 }
357 359 }
358 360
359 361 delete tab;
360 362 table_size = last_used = count = 0;
361 363 tab = NULL;
362 364 sfree(mapping.expire);
363 365 mapping.expire = NULL;
364 366 mapping.objType = NIS_BOGUS_OBJ;
365 367 mapping.expireType = NIS_BOGUS_OBJ;
366 368 sfree(mapping.objName);
367 369 mapping.objName = 0;
368 370 /* Leave other values of the mapping structure unchanged */
369 371 enumMode.flag = 0;
370 372 enumCount.flag = 0;
371 373 sfree(enumIndex.ptr);
372 374 enumIndex.ptr = 0;
373 375 sfree(enumArray.ptr);
374 376 enumArray.ptr = 0;
375 377 WRITEUNLOCKV(this, "wu db_table::reset");
376 378 }
377 379
378 380 db_status
379 381 db_table::allocateExpire(long oldSize, long newSize) {
380 382 time_t *newExpire;
381 383
382 384 newExpire = (time_t *)realloc(mapping.expire,
383 385 newSize * sizeof (mapping.expire[0]));
384 386 if (newExpire != NULL) {
385 387 /* Initialize new portion */
386 388 (void) memset(&newExpire[oldSize], 0,
387 389 (newSize-oldSize) * sizeof (newExpire[0]));
388 390 mapping.expire = newExpire;
389 391 } else {
390 392 return (DB_MEMORY_LIMIT);
391 393 }
392 394
393 395 return (DB_SUCCESS);
394 396 }
395 397
396 398 db_status
397 399 db_table::allocateEnumArray(long oldSize, long newSize) {
398 400 entry_object **newEnumArray;
399 401 const char *myself = "db_table::allocateEnumArray";
400 402
401 403 if (enumCount.flag > 0) {
402 404 if (enumIndex.ptr == 0) {
403 405 enumIndex.ptr = (entryp *)am(myself, enumCount.flag *
404 406 sizeof (entryp));
405 407 if (enumIndex.ptr == 0)
406 408 return (DB_MEMORY_LIMIT);
407 409 }
408 410 oldSize = 0;
409 411 newSize = enumCount.flag;
410 412 }
411 413 newEnumArray = (entry_object **)realloc(enumArray.ptr,
412 414 newSize * sizeof (entry_object *));
413 415 if (newEnumArray != 0 && newSize > oldSize) {
414 416 (void) memcpy(&newEnumArray[oldSize], &tab[oldSize],
415 417 (newSize-oldSize) * sizeof (entry_object *));
416 418 enumArray.ptr = newEnumArray;
417 419 } else if (newEnumArray == 0) {
418 420 return (DB_MEMORY_LIMIT);
419 421 }
420 422
421 423 return (DB_SUCCESS);
422 424 }
423 425
424 426 /* Expand the table. Fatal error if insufficient memory. */
425 427 void
426 428 db_table::grow()
427 429 {
428 430 WRITELOCKV(this, "w db_table::grow");
429 431 long oldsize = table_size;
430 432 entry_object_p *oldtab = tab;
431 433 long i;
432 434
433 435 table_size = get_new_table_size(oldsize);
434 436
435 437 #ifdef DEBUG
436 438 fprintf(stderr, "db_table GROWING to %d\n", table_size);
437 439 #endif
438 440
439 441 if (table_size > CALLOC_LIMIT) {
440 442 table_size = oldsize;
441 443 WRITEUNLOCKV(this, "wu db_table::grow");
442 444 FATAL("db_table::grow: table size exceeds calloc limit",
443 445 DB_MEMORY_LIMIT);
444 446 }
445 447
446 448 // if ((tab = new entry_object_p[table_size]) == NULL)
447 449 if ((tab = (entry_object_p*)
448 450 calloc((unsigned int) table_size,
449 451 sizeof (entry_object_p))) == NULL) {
450 452 tab = oldtab; // restore previous table info
451 453 table_size = oldsize;
452 454 WRITEUNLOCKV(this, "wu db_table::grow");
453 455 FATAL("db_table::grow: cannot allocate space", DB_MEMORY_LIMIT);
454 456 }
455 457
456 458 /*
457 459 * For directories, we may need the expire time array even if the
458 460 * directory itself isn't mapped. If the objType and expireType both
459 461 * are bogus, we don't know yet if this is a table or a directory,
460 462 * and must proceed accordingly.
461 463 */
462 464 if (mapping.objType == NIS_DIRECTORY_OBJ ||
463 465 mapping.expireType != NIS_TABLE_OBJ ||
464 466 mapping.fromLDAP) {
465 467 db_status stat = allocateExpire(oldsize, table_size);
466 468 if (stat != DB_SUCCESS) {
467 469 free(tab);
468 470 tab = oldtab;
469 471 table_size = oldsize;
470 472 WRITEUNLOCKV(this, "wu db_table::grow expire");
471 473 FATAL(
472 474 "db_table::grow: cannot allocate space for expire", stat);
473 475 }
474 476 }
475 477
476 478 if (oldtab != NULL) {
477 479 for (i = 0; i < oldsize; i++) { // transfer old to new
478 480 tab[i] = oldtab[i];
479 481 }
480 482 delete oldtab;
481 483 }
482 484
483 485 if (enumMode.flag) {
484 486 db_status stat = allocateEnumArray(oldsize, table_size);
485 487 if (stat != DB_SUCCESS) {
486 488 free(tab);
487 489 tab = oldtab;
488 490 table_size = oldsize;
489 491 WRITEUNLOCKV(this, "wu db_table::grow enumArray");
490 492 FATAL(
491 493 "db_table::grow: cannot allocate space for enumArray", stat);
492 494 }
493 495 }
494 496
495 497 WRITEUNLOCKV(this, "wu db_table::grow");
496 498 }
497 499
498 500 /*
499 501 * Return the first entry in table, also return its position in
500 502 * 'where'. Return NULL in both if no next entry is found.
501 503 */
502 504 entry_object*
503 505 db_table::first_entry(entryp * where)
504 506 {
505 507 ASSERTRHELD(table);
506 508 if (count == 0 || tab == NULL) { /* empty table */
507 509 *where = NULL;
508 510 return (NULL);
509 511 } else {
510 512 entryp i;
511 513 for (i = DB_TABLE_START;
512 514 i < table_size && i <= last_used; i++) {
513 515 if (tab[i] != NULL) {
514 516 *where = i;
515 517 return (tab[i]);
516 518 }
517 519 }
518 520 }
519 521 *where = NULL;
520 522 return (NULL);
521 523 }
522 524
523 525 /*
524 526 * Return the next entry in table from 'prev', also return its position in
525 527 * 'newentry'. Return NULL in both if no next entry is found.
526 528 */
527 529 entry_object *
528 530 db_table::next_entry(entryp prev, entryp* newentry)
529 531 {
530 532 long i;
531 533
532 534 ASSERTRHELD(table);
533 535 if (prev >= table_size || tab == NULL || tab[prev] == NULL)
534 536 return (NULL);
535 537 for (i = prev+1; i < table_size && i <= last_used; i++) {
536 538 if (tab[i] != NULL) {
537 539 *newentry = i;
538 540 return (tab[i]);
539 541 }
540 542 }
541 543 *newentry = NULL;
542 544 return (NULL);
543 545 }
544 546
545 547 /* Return entry at location 'where', NULL if location is invalid. */
546 548 entry_object *
547 549 db_table::get_entry(entryp where)
548 550 {
549 551 ASSERTRHELD(table);
550 552 if (where < table_size && tab != NULL && tab[where] != NULL)
551 553 return (tab[where]);
552 554 else
553 555 return (NULL);
554 556 }
555 557
556 558 void
557 559 db_table::setEntryExp(entryp where, entry_obj *obj, int initialLoad) {
558 560 nis_object *o;
559 561 const char *myself = "db_table::setEntryExp";
560 562
561 563 /*
562 564 * If we don't know what type of object this is yet, we
563 565 * can find out now. If it's a directory, the pseudo-object
564 566 * in column zero will have the type "IN_DIRECTORY";
565 567 * otherwise, it's a table object.
566 568 */
567 569 if (mapping.expireType == NIS_BOGUS_OBJ) {
568 570 if (obj != 0) {
569 571 if (obj->en_type != 0 &&
570 572 strcmp(obj->en_type, "IN_DIRECTORY") == 0) {
571 573 mapping.expireType = NIS_DIRECTORY_OBJ;
572 574 } else {
573 575 mapping.expireType = NIS_TABLE_OBJ;
574 576 if (!mapping.fromLDAP) {
575 577 free(mapping.expire);
576 578 mapping.expire = 0;
577 579 }
578 580 }
579 581 }
580 582 }
581 583
582 584 /* Set the entry TTL */
583 585 if (mapping.expire != NULL) {
584 586 struct timeval now;
585 587 time_t lo, hi, ttl;
586 588
587 589 (void) gettimeofday(&now, NULL);
588 590 if (mapping.expireType == NIS_TABLE_OBJ) {
589 591 lo = mapping.initTtlLo;
590 592 hi = mapping.initTtlHi;
591 593 ttl = mapping.ttl;
592 594 /* TTL == 0 means always expired */
593 595 if (ttl == 0)
594 596 ttl = -1;
595 597 } else {
596 598 __nis_table_mapping_t *t = 0;
597 599
598 600 o = unmakePseudoEntryObj(obj, 0);
599 601 if (o != 0) {
600 602 __nis_buffer_t b = {0, 0};
601 603
602 604 bp2buf(myself, &b, "%s.%s",
603 605 o->zo_name, o->zo_domain);
604 606 t = getObjMapping(b.buf, 0, 1, 0, 0);
605 607 sfree(b.buf);
606 608 nis_destroy_object(o);
607 609 }
608 610
609 611 if (t != 0) {
610 612 lo = t->initTtlLo;
611 613 hi = t->initTtlHi;
612 614 ttl = t->ttl;
613 615 /* TTL == 0 means always expired */
614 616 if (ttl == 0)
615 617 ttl = -1;
616 618 } else {
617 619 /*
618 620 * No expiration time initialization
619 621 * data. Cook up values that will
620 622 * result in mapping.expire[where]
621 623 * set to maxTimeT.
622 624 */
623 625 hi = lo = ttl = maxTimeT - now.tv_sec;
624 626 }
625 627 }
626 628
627 629 if (initialLoad) {
628 630 int interval = hi - lo + 1;
629 631 if (interval <= 1) {
630 632 mapping.expire[where] = now.tv_sec + lo;
631 633 } else {
632 634 srand48(now.tv_sec);
633 635 mapping.expire[where] = now.tv_sec +
634 636 (lrand48() % interval);
635 637 }
636 638 if (mapping.enumExpire == 0 ||
637 639 mapping.expire[where] <
638 640 mapping.enumExpire)
639 641 mapping.enumExpire = mapping.expire[where];
640 642 } else {
641 643 mapping.expire[where] = now.tv_sec + ttl;
642 644 }
643 645 }
644 646 }
645 647
646 648 /*
647 649 * Add given entry to table in first available slot (either look in freelist
648 650 * or add to end of table) and return the the position of where the record
649 651 * is placed. 'count' is incremented if entry is added. Table may grow
650 652 * as a side-effect of the addition. Copy is made of input.
651 653 */
652 654 entryp
653 655 db_table::add_entry(entry_object *obj, int initialLoad) {
654 656 /*
655 657 * We're returning an index of the table array, so the caller
656 658 * should hold a lock until done with the index. To save us
657 659 * the bother of upgrading to a write lock, it might as well
658 660 * be a write lock to begin with.
659 661 */
660 662 ASSERTWHELD(table);
661 663 entryp where = freelist.pop();
662 664 if (where == NULL) { /* empty freelist */
663 665 if (last_used >= (table_size-1)) /* full (> is for 0) */
664 666 grow();
665 667 where = ++last_used;
666 668 }
667 669 if (tab != NULL) {
668 670 ++count;
669 671 setEntryExp(where, obj, initialLoad);
670 672
671 673 if (enumMode.flag)
672 674 enumTouch(where);
673 675 tab[where] = new_entry(obj);
674 676 return (where);
675 677 } else {
676 678 return (NULL);
677 679 }
678 680 }
679 681
680 682 /*
681 683 * Replaces object at specified location by given entry.
682 684 * Returns TRUE if replacement successful; FALSE otherwise.
683 685 * There must something already at the specified location, otherwise,
684 686 * replacement fails. Copy is not made of the input.
685 687 * The pre-existing entry is freed.
686 688 */
687 689 bool_t
688 690 db_table::replace_entry(entryp where, entry_object * obj)
689 691 {
690 692 ASSERTWHELD(table);
691 693 if (where < DB_TABLE_START || where >= table_size ||
692 694 tab == NULL || tab[where] == NULL)
693 695 return (FALSE);
694 696 /* (Re-)set the entry TTL */
695 697 setEntryExp(where, obj, 0);
696 698
697 699 if (enumMode.flag)
698 700 enumTouch(where);
699 701 free_entry(tab[where]);
700 702 tab[where] = obj;
701 703 return (TRUE);
702 704 }
703 705
704 706 /*
705 707 * Deletes entry at specified location. Returns TRUE if location is valid;
706 708 * FALSE if location is invalid, or the freed location cannot be added to
707 709 * the freelist. 'count' is decremented if the deletion occurs. The object
708 710 * at that location is freed.
709 711 */
710 712 bool_t
711 713 db_table::delete_entry(entryp where)
712 714 {
713 715 bool_t ret = TRUE;
714 716
715 717 ASSERTWHELD(table);
716 718 if (where < DB_TABLE_START || where >= table_size ||
717 719 tab == NULL || tab[where] == NULL)
718 720 return (FALSE);
719 721 if (mapping.expire != NULL) {
720 722 mapping.expire[where] = 0;
721 723 }
722 724 if (enumMode.flag)
723 725 enumTouch(where);
724 726 free_entry(tab[where]);
725 727 tab[where] = NULL; /* very important to set it to null */
726 728 --count;
727 729 if (where == last_used) { /* simple case, deleting from end */
728 730 --last_used;
729 731 return (TRUE);
730 732 } else {
731 733 return (freelist.push(where));
732 734 }
733 735 return (ret);
734 736 }
735 737
736 738 /*
737 739 * Returns statistics of table.
738 740 * [vector_size][table_size][last_used][count][freelist].
739 741 * It is up to the caller to free the returned vector when his is through.
740 742 * The free list is included if 'fl' is TRUE.
741 743 */
742 744 long *
743 745 db_table::stats(bool_t include_freelist)
744 746 {
745 747 long *answer;
746 748
747 749 READLOCK(this, NULL, "r db_table::stats");
748 750 if (include_freelist)
749 751 answer = freelist.stats(3);
750 752 else {
751 753 answer = (long *)malloc(3*sizeof (long));
752 754 }
753 755
754 756 if (answer) {
755 757 answer[0] = table_size;
756 758 answer[1] = last_used;
757 759 answer[2] = count;
758 760 }
759 761 READUNLOCK(this, answer, "ru db_table::stats");
760 762 return (answer);
761 763 }
762 764
763 765 bool_t
764 766 db_table::configure(char *tablePath) {
765 767 long i;
766 768 struct timeval now;
767 769 const char *myself = "db_table::configure";
768 770
769 771 (void) gettimeofday(&now, NULL);
770 772
771 773 WRITELOCK(this, FALSE, "db_table::configure w");
772 774
773 775 /* (Re-)initialize from global info */
774 776 initMappingStruct(&mapping);
775 777
776 778 /* Retrieve table mapping for this table */
777 779 mapping.tm = (__nis_table_mapping_t *)__nis_find_item_mt(
778 780 tablePath, &ldapMappingList, 0, 0);
779 781 if (mapping.tm != 0) {
780 782 __nis_object_dn_t *odn = mapping.tm->objectDN;
781 783
782 784 /*
783 785 * The mapping.fromLDAP and mapping.toLDAP fields serve as
784 786 * quick-references that tell us if mapping is enabled.
785 787 * Hence, initialize them appropriately from the table
786 788 * mapping objectDN.
787 789 */
788 790 while (odn != 0 && (!mapping.fromLDAP || !mapping.toLDAP)) {
789 791 if (odn->read.scope != LDAP_SCOPE_UNKNOWN)
790 792 mapping.fromLDAP = TRUE;
791 793 if (odn->write.scope != LDAP_SCOPE_UNKNOWN)
792 794 mapping.toLDAP = TRUE;
793 795 odn = (__nis_object_dn_t *)odn->next;
794 796 }
795 797
796 798 /* Set the timeout values */
797 799 mapping.initTtlLo = mapping.tm->initTtlLo;
798 800 mapping.initTtlHi = mapping.tm->initTtlHi;
799 801 mapping.ttl = mapping.tm->ttl;
800 802
801 803 mapping.objName = sdup(myself, T, mapping.tm->objName);
802 804 if (mapping.objName == 0 && mapping.tm->objName != 0) {
803 805 WRITEUNLOCK(this, FALSE,
804 806 "db_table::configure wu objName");
805 807 FATAL3("db_table::configure objName",
806 808 DB_MEMORY_LIMIT, FALSE);
807 809 }
808 810 }
809 811
810 812 /*
811 813 * In order to initialize the expiration times, we need to know
812 814 * if 'this' represents a table or a directory. To that end, we
813 815 * find an entry in the table, and invoke setEntryExp() on it.
814 816 * As a side effect, setEntryExp() will examine the pseudo-object
815 817 * in the entry, and set the expireType accordingly.
816 818 */
817 819 if (tab != 0) {
818 820 for (i = 0; i <= last_used; i++) {
819 821 if (tab[i] != NULL) {
820 822 setEntryExp(i, tab[i], 1);
821 823 break;
822 824 }
823 825 }
824 826 }
825 827
826 828 /*
827 829 * If mapping from an LDAP repository, make sure we have the
828 830 * expiration time array.
829 831 */
830 832 if ((mapping.expireType != NIS_TABLE_OBJ || mapping.fromLDAP) &&
831 833 mapping.expire == NULL && table_size > 0 && tab != 0) {
832 834 db_status stat = allocateExpire(0, table_size);
833 835 if (stat != DB_SUCCESS) {
834 836 WRITEUNLOCK(this, FALSE,
835 837 "db_table::configure wu expire");
836 838 FATAL3("db_table::configure expire",
837 839 stat, FALSE);
838 840 }
839 841 } else if (mapping.expireType == NIS_TABLE_OBJ && !mapping.fromLDAP &&
840 842 mapping.expire != NULL) {
841 843 /* Not using expiration times */
842 844 free(mapping.expire);
843 845 mapping.expire = NULL;
844 846 }
845 847
846 848 /*
847 849 * Set initial expire times for entries that don't already have one.
848 850 * Establish the enumeration expiration time to be the minimum of
849 851 * all expiration times in the table, though no larger than current
850 852 * time plus initTtlHi.
851 853 */
852 854 if (mapping.expire != NULL) {
853 855 int interval = mapping.initTtlHi - mapping.initTtlLo + 1;
854 856 time_t enumXp = now.tv_sec + mapping.initTtlHi;
855 857
856 858 if (interval > 1)
857 859 srand48(now.tv_sec);
858 860 for (i = 0; i <= last_used; i++) {
859 861 if (tab[i] != NULL && mapping.expire[i] == 0) {
860 862 if (mapping.expireType == NIS_TABLE_OBJ) {
861 863 if (interval > 1)
862 864 mapping.expire[i] =
863 865 now.tv_sec +
864 866 (lrand48() % interval);
865 867 else
866 868 mapping.expire[i] =
867 869 now.tv_sec +
868 870 mapping.initTtlLo;
869 871 } else {
870 872 setEntryExp(i, tab[i], 1);
871 873 }
872 874 }
873 875 if (enumXp > mapping.expire[i])
874 876 enumXp = mapping.expire[i];
875 877 }
876 878 mapping.enumExpire = enumXp;
877 879 }
878 880
879 881 WRITEUNLOCK(this, FALSE, "db_table::configure wu");
880 882
881 883 return (TRUE);
882 884 }
883 885
884 886 /* Return TRUE if the entry at 'loc' hasn't expired */
885 887 bool_t
886 888 db_table::cacheValid(entryp loc) {
887 889 bool_t ret;
888 890 struct timeval now;
889 891
890 892 (void) gettimeofday(&now, 0);
891 893
892 894 READLOCK(this, FALSE, "db_table::cacheValid r");
893 895
894 896 if (loc < 0 || loc >= table_size || tab == 0 || tab[loc] == 0)
895 897 ret = FALSE;
896 898 else if (mapping.expire == 0 || mapping.expire[loc] >= now.tv_sec)
897 899 ret = TRUE;
898 900 else
899 901 ret = FALSE;
900 902
901 903 READUNLOCK(this, ret, "db_table::cacheValid ru");
902 904
903 905 return (ret);
904 906 }
905 907
906 908 /*
907 909 * If the supplied object has the same content as the one at 'loc',
908 910 * update the expiration time for the latter, and return TRUE.
909 911 */
910 912 bool_t
911 913 db_table::dupEntry(entry_object *obj, entryp loc) {
912 914 if (obj == 0 || loc < 0 || loc >= table_size || tab == 0 ||
913 915 tab[loc] == 0)
914 916 return (FALSE);
915 917
916 918 if (sameEntry(obj, tab[loc])) {
917 919 setEntryExp(loc, tab[loc], 0);
918 920
919 921 if (enumMode.flag > 0)
920 922 enumTouch(loc);
921 923 return (TRUE);
922 924 }
923 925
924 926 return (FALSE);
925 927 }
926 928
927 929 /*
928 930 * If enumeration mode is enabled, we keep a shadow array that initially
929 931 * starts out the same as 'tab'. Any update activity (add, remove, replace,
930 932 * or update timestamp) for an entry in the table means we delete the shadow
931 933 * array pointer. When ending enumeration mode, we return the shadow array.
932 934 * Any non-NULL entries in the array have not been updated since the start
933 935 * of the enum mode.
934 936 *
935 937 * The indended use is for enumeration of an LDAP container, where we
936 938 * will update all entries that currently exist in LDAP. The entries we
937 939 * don't update are those that don't exist in LDAP, and thus should be
938 940 * removed.
939 941 *
940 942 * Note that any LDAP query strictly speaking can be a partial enumeration
941 943 * (i.e., return more than one match). Since the query might also have
942 944 * matched multiple local DB entries, we need to do the same work as for
943 945 * enumeration for any query. In order to avoid having to work on the
944 946 * whole 'tab' array for simple queries (which we expect usually will
945 947 * match just one or at most a few entries), we have a "reduced" enum mode,
946 948 * where the caller supplies a count of the number of DB entries (derived
947 949 * from db_mindex::satisfy_query() or similar), and then uses enumSetup()
948 950 * to specify which 'tab' entries we're interested in.
949 951 */
950 952 void
951 953 db_table::setEnumMode(long enumNum) {
952 954 const char *myself = "setEnumMode";
953 955
954 956 enumMode.flag++;
955 957 if (enumMode.flag == 1) {
956 958 db_status stat;
957 959
958 960 if (enumNum < 0)
959 961 enumNum = 0;
960 962 else if (enumNum >= table_size)
961 963 enumNum = table_size;
962 964
963 965 enumCount.flag = enumNum;
964 966
965 967 stat = allocateEnumArray(0, table_size);
966 968
967 969 if (stat != DB_SUCCESS) {
968 970 enumMode.flag = 0;
969 971 enumCount.flag = 0;
970 972 logmsg(MSG_NOTIMECHECK, LOG_ERR,
971 973 "%s: No memory for enum check array; entry removal disabled",
972 974 myself);
973 975 }
974 976 }
975 977 }
976 978
977 979 void
978 980 db_table::clearEnumMode(void) {
979 981 if (enumMode.flag > 0) {
980 982 enumMode.flag--;
981 983 if (enumMode.flag == 0) {
982 984 sfree(enumArray.ptr);
983 985 enumArray.ptr = 0;
984 986 if (enumCount.flag > 0) {
985 987 sfree(enumIndex.ptr);
986 988 enumIndex.ptr = 0;
987 989 enumCount.flag = 0;
988 990 }
989 991 }
990 992 }
991 993 }
992 994
993 995 entry_object **
994 996 db_table::endEnumMode(long *numEa) {
995 997 if (enumMode.flag > 0) {
996 998 enumMode.flag--;
997 999 if (enumMode.flag == 0) {
998 1000 entry_obj **ea = (entry_object **)enumArray.ptr;
999 1001 long nea;
1000 1002
1001 1003 enumArray.ptr = 0;
1002 1004
1003 1005 if (enumCount.flag > 0) {
1004 1006 nea = enumCount.flag;
1005 1007 enumCount.flag = 0;
1006 1008 sfree(enumIndex.ptr);
1007 1009 enumIndex.ptr = 0;
1008 1010 } else {
1009 1011 nea = table_size;
1010 1012 }
1011 1013
1012 1014 if (numEa != 0)
1013 1015 *numEa = nea;
1014 1016
1015 1017 return (ea);
1016 1018 }
1017 1019 }
1018 1020
1019 1021 if (numEa != 0)
1020 1022 *numEa = 0;
1021 1023
1022 1024 return (0);
1023 1025 }
1024 1026
1025 1027 /*
1026 1028 * Set the appropriate entry in the enum array to NULL.
1027 1029 */
1028 1030 void
1029 1031 db_table::enumTouch(entryp loc) {
1030 1032 if (loc < 0 || loc >= table_size)
1031 1033 return;
1032 1034
1033 1035 if (enumMode.flag > 0) {
1034 1036 if (enumCount.flag < 1) {
1035 1037 ((entry_object **)enumArray.ptr)[loc] = 0;
1036 1038 } else {
1037 1039 int i;
1038 1040
1039 1041 for (i = 0; i < enumCount.flag; i++) {
1040 1042 if (loc == ((entryp *)enumIndex.ptr)[i]) {
1041 1043 ((entry_object **)enumArray.ptr)[i] = 0;
1042 1044 break;
1043 1045 }
1044 1046 }
1045 1047 }
1046 1048 }
1047 1049 }
1048 1050
1049 1051 /*
1050 1052 * Add the entry indicated by 'loc' to the enumIndex array, at 'index'.
1051 1053 */
1052 1054 void
1053 1055 db_table::enumSetup(entryp loc, long index) {
1054 1056 if (enumMode.flag == 0 || loc < 0 || loc >= table_size ||
1055 1057 index < 0 || index >= enumCount.flag)
1056 1058 return;
1057 1059
1058 1060 ((entryp *)enumIndex.ptr)[index] = loc;
1059 1061 ((entry_object **)enumArray.ptr)[index] = tab[loc];
1060 1062 }
1061 1063
1062 1064 /*
1063 1065 * Touch, i.e., update the expiration time for the entry. Also, if enum
1064 1066 * mode is in effect, mark the entry used for enum purposes.
1065 1067 */
1066 1068 void
1067 1069 db_table::touchEntry(entryp loc) {
1068 1070 if (loc < 0 || loc >= table_size || tab == 0 || tab[loc] == 0)
1069 1071 return;
1070 1072
1071 1073 setEntryExp(loc, tab[loc], 0);
1072 1074
1073 1075 enumTouch(loc);
1074 1076 }
1075 1077
1076 1078 /* ************************* pickle_table ********************* */
1077 1079 /* Does the actual writing to/from file specific for db_table structure. */
1078 1080 /*
1079 1081 * This was a static earlier with the func name being transfer_aux. The
1080 1082 * backup and restore project needed this to copy files over.
1081 1083 */
1082 1084 bool_t
1083 1085 transfer_aux_table(XDR* x, pptr dp)
1084 1086 {
1085 1087 return (xdr_db_table(x, (db_table*) dp));
1086 1088 }
1087 1089
1088 1090 class pickle_table: public pickle_file {
1089 1091 public:
1090 1092 pickle_table(char *f, pickle_mode m) : pickle_file(f, m) {}
1091 1093
1092 1094 /* Transfers db_table structure pointed to by dp to/from file. */
1093 1095 int transfer(db_table* dp)
1094 1096 { return (pickle_file::transfer((pptr) dp, &transfer_aux_table)); }
1095 1097 };
1096 1098
1097 1099 /*
1098 1100 * Writes the contents of table, including the all the entries, into the
1099 1101 * specified file in XDR format. May need to change this to use APPEND
1100 1102 * mode instead.
1101 1103 */
1102 1104 int
1103 1105 db_table::dump(char *file)
1104 1106 {
1105 1107 int ret;
1106 1108 READLOCK(this, -1, "r db_table::dump");
1107 1109 pickle_table f(file, PICKLE_WRITE); /* may need to use APPEND mode */
1108 1110 int status = f.transfer(this);
1109 1111
1110 1112 if (status == 1)
1111 1113 ret = -1;
1112 1114 else
1113 1115 ret = status;
1114 1116 READUNLOCK(this, ret, "ru db_table::dump");
1115 1117 }
1116 1118
1117 1119 /* Constructor that loads in the table from the given file */
1118 1120 db_table::db_table(char *file) : freelist()
1119 1121 {
1120 1122 pickle_table f(file, PICKLE_READ);
1121 1123 tab = NULL;
1122 1124 table_size = last_used = count = 0;
1123 1125
1124 1126 /* load table */
1125 1127 if (f.transfer(this) < 0) {
1126 1128 /* fell through, something went wrong, initialize to null */
1127 1129 tab = NULL;
1128 1130 table_size = last_used = count = 0;
1129 1131 freelist.init();
1130 1132 }
1131 1133
1132 1134 db_table_ldap_init();
1133 1135 initMappingStruct(&mapping);
1134 1136 }
1135 1137
1136 1138 /* Returns whether location is valid. */
1137 1139 bool_t db_table::entry_exists_p(entryp i) {
1138 1140 bool_t ret = FALSE;
1139 1141 READLOCK(this, FALSE, "r db_table::entry_exists_p");
1140 1142 if (tab != NULL && i < table_size)
1141 1143 ret = tab[i] != NULL;
1142 1144 READUNLOCK(this, ret, "ru db_table::entry_exists_p");
1143 1145 return (ret);
1144 1146 }
1145 1147
1146 1148 /* Empty free list */
1147 1149 void db_free_list::init() {
1148 1150 WRITELOCKV(this, "w db_free_list::init");
1149 1151 head = NULL;
1150 1152 count = 0;
1151 1153 WRITEUNLOCKV(this, "wu db_free_list::init");
1152 1154 }
↓ open down ↓ |
1065 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX