2 Unix SMB/CIFS implementation.
4 Generic, persistent and shared between processes cache mechanism for use
5 by various parts of the Samba code
7 Copyright (C) Rafal Szczesniak 2002
8 Copyright (C) Volker Lendecke 2009
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.
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.
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/>.
27 #define DBGC_CLASS DBGC_TDB
29 #define TIMEOUT_LEN 12
30 #define CACHE_DATA_FMT "%12u/"
31 #define READ_CACHE_DATA_FMT_TEMPLATE "%%12u/%%%us"
32 #define BLOB_TYPE "DATA_BLOB"
33 #define BLOB_TYPE_LEN 9
35 static struct tdb_context
*cache
;
36 static struct tdb_context
*cache_notrans
;
40 * @brief Generic, persistent and shared between processes cache mechanism
41 * for use by various parts of the Samba code
43 * We have two cache files, one with transactions (gencache.tdb) and
44 * one without (gencache_notrans.tdb) that is CLEAR_IF_FIRST. Normal
45 * writes go to the gencache_notrans.tdb to be fast. Every 100 writes
46 * to the gencache (settable with gencache:stabilize_count) or every 5
47 * minutes (settable with gencache:stabilize_interval) we stabilize
48 * gencache_notrans.tdb with one transaction to gencache.tdb.
54 * Cache initialisation function. Opens cache tdb file or creates
55 * it if does not exist.
57 * @return true on successful initialisation of the cache or
61 static bool gencache_init(void)
63 char* cache_fname
= NULL
;
64 int open_flags
= O_RDWR
|O_CREAT
;
65 bool first_try
= true;
67 /* skip file open if it's already opened */
68 if (cache
) return True
;
70 cache_fname
= lock_path("gencache.tdb");
72 DEBUG(5, ("Opening cache file at %s\n", cache_fname
));
75 cache
= tdb_open_log(cache_fname
, 0, TDB_DEFAULT
, open_flags
, 0644);
78 ret
= tdb_check(cache
, NULL
, NULL
);
83 DEBUG(0, ("gencache_init: tdb_check(%s) failed\n",
88 DEBUG(0, ("gencache_init: tdb_check(%s) failed - retry after CLEAR_IF_FIRST\n",
90 cache
= tdb_open_log(cache_fname
, 0, TDB_CLEAR_IF_FIRST
, open_flags
, 0644);
99 if (!cache
&& (errno
== EACCES
)) {
100 open_flags
= O_RDONLY
;
101 cache
= tdb_open_log(cache_fname
, 0, TDB_DEFAULT
, open_flags
,
104 DEBUG(5, ("gencache_init: Opening cache file %s read-only.\n", cache_fname
));
109 DEBUG(5, ("Attempt to open gencache.tdb has failed.\n"));
113 cache_fname
= lock_path("gencache_notrans.tdb");
115 DEBUG(5, ("Opening cache file at %s\n", cache_fname
));
117 cache_notrans
= tdb_open_log(cache_fname
, 0,
118 TDB_CLEAR_IF_FIRST
| TDB_SEQNUM
,
120 if (cache_notrans
== NULL
) {
121 DEBUG(5, ("Opening %s failed: %s\n", cache_fname
,
131 static TDB_DATA
last_stabilize_key(void)
134 result
.dptr
= (uint8_t *)"@LAST_STABILIZED";
139 struct gencache_parse_last_stabilize_state
{
145 static int gencache_parse_last_stabilize_fn(TDB_DATA key
, TDB_DATA data
,
148 struct gencache_parse_last_stabilize_state
*state
=
149 (struct gencache_parse_last_stabilize_state
*)private_data
;
151 if ((data
.dptr
== NULL
) || (data
.dsize
== 0) ||
152 (data
.dptr
[data
.dsize
-1] != '\0')) {
156 if (sscanf((char *)data
.dptr
, "%d/%d", &state
->last_time
,
157 &state
->last_seqnum
) != 2) {
165 static bool gencache_parse_last_stabilize(time_t *last_time
, int *last_seqnum
)
167 struct gencache_parse_last_stabilize_state state
;
171 if (tdb_parse_record(cache_notrans
, last_stabilize_key(),
172 gencache_parse_last_stabilize_fn
,
179 *last_time
= state
.last_time
;
180 *last_seqnum
= state
.last_seqnum
;
185 * Set an entry in the cache file. If there's no such
188 * @param keystr string that represents a key of this entry
189 * @param blob DATA_BLOB value being cached
190 * @param timeout time when the value is expired
192 * @retval true when entry is successfuly stored
193 * @retval false on failure
196 bool gencache_set_data_blob(const char *keystr
, const DATA_BLOB
*blob
,
201 time_t last_stabilize
;
203 static int writecount
;
205 if (tdb_data_cmp(string_term_tdb_data(keystr
),
206 last_stabilize_key()) == 0) {
207 DEBUG(10, ("Can't store %s as a key\n", keystr
));
211 if ((keystr
== NULL
) || (blob
== NULL
)) {
215 if (!gencache_init()) return False
;
217 val
= talloc_asprintf(talloc_tos(), CACHE_DATA_FMT
, (int)timeout
);
221 val
= talloc_realloc(NULL
, val
, char, talloc_array_length(val
)-1);
225 val
= (char *)talloc_append_blob(NULL
, val
, *blob
);
230 DEBUG(10, ("Adding cache entry with key = %s and timeout ="
231 " %s (%d seconds %s)\n", keystr
, ctime(&timeout
),
232 (int)(timeout
- time(NULL
)),
233 timeout
> time(NULL
) ? "ahead" : "in the past"));
235 ret
= tdb_store_bystring(
236 cache_notrans
, keystr
,
237 make_tdb_data((uint8_t *)val
, talloc_array_length(val
)),
246 * Every 100 writes within a single process, stabilize the cache with
247 * a transaction. This is done to prevent a single transaction to
248 * become huge and chew lots of memory.
251 if (writecount
> lp_parm_int(-1, "gencache", "stabilize_count", 100)) {
252 gencache_stabilize();
258 * Every 5 minutes, call gencache_stabilize() to not let grow
259 * gencache_notrans.tdb too large.
262 if (gencache_parse_last_stabilize(&last_stabilize
, &last_seqnum
)) {
265 next
= last_stabilize
+ lp_parm_int(
266 -1, "gencache", "stabilize_interval", 300);
268 if (next
< time(NULL
)) {
269 gencache_stabilize();
278 * Delete one entry from the cache file.
280 * @param keystr string that represents a key of this entry
282 * @retval true upon successful deletion
283 * @retval false in case of failure
286 bool gencache_del(const char *keystr
)
288 bool exists
, was_expired
;
292 if (keystr
== NULL
) {
296 if (!gencache_init()) return False
;
298 DEBUG(10, ("Deleting cache entry (key = %s)\n", keystr
));
301 * We delete an element by setting its timeout to 0. This way we don't
302 * have to do a transaction on gencache.tdb every time we delete an
306 exists
= gencache_get_data_blob(keystr
, &value
, NULL
, &was_expired
);
308 if (!exists
&& was_expired
) {
310 * gencache_get_data_blob has implicitly deleted this
311 * entry, so we have to return success here.
317 data_blob_free(&value
);
318 ret
= gencache_set(keystr
, "", 0);
323 static bool gencache_pull_timeout(char *val
, time_t *pres
, char **pendptr
)
328 res
= strtol(val
, &endptr
, 10);
330 if ((endptr
== NULL
) || (*endptr
!= '/')) {
331 DEBUG(2, ("Invalid gencache data format: %s\n", val
));
337 if (pendptr
!= NULL
) {
344 * Get existing entry from the cache file.
346 * @param keystr string that represents a key of this entry
347 * @param blob DATA_BLOB that is filled with entry's blob
348 * @param timeout pointer to a time_t that is filled with entry's
351 * @retval true when entry is successfuly fetched
352 * @retval False for failure
355 bool gencache_get_data_blob(const char *keystr
, DATA_BLOB
*blob
,
356 time_t *timeout
, bool *was_expired
)
361 bool expired
= false;
363 if (keystr
== NULL
) {
367 if (tdb_data_cmp(string_term_tdb_data(keystr
),
368 last_stabilize_key()) == 0) {
369 DEBUG(10, ("Can't get %s as a key\n", keystr
));
373 if (!gencache_init()) {
377 databuf
= tdb_fetch_bystring(cache_notrans
, keystr
);
379 if (databuf
.dptr
== NULL
) {
380 databuf
= tdb_fetch_bystring(cache
, keystr
);
383 if (databuf
.dptr
== NULL
) {
384 DEBUG(10, ("Cache entry with key = %s couldn't be found \n",
389 if (!gencache_pull_timeout((char *)databuf
.dptr
, &t
, &endptr
)) {
390 SAFE_FREE(databuf
.dptr
);
394 DEBUG(10, ("Returning %s cache entry: key = %s, value = %s, "
395 "timeout = %s", t
> time(NULL
) ? "valid" :
396 "expired", keystr
, endptr
+1, ctime(&t
)));
400 SAFE_FREE(databuf
.dptr
);
404 if (t
<= time(NULL
)) {
407 * We're expired, delete the entry. We can't use gencache_del
408 * here, because that uses gencache_get_data_blob for checking
409 * the existence of a record. We know the thing exists and
410 * directly store an empty value with 0 timeout.
412 gencache_set(keystr
, "", 0);
414 SAFE_FREE(databuf
.dptr
);
423 databuf
.dsize
- PTR_DIFF(endptr
+1, databuf
.dptr
));
424 if (blob
->data
== NULL
) {
425 SAFE_FREE(databuf
.dptr
);
426 DEBUG(0, ("memdup failed\n"));
431 SAFE_FREE(databuf
.dptr
);
440 if (was_expired
!= NULL
) {
441 *was_expired
= expired
;
446 struct stabilize_state
{
450 static int stabilize_fn(struct tdb_context
*tdb
, TDB_DATA key
, TDB_DATA val
,
456 * Migrate the clear-if-first gencache data to the stable,
457 * transaction-based gencache.tdb
460 bool gencache_stabilize(void)
462 struct stabilize_state state
;
468 if (!gencache_init()) {
472 if (gencache_parse_last_stabilize(&last_time
, &last_seqnum
)
473 && (last_seqnum
== tdb_get_seqnum(cache_notrans
))) {
474 /* Nothing changed */
478 res
= tdb_transaction_start(cache
);
480 DEBUG(10, ("Could not start transaction on gencache.tdb: "
481 "%s\n", tdb_errorstr(cache
)));
484 res
= tdb_transaction_start(cache_notrans
);
486 tdb_transaction_cancel(cache
);
487 DEBUG(10, ("Could not start transaction on "
488 "gencache_notrans.tdb: %s\n",
489 tdb_errorstr(cache_notrans
)));
494 state
.written
= false;
496 res
= tdb_traverse(cache_notrans
, stabilize_fn
, &state
);
497 if ((res
== -1) || state
.error
) {
498 if ((tdb_transaction_cancel(cache_notrans
) == -1)
499 || (tdb_transaction_cancel(cache
) == -1)) {
500 smb_panic("tdb_transaction_cancel failed\n");
505 if (!state
.written
) {
506 if ((tdb_transaction_cancel(cache_notrans
) == -1)
507 || (tdb_transaction_cancel(cache
) == -1)) {
508 smb_panic("tdb_transaction_cancel failed\n");
513 res
= tdb_transaction_commit(cache
);
515 DEBUG(10, ("tdb_transaction_commit on gencache.tdb failed: "
516 "%s\n", tdb_errorstr(cache
)));
517 if (tdb_transaction_cancel(cache_notrans
) == -1) {
518 smb_panic("tdb_transaction_cancel failed\n");
523 res
= tdb_transaction_commit(cache_notrans
);
525 DEBUG(10, ("tdb_transaction_commit on gencache.tdb failed: "
526 "%s\n", tdb_errorstr(cache
)));
530 now
= talloc_asprintf(talloc_tos(), "%d/%d",
532 tdb_get_seqnum(cache_notrans
)+1);
534 tdb_store(cache_notrans
, last_stabilize_key(),
535 string_term_tdb_data(now
), 0);
542 static int stabilize_fn(struct tdb_context
*tdb
, TDB_DATA key
, TDB_DATA val
,
545 struct stabilize_state
*state
= (struct stabilize_state
*)priv
;
549 if (tdb_data_cmp(key
, last_stabilize_key()) == 0) {
553 if (!gencache_pull_timeout((char *)val
.dptr
, &timeout
, NULL
)) {
554 DEBUG(10, ("Ignoring invalid entry\n"));
557 if ((timeout
< time(NULL
)) || (val
.dsize
== 0)) {
558 res
= tdb_delete(cache
, key
);
559 if ((res
== -1) && (tdb_error(cache
) == TDB_ERR_NOEXIST
)) {
562 state
->written
= true;
565 res
= tdb_store(cache
, key
, val
, 0);
567 state
->written
= true;
572 DEBUG(10, ("Transfer to gencache.tdb failed: %s\n",
573 tdb_errorstr(cache
)));
578 if (tdb_delete(cache_notrans
, key
) == -1) {
579 DEBUG(10, ("tdb_delete from gencache_notrans.tdb failed: "
580 "%s\n", tdb_errorstr(cache_notrans
)));
588 * Get existing entry from the cache file.
590 * @param keystr string that represents a key of this entry
591 * @param valstr buffer that is allocated and filled with the entry value
592 * buffer's disposing must be done outside
593 * @param timeout pointer to a time_t that is filled with entry's
596 * @retval true when entry is successfuly fetched
597 * @retval False for failure
600 bool gencache_get(const char *keystr
, char **value
, time_t *ptimeout
)
605 ret
= gencache_get_data_blob(keystr
, &blob
, ptimeout
, NULL
);
609 if ((blob
.data
== NULL
) || (blob
.length
== 0)) {
610 SAFE_FREE(blob
.data
);
613 if (blob
.data
[blob
.length
-1] != '\0') {
614 /* Not NULL terminated, can't be a string */
615 SAFE_FREE(blob
.data
);
619 *value
= SMB_STRDUP((char *)blob
.data
);
620 data_blob_free(&blob
);
621 if (*value
== NULL
) {
626 data_blob_free(&blob
);
631 * Set an entry in the cache file. If there's no such
634 * @param keystr string that represents a key of this entry
635 * @param value text representation value being cached
636 * @param timeout time when the value is expired
638 * @retval true when entry is successfuly stored
639 * @retval false on failure
642 bool gencache_set(const char *keystr
, const char *value
, time_t timeout
)
644 DATA_BLOB blob
= data_blob_const(value
, strlen(value
)+1);
645 return gencache_set_data_blob(keystr
, &blob
, timeout
);
649 * Iterate through all entries which key matches to specified pattern
651 * @param fn pointer to the function that will be supplied with each single
652 * matching cache entry (key, value and timeout) as an arguments
653 * @param data void pointer to an arbitrary data that is passed directly to the fn
654 * function on each call
655 * @param keystr_pattern pattern the existing entries' keys are matched to
659 struct gencache_iterate_state
{
660 void (*fn
)(const char *key
, const char *value
, time_t timeout
,
667 static int gencache_iterate_fn(struct tdb_context
*tdb
, TDB_DATA key
,
668 TDB_DATA value
, void *priv
)
670 struct gencache_iterate_state
*state
=
671 (struct gencache_iterate_state
*)priv
;
673 char *free_key
= NULL
;
675 char *free_val
= NULL
;
680 if (tdb_data_cmp(key
, last_stabilize_key()) == 0) {
684 if (state
->in_persistent
&& tdb_exists(cache_notrans
, key
)) {
688 if (key
.dptr
[key
.dsize
-1] == '\0') {
689 keystr
= (char *)key
.dptr
;
691 /* ensure 0-termination */
692 keystr
= SMB_STRNDUP((char *)key
.dptr
, key
.dsize
);
696 if ((value
.dptr
== NULL
) || (value
.dsize
<= TIMEOUT_LEN
)) {
700 if (fnmatch(state
->pattern
, keystr
, 0) != 0) {
704 if (value
.dptr
[value
.dsize
-1] == '\0') {
705 valstr
= (char *)value
.dptr
;
707 /* ensure 0-termination */
708 valstr
= SMB_STRNDUP((char *)value
.dptr
, value
.dsize
);
712 u
= strtoul(valstr
, &timeout_endp
, 10);
714 if ((*timeout_endp
!= '/') || ((timeout_endp
-valstr
) != TIMEOUT_LEN
)) {
721 DEBUG(10, ("Calling function with arguments "
722 "(key = %s, value = %s, timeout = %s)\n",
723 keystr
, timeout_endp
, ctime(&timeout
)));
724 state
->fn(keystr
, timeout_endp
, timeout
, state
->priv
);
732 void gencache_iterate(void (*fn
)(const char* key
, const char *value
, time_t timeout
, void* dptr
),
733 void* data
, const char* keystr_pattern
)
735 struct gencache_iterate_state state
;
737 if ((fn
== NULL
) || (keystr_pattern
== NULL
)) {
741 if (!gencache_init()) return;
743 DEBUG(5, ("Searching cache keys with pattern %s\n", keystr_pattern
));
746 state
.pattern
= keystr_pattern
;
749 state
.in_persistent
= false;
750 tdb_traverse(cache_notrans
, gencache_iterate_fn
, &state
);
752 state
.in_persistent
= true;
753 tdb_traverse(cache
, gencache_iterate_fn
, &state
);