Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : Generic, persistent and shared between processes cache mechanism for use
5 : by various parts of the Samba code
6 :
7 : Copyright (C) Rafal Szczesniak 2002
8 : Copyright (C) Volker Lendecke 2009
9 :
10 : This program is free software; you can redistribute it and/or modify
11 : it under the terms of the GNU General Public License as published by
12 : the Free Software Foundation; either version 3 of the License, or
13 : (at your option) any later version.
14 :
15 : This program is distributed in the hope that it will be useful,
16 : but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : GNU General Public License for more details.
19 :
20 : You should have received a copy of the GNU General Public License
21 : along with this program. If not, see <http://www.gnu.org/licenses/>.
22 : */
23 :
24 : #include "includes.h"
25 : #include "lib/gencache.h"
26 : #include "system/filesys.h"
27 : #include "system/glob.h"
28 : #include "util_tdb.h"
29 : #include "tdb_wrap/tdb_wrap.h"
30 : #include "zlib.h"
31 : #include "lib/util/strv.h"
32 : #include "lib/util/util_paths.h"
33 :
34 : #undef DBGC_CLASS
35 : #define DBGC_CLASS DBGC_TDB
36 :
37 : #define GENCACHE_USER_PATH "~/.cache/samba/gencache.tdb"
38 :
39 : static struct tdb_wrap *cache;
40 :
41 : /**
42 : * @file gencache.c
43 : * @brief Generic, persistent and shared between processes cache mechanism
44 : * for use by various parts of the Samba code
45 : *
46 : **/
47 :
48 : static bool gencache_pull_timeout(TDB_DATA key,
49 : TDB_DATA data,
50 : time_t *pres,
51 : DATA_BLOB *payload);
52 :
53 : struct gencache_timeout {
54 : time_t timeout;
55 : };
56 :
57 1525725 : bool gencache_timeout_expired(const struct gencache_timeout *t)
58 : {
59 1525725 : return t->timeout <= time(NULL);
60 : }
61 :
62 : /**
63 : * Cache initialisation function. Opens cache tdb file or creates
64 : * it if does not exist.
65 : *
66 : * @return true on successful initialisation of the cache or
67 : * false on failure
68 : **/
69 :
70 3807403 : static bool gencache_init(void)
71 : {
72 3807403 : char* cache_fname = NULL;
73 3807403 : int open_flags = O_RDWR|O_CREAT;
74 3807403 : int tdb_flags = TDB_INCOMPATIBLE_HASH|TDB_NOSYNC|TDB_MUTEX_LOCKING;
75 1009755 : int hash_size;
76 :
77 : /* skip file open if it's already opened */
78 3807403 : if (cache) {
79 2788656 : return true;
80 : }
81 :
82 9005 : hash_size = lp_parm_int(-1, "gencache", "hash_size", 10000);
83 :
84 9005 : cache_fname = lock_path(talloc_tos(), "gencache.tdb");
85 9005 : if (cache_fname == NULL) {
86 0 : return false;
87 : }
88 :
89 9005 : DEBUG(5, ("Opening cache file at %s\n", cache_fname));
90 :
91 9005 : cache = tdb_wrap_open(NULL, cache_fname, hash_size,
92 : tdb_flags,
93 : open_flags, 0644);
94 : /*
95 : * Allow client tools to create a gencache in the home directory
96 : * as a normal user.
97 : */
98 9005 : if (cache == NULL && errno == EACCES && geteuid() != 0) {
99 0 : char *cache_dname = NULL, *tmp = NULL;
100 0 : bool ok;
101 :
102 0 : TALLOC_FREE(cache_fname);
103 :
104 0 : cache_fname = path_expand_tilde(talloc_tos(),
105 : GENCACHE_USER_PATH);
106 0 : if (cache_fname == NULL) {
107 0 : DBG_ERR("Failed to expand path: %s\n",
108 : GENCACHE_USER_PATH);
109 0 : return false;
110 : }
111 :
112 0 : tmp = talloc_strdup(talloc_tos(), cache_fname);
113 0 : if (tmp == NULL) {
114 0 : DBG_ERR("No memory!\n");
115 0 : TALLOC_FREE(cache_fname);
116 0 : return false;
117 : }
118 :
119 0 : cache_dname = dirname(tmp);
120 0 : if (cache_dname == NULL) {
121 0 : DBG_ERR("Invalid path: %s\n", cache_fname);
122 0 : TALLOC_FREE(tmp);
123 0 : TALLOC_FREE(cache_fname);
124 0 : return false;
125 : }
126 :
127 0 : ok = directory_create_or_exists_recursive(cache_dname, 0700);
128 0 : if (!ok) {
129 0 : DBG_ERR("Failed to create directory: %s - %s\n",
130 : cache_dname, strerror(errno));
131 0 : TALLOC_FREE(tmp);
132 0 : TALLOC_FREE(cache_fname);
133 0 : return false;
134 : }
135 0 : TALLOC_FREE(tmp);
136 :
137 0 : cache = tdb_wrap_open(NULL,
138 : cache_fname,
139 : hash_size,
140 : tdb_flags,
141 : open_flags,
142 : 0644);
143 0 : if (cache != NULL) {
144 0 : DBG_INFO("Opening user cache file %s.\n",
145 : cache_fname);
146 : }
147 : }
148 :
149 9005 : if (cache == NULL) {
150 0 : DEBUG(5, ("Opening %s failed: %s\n", cache_fname,
151 : strerror(errno)));
152 0 : TALLOC_FREE(cache_fname);
153 0 : return false;
154 : }
155 9005 : TALLOC_FREE(cache_fname);
156 :
157 9005 : return true;
158 : }
159 :
160 : /*
161 : * Walk the hash chain for "key", deleting all expired entries for
162 : * that hash chain
163 : */
164 : struct gencache_prune_expired_state {
165 : TALLOC_CTX *mem_ctx;
166 : char *keys;
167 : };
168 :
169 9911 : static int gencache_prune_expired_fn(struct tdb_context *tdb,
170 : TDB_DATA key,
171 : TDB_DATA data,
172 : void *private_data)
173 : {
174 9911 : struct gencache_prune_expired_state *state = private_data;
175 22 : struct gencache_timeout t;
176 9911 : bool ok = false;
177 9911 : bool expired = false;
178 :
179 9911 : if ((key.dsize == 0) || (key.dptr[key.dsize-1] != '\0')) {
180 : /* not a valid record, should never happen */
181 0 : return 0;
182 : }
183 :
184 9911 : ok = gencache_pull_timeout(key, data, &t.timeout, NULL);
185 9911 : if (ok) {
186 9911 : expired = gencache_timeout_expired(&t);
187 : }
188 :
189 9911 : if (!ok || expired) {
190 0 : int ret;
191 :
192 621 : ret = strv_add(state->mem_ctx, &state->keys, (char *)key.dptr);
193 621 : if (ret != 0) {
194 : /*
195 : * Exit the loop. It's unlikely that it will
196 : * succeed next time.
197 : */
198 0 : return -1;
199 : }
200 : }
201 :
202 9889 : return 0;
203 : }
204 :
205 29148 : static void gencache_prune_expired(struct tdb_context *tdb,
206 : TDB_DATA chain_key)
207 : {
208 58296 : struct gencache_prune_expired_state state = {
209 29148 : .mem_ctx = talloc_tos(),
210 : };
211 29148 : char *keystr = NULL;
212 591 : int ret;
213 :
214 29148 : ret = tdb_traverse_key_chain(
215 : tdb, chain_key, gencache_prune_expired_fn, &state);
216 29148 : if (ret == -1) {
217 0 : DBG_DEBUG("tdb_traverse_key_chain failed: %s\n",
218 : tdb_errorstr(tdb));
219 0 : return;
220 : }
221 :
222 29769 : while ((keystr = strv_next(state.keys, keystr)) != NULL) {
223 621 : TDB_DATA key = string_term_tdb_data(keystr);
224 :
225 : /*
226 : * We expect the hash chain of "chain_key" to be
227 : * locked. So between gencache_prune_expired_fn
228 : * figuring out "keystr" is expired and the
229 : * tdb_delete, nobody can have reset the timeout.
230 : */
231 621 : tdb_delete(tdb, key);
232 : }
233 :
234 29148 : TALLOC_FREE(state.keys);
235 : }
236 :
237 : /**
238 : * Set an entry in the cache file. If there's no such
239 : * one, then add it.
240 : *
241 : * @param keystr string that represents a key of this entry
242 : * @param blob DATA_BLOB value being cached
243 : * @param timeout time when the value is expired
244 : *
245 : * @retval true when entry is successfully stored
246 : * @retval false on failure
247 : **/
248 :
249 29148 : bool gencache_set_data_blob(const char *keystr, DATA_BLOB blob,
250 : time_t timeout)
251 : {
252 591 : TDB_DATA key;
253 591 : int ret;
254 591 : TDB_DATA dbufs[3];
255 591 : uint32_t crc;
256 :
257 29148 : if ((keystr == NULL) || (blob.data == NULL)) {
258 0 : return false;
259 : }
260 :
261 29148 : key = string_term_tdb_data(keystr);
262 :
263 29148 : if (!gencache_init()) {
264 0 : return false;
265 : }
266 :
267 29148 : dbufs[0] = (TDB_DATA) { .dptr = (uint8_t *)&timeout,
268 : .dsize = sizeof(time_t) };
269 29148 : dbufs[1] = (TDB_DATA) { .dptr = blob.data, .dsize = blob.length };
270 :
271 29148 : crc = crc32(0, Z_NULL, 0);
272 29148 : crc = crc32(crc, key.dptr, key.dsize);
273 29148 : crc = crc32(crc, dbufs[0].dptr, dbufs[0].dsize);
274 29148 : crc = crc32(crc, dbufs[1].dptr, dbufs[1].dsize);
275 :
276 29148 : dbufs[2] = (TDB_DATA) { .dptr = (uint8_t *)&crc,
277 : .dsize = sizeof(crc) };
278 :
279 29148 : DBG_DEBUG("Adding cache entry with key=[%s] and timeout="
280 : "[%s] (%ld seconds %s)\n", keystr,
281 : timestring(talloc_tos(), timeout),
282 : ((long int)timeout) - time(NULL),
283 : timeout > time(NULL) ? "ahead" : "in the past");
284 :
285 29148 : ret = tdb_chainlock(cache->tdb, key);
286 29148 : if (ret == -1) {
287 0 : DBG_WARNING("tdb_chainlock for key [%s] failed: %s\n",
288 : keystr, tdb_errorstr(cache->tdb));
289 0 : return false;
290 : }
291 :
292 29148 : gencache_prune_expired(cache->tdb, key);
293 :
294 29148 : ret = tdb_storev(cache->tdb, key, dbufs, ARRAY_SIZE(dbufs), 0);
295 :
296 29148 : tdb_chainunlock(cache->tdb, key);
297 :
298 29148 : if (ret == 0) {
299 28557 : return true;
300 : }
301 0 : if (tdb_error(cache->tdb) != TDB_ERR_CORRUPT) {
302 0 : return false;
303 : }
304 :
305 0 : ret = tdb_wipe_all(cache->tdb);
306 0 : SMB_ASSERT(ret == 0);
307 :
308 0 : return false;
309 : }
310 :
311 : /**
312 : * Delete one entry from the cache file.
313 : *
314 : * @param keystr string that represents a key of this entry
315 : *
316 : * @retval true upon successful deletion
317 : * @retval false in case of failure
318 : **/
319 :
320 1875 : bool gencache_del(const char *keystr)
321 : {
322 1875 : TDB_DATA key = string_term_tdb_data(keystr);
323 49 : int ret;
324 :
325 1875 : if (keystr == NULL) {
326 0 : return false;
327 : }
328 :
329 1875 : if (!gencache_init()) {
330 0 : return false;
331 : }
332 :
333 1875 : DEBUG(10, ("Deleting cache entry (key=[%s])\n", keystr));
334 :
335 1875 : ret = tdb_delete(cache->tdb, key);
336 :
337 1875 : if (ret == 0) {
338 1479 : return true;
339 : }
340 367 : if (tdb_error(cache->tdb) != TDB_ERR_CORRUPT) {
341 347 : return false;
342 : }
343 :
344 0 : ret = tdb_wipe_all(cache->tdb);
345 0 : SMB_ASSERT(ret == 0);
346 :
347 0 : return true; /* We've deleted a bit more... */
348 : }
349 :
350 3618417 : static bool gencache_pull_timeout(TDB_DATA key,
351 : TDB_DATA data,
352 : time_t *pres,
353 : DATA_BLOB *payload)
354 : {
355 1008766 : size_t crc_ofs;
356 1008766 : uint32_t crc, stored_crc;
357 :
358 3618417 : if ((data.dptr == NULL) ||
359 2609651 : (data.dsize < (sizeof(time_t) + sizeof(uint32_t)))) {
360 0 : return false;
361 : }
362 :
363 3618417 : crc_ofs = data.dsize - sizeof(uint32_t);
364 :
365 3618417 : crc = crc32(0, Z_NULL, 0);
366 3618417 : crc = crc32(crc, key.dptr, key.dsize);
367 3618417 : crc = crc32(crc, data.dptr, crc_ofs);
368 :
369 3618417 : memcpy(&stored_crc, data.dptr + crc_ofs, sizeof(uint32_t));
370 :
371 3618417 : if (stored_crc != crc) {
372 0 : return false;
373 : }
374 :
375 3618417 : if (pres != NULL) {
376 3618417 : memcpy(pres, data.dptr, sizeof(time_t));
377 : }
378 3618417 : if (payload != NULL) {
379 3608506 : *payload = (DATA_BLOB) {
380 3608506 : .data = data.dptr+sizeof(time_t),
381 3608506 : .length = data.dsize-sizeof(time_t)-sizeof(uint32_t),
382 : };
383 : }
384 2609651 : return true;
385 : }
386 :
387 : struct gencache_parse_state {
388 : void (*parser)(const struct gencache_timeout *timeout,
389 : DATA_BLOB blob,
390 : void *private_data);
391 : void *private_data;
392 : bool format_error;
393 : };
394 :
395 3533426 : static int gencache_parse_fn(TDB_DATA key, TDB_DATA data, void *private_data)
396 : {
397 3533426 : struct gencache_parse_state *state = private_data;
398 1008744 : struct gencache_timeout t;
399 1008744 : DATA_BLOB payload;
400 1008744 : bool ret;
401 :
402 3533426 : ret = gencache_pull_timeout(key, data, &t.timeout, &payload);
403 3533426 : if (!ret) {
404 0 : state->format_error = true;
405 0 : return 0;
406 : }
407 3533426 : state->parser(&t, payload, state->private_data);
408 :
409 3533426 : return 0;
410 : }
411 :
412 3776146 : bool gencache_parse(const char *keystr,
413 : void (*parser)(const struct gencache_timeout *timeout,
414 : DATA_BLOB blob,
415 : void *private_data),
416 : void *private_data)
417 : {
418 3776146 : struct gencache_parse_state state = {
419 : .parser = parser, .private_data = private_data
420 : };
421 3776146 : TDB_DATA key = string_term_tdb_data(keystr);
422 1009115 : int ret;
423 :
424 3776146 : if (keystr == NULL) {
425 0 : return false;
426 : }
427 3776146 : if (!gencache_init()) {
428 0 : return false;
429 : }
430 :
431 3776146 : ret = tdb_parse_record(cache->tdb, key,
432 : gencache_parse_fn, &state);
433 3776146 : if ((ret == -1) && (tdb_error(cache->tdb) == TDB_ERR_CORRUPT)) {
434 0 : goto wipe;
435 : }
436 3776146 : if (ret == -1) {
437 242349 : return false;
438 : }
439 3533426 : if (state.format_error) {
440 0 : ret = tdb_delete(cache->tdb, key);
441 0 : if (ret == -1) {
442 0 : goto wipe;
443 : }
444 0 : return false;
445 : }
446 2524682 : return true;
447 :
448 0 : wipe:
449 0 : ret = tdb_wipe_all(cache->tdb);
450 0 : SMB_ASSERT(ret == 0);
451 0 : return false;
452 : }
453 :
454 : struct gencache_get_data_blob_state {
455 : TALLOC_CTX *mem_ctx;
456 : DATA_BLOB *blob;
457 : time_t timeout;
458 : bool result;
459 : };
460 :
461 1017611 : static void gencache_get_data_blob_parser(const struct gencache_timeout *t,
462 : DATA_BLOB blob,
463 : void *private_data)
464 : {
465 1017611 : struct gencache_get_data_blob_state *state =
466 : (struct gencache_get_data_blob_state *)private_data;
467 :
468 1017611 : if (t->timeout == 0) {
469 169 : state->result = false;
470 169 : return;
471 : }
472 1017442 : state->timeout = t->timeout;
473 :
474 1017442 : if (state->blob == NULL) {
475 0 : state->result = true;
476 0 : return;
477 : }
478 :
479 1017442 : *state->blob = data_blob_talloc(state->mem_ctx, blob.data,
480 : blob.length);
481 1017442 : if (state->blob->data == NULL) {
482 0 : state->result = false;
483 0 : return;
484 : }
485 1017442 : state->result = true;
486 : }
487 :
488 : /**
489 : * Get existing entry from the cache file.
490 : *
491 : * @param keystr string that represents a key of this entry
492 : * @param blob DATA_BLOB that is filled with entry's blob
493 : * @param timeout pointer to a time_t that is filled with entry's
494 : * timeout
495 : *
496 : * @retval true when entry is successfully fetched
497 : * @retval false for failure
498 : **/
499 :
500 1067698 : bool gencache_get_data_blob(const char *keystr, TALLOC_CTX *mem_ctx,
501 : DATA_BLOB *blob,
502 : time_t *timeout, bool *was_expired)
503 : {
504 2344 : struct gencache_get_data_blob_state state;
505 1067698 : bool expired = false;
506 :
507 1067698 : state.result = false;
508 1067698 : state.mem_ctx = mem_ctx;
509 1067698 : state.blob = blob;
510 :
511 1067698 : if (!gencache_parse(keystr, gencache_get_data_blob_parser, &state)) {
512 50087 : goto fail;
513 : }
514 1017611 : if (!state.result) {
515 169 : goto fail;
516 : }
517 1017442 : if (state.timeout <= time(NULL)) {
518 : /*
519 : * We're expired, delete the entry. We can't use gencache_del
520 : * here, because that uses gencache_get_data_blob for checking
521 : * the existence of a record. We know the thing exists and
522 : * directly store an empty value with 0 timeout.
523 : */
524 134 : gencache_set(keystr, "", 0);
525 134 : expired = true;
526 134 : goto fail;
527 : }
528 1017308 : if (timeout) {
529 1016801 : *timeout = state.timeout;
530 : }
531 :
532 1015015 : return true;
533 :
534 50390 : fail:
535 50390 : if (was_expired != NULL) {
536 0 : *was_expired = expired;
537 : }
538 50390 : if (state.result && state.blob) {
539 134 : data_blob_free(state.blob);
540 : }
541 50339 : return false;
542 : }
543 :
544 : /**
545 : * Get existing entry from the cache file.
546 : *
547 : * @param keystr string that represents a key of this entry
548 : * @param valstr buffer that is allocated and filled with the entry value
549 : * buffer's disposing must be done outside
550 : * @param timeout pointer to a time_t that is filled with entry's
551 : * timeout
552 : *
553 : * @retval true when entry is successfully fetched
554 : * @retval false for failure
555 : **/
556 :
557 1067652 : bool gencache_get(const char *keystr, TALLOC_CTX *mem_ctx, char **value,
558 : time_t *ptimeout)
559 : {
560 2342 : DATA_BLOB blob;
561 1067652 : bool ret = false;
562 :
563 1067652 : ret = gencache_get_data_blob(keystr, mem_ctx, &blob, ptimeout, NULL);
564 1067652 : if (!ret) {
565 50334 : return false;
566 : }
567 1017268 : if ((blob.data == NULL) || (blob.length == 0)) {
568 0 : data_blob_free(&blob);
569 0 : return false;
570 : }
571 1017268 : if (blob.data[blob.length-1] != '\0') {
572 : /* Not NULL terminated, can't be a string */
573 1 : data_blob_free(&blob);
574 1 : return false;
575 : }
576 1017267 : if (value) {
577 : /*
578 : * talloc_move generates a type-punned warning here. As we
579 : * leave the function immediately, do a simple talloc_steal.
580 : */
581 1017266 : *value = (char *)talloc_steal(mem_ctx, blob.data);
582 1017266 : return true;
583 : }
584 1 : data_blob_free(&blob);
585 1 : return true;
586 : }
587 :
588 : /**
589 : * Set an entry in the cache file. If there's no such
590 : * one, then add it.
591 : *
592 : * @param keystr string that represents a key of this entry
593 : * @param value text representation value being cached
594 : * @param timeout time when the value is expired
595 : *
596 : * @retval true when entry is successfully stored
597 : * @retval false on failure
598 : **/
599 :
600 28149 : bool gencache_set(const char *keystr, const char *value, time_t timeout)
601 : {
602 28149 : DATA_BLOB blob = data_blob_const(value, strlen(value)+1);
603 28149 : return gencache_set_data_blob(keystr, blob, timeout);
604 : }
605 :
606 : struct gencache_iterate_blobs_state {
607 : void (*fn)(const char *key, DATA_BLOB value,
608 : time_t timeout, void *private_data);
609 : const char *pattern;
610 : void *private_data;
611 : };
612 :
613 75080 : static int gencache_iterate_blobs_fn(struct tdb_context *tdb, TDB_DATA key,
614 : TDB_DATA data, void *priv)
615 : {
616 75080 : struct gencache_iterate_blobs_state *state =
617 : (struct gencache_iterate_blobs_state *)priv;
618 0 : char *keystr;
619 75080 : char *free_key = NULL;
620 0 : time_t timeout;
621 0 : DATA_BLOB payload;
622 :
623 75080 : if (key.dptr[key.dsize-1] == '\0') {
624 75080 : keystr = (char *)key.dptr;
625 : } else {
626 : /* ensure 0-termination */
627 0 : keystr = talloc_strndup(talloc_tos(), (char *)key.dptr, key.dsize);
628 0 : free_key = keystr;
629 0 : if (keystr == NULL) {
630 0 : goto done;
631 : }
632 : }
633 :
634 75080 : if (!gencache_pull_timeout(key, data, &timeout, &payload)) {
635 0 : goto done;
636 : }
637 :
638 75080 : if (timeout == 0) {
639 : /* delete marker */
640 1 : goto done;
641 : }
642 :
643 75079 : if (fnmatch(state->pattern, keystr, 0) != 0) {
644 74206 : goto done;
645 : }
646 :
647 873 : DEBUG(10, ("Calling function with arguments "
648 : "(key=[%s], timeout=[%s])\n",
649 : keystr, timestring(talloc_tos(), timeout)));
650 :
651 873 : state->fn(keystr, payload, timeout, state->private_data);
652 :
653 75080 : done:
654 75080 : TALLOC_FREE(free_key);
655 75080 : return 0;
656 : }
657 :
658 234 : void gencache_iterate_blobs(void (*fn)(const char *key, DATA_BLOB value,
659 : time_t timeout, void *private_data),
660 : void *private_data, const char *pattern)
661 : {
662 0 : struct gencache_iterate_blobs_state state;
663 0 : int ret;
664 :
665 234 : if ((fn == NULL) || (pattern == NULL) || !gencache_init()) {
666 0 : return;
667 : }
668 :
669 234 : DEBUG(5, ("Searching cache keys with pattern %s\n", pattern));
670 :
671 234 : state.fn = fn;
672 234 : state.pattern = pattern;
673 234 : state.private_data = private_data;
674 :
675 234 : ret = tdb_traverse(cache->tdb, gencache_iterate_blobs_fn, &state);
676 :
677 234 : if ((ret == -1) && (tdb_error(cache->tdb) == TDB_ERR_CORRUPT)) {
678 0 : ret = tdb_wipe_all(cache->tdb);
679 0 : SMB_ASSERT(ret == 0);
680 : }
681 : }
682 :
683 : /**
684 : * Iterate through all entries which key matches to specified pattern
685 : *
686 : * @param fn pointer to the function that will be supplied with each single
687 : * matching cache entry (key, value and timeout) as an arguments
688 : * @param data void pointer to an arbitrary data that is passed directly to the fn
689 : * function on each call
690 : * @param keystr_pattern pattern the existing entries' keys are matched to
691 : *
692 : **/
693 :
694 : struct gencache_iterate_state {
695 : void (*fn)(const char *key, const char *value, time_t timeout,
696 : void *priv);
697 : void *private_data;
698 : };
699 :
700 873 : static void gencache_iterate_fn(const char *key, DATA_BLOB value,
701 : time_t timeout, void *private_data)
702 : {
703 873 : struct gencache_iterate_state *state =
704 : (struct gencache_iterate_state *)private_data;
705 0 : char *valstr;
706 873 : char *free_val = NULL;
707 :
708 873 : if (value.data[value.length-1] == '\0') {
709 867 : valstr = (char *)value.data;
710 : } else {
711 : /* ensure 0-termination */
712 6 : valstr = talloc_strndup(talloc_tos(), (char *)value.data, value.length);
713 6 : free_val = valstr;
714 6 : if (valstr == NULL) {
715 0 : goto done;
716 : }
717 : }
718 :
719 873 : DEBUG(10, ("Calling function with arguments "
720 : "(key=[%s], value=[%s], timeout=[%s])\n",
721 : key, valstr, timestring(talloc_tos(), timeout)));
722 :
723 873 : state->fn(key, valstr, timeout, state->private_data);
724 :
725 873 : done:
726 :
727 873 : TALLOC_FREE(free_val);
728 873 : }
729 :
730 234 : void gencache_iterate(void (*fn)(const char *key, const char *value,
731 : time_t timeout, void *dptr),
732 : void *private_data, const char *pattern)
733 : {
734 0 : struct gencache_iterate_state state;
735 :
736 234 : if (fn == NULL) {
737 0 : return;
738 : }
739 234 : state.fn = fn;
740 234 : state.private_data = private_data;
741 234 : gencache_iterate_blobs(gencache_iterate_fn, &state, pattern);
742 : }
|