2 * $Id: store_rebuild.cc,v 1.46 1998/08/24 22:06:48 wessels Exp $
4 * DEBUG: section 20 Store Rebuild Routines
5 * AUTHOR: Duane Wessels
7 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
8 * ----------------------------------------------------------
10 * Squid is the result of efforts by numerous individuals from the
11 * Internet community. Development is led by Duane Wessels of the
12 * National Laboratory for Applied Network Research and funded by the
13 * National Science Foundation. Squid is Copyrighted (C) 1998 by
14 * Duane Wessels and the University of California San Diego. Please
15 * see the COPYRIGHT file for full details. Squid incorporates
16 * software developed and/or copyrighted by other sources. Please see
17 * the CREDITS file for full details.
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
37 #define STORE_META_BUFSZ 4096
40 typedef struct _rebuild_dir rebuild_dir
;
41 typedef int RBHD(rebuild_dir
* d
);
59 char fullpath
[SQUID_MAXPATHLEN
];
60 char fullfilename
[SQUID_MAXPATHLEN
];
63 struct storeRebuildState
{
64 rebuild_dir
*rebuild_dir
;
65 int objcount
; /* # objects successfully reloaded */
66 int expcount
; /* # objects expired */
67 int linecount
; /* # lines parsed from cache logfile */
68 int statcount
; /* # entries from directory walking */
69 int clashcount
; /* # swapfile clashes avoided */
70 int dupcount
; /* # duplicates purged */
71 int cancelcount
; /* # SWAP_LOG_DEL objects purged */
72 int invalid
; /* # bad lines */
73 int badflags
; /* # bad e->flags */
81 typedef struct valid_ctrl_t
{
88 static RBHD storeRebuildFromDirectory
;
89 static RBHD storeRebuildFromSwapLog
;
90 static void storeRebuildComplete(void);
91 static EVH storeRebuildADirectory
;
92 static int storeGetNextFile(rebuild_dir
*, int *sfileno
, int *size
);
93 static StoreEntry
*storeAddDiskRestore(const cache_key
* key
,
103 static AIOCB storeValidateComplete
;
106 storeRebuildFromDirectory(rebuild_dir
* d
)
108 LOCAL_ARRAY(char, hdr_buf
, DISK_PAGE_SIZE
);
109 StoreEntry
*e
= NULL
;
111 cache_key key
[MD5_DIGEST_CHARS
];
121 debug(20, 3) ("storeRebuildFromDirectory: DIR #%d\n", d
->dirn
);
122 for (count
= 0; count
< d
->speed
; count
++) {
124 fd
= storeGetNextFile(d
, &sfileno
, &size
);
126 debug(20, 1) ("storeRebuildFromDirectory: DIR #%d done!\n", d
->dirn
);
127 storeDirCloseTmpSwapLog(d
->dirn
);
128 store_rebuilding
= 0;
134 /* lets get file stats here */
135 if (fstat(fd
, &sb
) < 0) {
136 debug(20, 1) ("storeRebuildFromDirectory: fstat(FD %d): %s\n",
142 if ((++RebuildState
.statcount
& 0xFFFF) == 0)
143 debug(20, 1) (" %7d files opened so far.\n",
144 RebuildState
.statcount
);
145 debug(20, 9) ("file_in: fd=%d %08X\n", fd
, sfileno
);
146 if (read(fd
, hdr_buf
, DISK_PAGE_SIZE
) < 0) {
147 debug(20, 1) ("storeRebuildFromDirectory: read(FD %d): %s\n",
156 tlv_list
= storeSwapMetaUnpack(hdr_buf
, &swap_hdr_len
);
157 if (tlv_list
== NULL
) {
158 debug(20, 1) ("storeRebuildFromDirectory: failed to get meta data\n");
159 storeUnlinkFileno(sfileno
);
162 debug(20, 3) ("storeRebuildFromDirectory: successful swap meta unpacking\n");
163 memset(key
, '\0', MD5_DIGEST_CHARS
);
164 memset(&tmpe
, '\0', sizeof(StoreEntry
));
165 for (t
= tlv_list
; t
; t
= t
->next
) {
168 assert(t
->length
== MD5_DIGEST_CHARS
);
169 xmemcpy(key
, t
->value
, MD5_DIGEST_CHARS
);
172 assert(t
->length
== STORE_HDR_METASIZE
);
173 xmemcpy(&tmpe
.timestamp
, t
->value
, STORE_HDR_METASIZE
);
179 storeSwapTLVFree(tlv_list
);
181 if (storeKeyNull(key
)) {
182 debug(20, 1) ("storeRebuildFromDirectory: NULL key\n");
183 storeUnlinkFileno(sfileno
);
188 if (tmpe
.swap_file_sz
== 0) {
189 tmpe
.swap_file_sz
= sb
.st_size
;
190 } else if (tmpe
.swap_file_sz
== sb
.st_size
- swap_hdr_len
) {
191 tmpe
.swap_file_sz
= sb
.st_size
;
192 } else if (tmpe
.swap_file_sz
!= sb
.st_size
) {
193 debug(20, 1) ("storeRebuildFromDirectory: SIZE MISMATCH %d!=%d\n",
194 tmpe
.swap_file_sz
, (int) sb
.st_size
);
195 storeUnlinkFileno(sfileno
);
198 if (EBIT_TEST(tmpe
.flag
, KEY_PRIVATE
)) {
199 storeUnlinkFileno(sfileno
);
200 RebuildState
.badflags
++;
204 if (e
&& e
->lastref
>= tmpe
.lastref
) {
205 /* key already exists, current entry is newer */
206 /* keep old, ignore new */
207 RebuildState
.dupcount
++;
209 } else if (NULL
!= e
) {
210 /* URL already exists, this swapfile not being used */
211 /* junk old, load new */
212 storeRelease(e
); /* release old entry */
213 RebuildState
.dupcount
++;
215 RebuildState
.objcount
++;
216 storeEntryDump(&tmpe
, 5);
217 e
= storeAddDiskRestore(key
,
224 tmpe
.refcount
, /* refcount */
225 tmpe
.flag
, /* flags */
232 storeRebuildFromSwapLog(rebuild_dir
* d
)
234 StoreEntry
*e
= NULL
;
236 size_t ss
= sizeof(storeSwapLogData
);
238 int used
; /* is swapfile already in use? */
239 int disk_entry_newer
; /* is the log entry newer than current entry? */
242 /* load a number of objects per invocation */
243 for (count
= 0; count
< d
->speed
; count
++) {
244 if (fread(&s
, ss
, 1, d
->log
) != 1) {
245 debug(20, 1) ("Done reading Cache Dir #%d swaplog (%d entries)\n",
249 storeDirCloseTmpSwapLog(d
->dirn
);
253 if (s
.op
<= SWAP_LOG_NOP
)
255 if (s
.op
>= SWAP_LOG_MAX
)
257 s
.swap_file_number
= storeDirProperFileno(d
->dirn
, s
.swap_file_number
);
258 debug(20, 3) ("storeRebuildFromSwapLog: %s %s %08X\n",
259 swap_log_op_str
[(int) s
.op
],
262 if (s
.op
== SWAP_LOG_ADD
) {
264 } else if (s
.op
== SWAP_LOG_DEL
) {
265 if ((e
= storeGet(s
.key
)) != NULL
) {
267 * Make sure we don't unlink the file, it might be
268 * in use by a subsequent entry. Also note that
269 * we don't have to subtract from store_swap_size
270 * because adding to store_swap_size happens in
271 * the cleanup procedure.
274 storeSetPrivateKey(e
);
275 EBIT_SET(e
->flag
, RELEASE_REQUEST
);
276 if (e
->swap_file_number
> -1) {
277 storeDirMapBitReset(e
->swap_file_number
);
278 e
->swap_file_number
= -1;
280 RebuildState
.objcount
--;
281 RebuildState
.cancelcount
++;
285 x
= log(++RebuildState
.bad_log_op
) / log(10.0);
286 if (0.0 == x
- (double) (int) x
)
287 debug(20, 1) ("WARNING: %d invalid swap log entries found\n",
288 RebuildState
.bad_log_op
);
289 RebuildState
.invalid
++;
292 if ((++RebuildState
.linecount
& 0xFFFF) == 0)
293 debug(20, 1) (" %7d Entries read so far.\n",
294 RebuildState
.linecount
);
295 if (!storeDirValidFileno(s
.swap_file_number
)) {
296 RebuildState
.invalid
++;
299 if (EBIT_TEST(s
.flags
, KEY_PRIVATE
)) {
300 RebuildState
.badflags
++;
304 used
= storeDirMapBitTest(s
.swap_file_number
);
305 /* If this URL already exists in the cache, does the swap log
306 * appear to have a newer entry? Compare 'lastref' from the
307 * swap log to e->lastref. */
308 disk_entry_newer
= e
? (s
.lastref
> e
->lastref
? 1 : 0) : 0;
309 if (used
&& !disk_entry_newer
) {
310 /* log entry is old, ignore it */
311 RebuildState
.clashcount
++;
313 } else if (used
&& e
&& e
->swap_file_number
== s
.swap_file_number
) {
314 /* swapfile taken, same URL, newer, update meta */
315 /* XXX what if e->store_status != STORE_OK? */
316 e
->lastref
= s
.timestamp
;
317 e
->timestamp
= s
.timestamp
;
318 e
->expires
= s
.expires
;
319 e
->lastmod
= s
.lastmod
;
321 e
->refcount
+= s
.refcount
;
324 /* swapfile in use, not by this URL, log entry is newer */
325 /* This is sorta bad: the log entry should NOT be newer at this
326 * point. If the log is dirty, the filesize check should have
327 * caught this. If the log is clean, there should never be a
329 debug(20, 1) ("WARNING: newer swaplog entry for fileno %08X\n",
331 /* I'm tempted to remove the swapfile here just to be safe,
332 * but there is a bad race condition in the NOVM version if
333 * the swapfile has recently been opened for writing, but
334 * not yet opened for reading. Because we can't map
335 * swapfiles back to StoreEntrys, we don't know the state
336 * of the entry using that file. */
337 /* We'll assume the existing entry is valid, probably because
338 * were in a slow rebuild and the the swap file number got taken
339 * and the validation procedure hasn't run. */
340 assert(RebuildState
.need_to_validate
);
341 RebuildState
.clashcount
++;
343 } else if (e
&& !disk_entry_newer
) {
344 /* key already exists, current entry is newer */
345 /* keep old, ignore new */
346 RebuildState
.dupcount
++;
349 /* key already exists, this swapfile not being used */
350 /* junk old, load new */
352 storeSetPrivateKey(e
);
353 EBIT_SET(e
->flag
, RELEASE_REQUEST
);
354 if (e
->swap_file_number
> -1) {
355 storeDirMapBitReset(e
->swap_file_number
);
356 e
->swap_file_number
= -1;
358 RebuildState
.dupcount
++;
360 /* URL doesnt exist, swapfile not in use */
364 /* update store_swap_size */
365 RebuildState
.objcount
++;
366 e
= storeAddDiskRestore(s
.key
,
376 storeDirSwapLog(e
, SWAP_LOG_ADD
);
382 storeRebuildADirectory(void *unused
)
387 if ((d
= RebuildState
.rebuild_dir
) == NULL
) {
388 storeRebuildComplete();
391 count
= d
->rebuild_func(d
);
392 RebuildState
.rebuild_dir
= d
->next
;
396 for (D
= &RebuildState
.rebuild_dir
; *D
; D
= &(*D
)->next
);
400 if (opt_foreground_rebuild
)
401 storeRebuildADirectory(NULL
);
403 eventAdd("storeRebuild", storeRebuildADirectory
, NULL
, 0.0, 1);
408 storeConvertFile(const cache_key
* key
,
421 LOCAL_ARRAY(char, swapfilename
, SQUID_MAXPATHLEN
);
422 LOCAL_ARRAY(char, copybuf
, DISK_PAGE_SIZE
);
427 e
.swap_file_sz
= swap_file_sz
;
430 e
.refcount
= refcount
;
432 storeSwapFullPath(file_number
, swapfilename
);
433 fd_r
= file_open(swapfilename
, O_RDONLY
, NULL
, NULL
, NULL
);
436 safeunlink(swapfilename
, 1);
437 fd_w
= file_open(swapfilename
, O_CREAT
| O_WRONLY
| O_TRUNC
, NULL
, NULL
, NULL
);
438 tlv_list
= storeSwapMetaBuild(&e
);
439 buf
= storeSwapMetaPack(tlv_list
, &hdr_len
);
440 x
= write(fd_w
, buf
, hdr_len
);
442 y
= read(fd_r
, copybuf
, DISK_PAGE_SIZE
);
443 x
= write(fd_w
, copybuf
, y
);
448 storeSwapTLVFree(tlv_list
);
453 storeGetNextFile(rebuild_dir
* d
, int *sfileno
, int *size
)
457 debug(20, 3) ("storeGetNextFile: flag=%d, %d: /%02X/%02X\n",
464 while (fd
< 0 && d
->done
== 0) {
466 if (0 == d
->flag
) { /* initialize, open first file */
472 assert(Config
.cacheSwap
.n_configured
> 0);
474 if (0 == d
->in_dir
) { /* we need to read in a new directory */
475 snprintf(d
->fullpath
, SQUID_MAXPATHLEN
, "%s/%02X/%02X",
476 Config
.cacheSwap
.swapDirs
[d
->dirn
].path
,
477 d
->curlvl1
, d
->curlvl2
);
478 if (d
->flag
&& d
->td
!= NULL
)
480 d
->td
= opendir(d
->fullpath
);
482 debug(50, 1) ("storeGetNextFile: opendir: %s: %s\n",
483 d
->fullpath
, xstrerror());
486 d
->entry
= readdir(d
->td
); /* skip . and .. */
487 d
->entry
= readdir(d
->td
);
488 if (d
->entry
== NULL
&& errno
== ENOENT
)
489 debug(20, 1) ("storeGetNextFile: directory does not exist!.\n");
490 debug(20, 3) ("storeGetNextFile: Directory %s\n", d
->fullpath
);
492 if (d
->td
!= NULL
&& (d
->entry
= readdir(d
->td
)) != NULL
) {
494 if (sscanf(d
->entry
->d_name
, "%x", &d
->fn
) != 1) {
495 debug(20, 3) ("storeGetNextFile: invalid %s\n",
499 if (!storeFilenoBelongsHere(d
->fn
, d
->dirn
, d
->curlvl1
, d
->curlvl2
)) {
500 debug(20, 3) ("storeGetNextFile: %08X does not belong in %d/%d/%d\n",
501 d
->fn
, d
->dirn
, d
->curlvl1
, d
->curlvl2
);
504 d
->fn
= storeDirProperFileno(d
->dirn
, d
->fn
);
505 used
= storeDirMapBitTest(d
->fn
);
507 debug(20, 3) ("storeGetNextFile: Locked, continuing with next.\n");
510 snprintf(d
->fullfilename
, SQUID_MAXPATHLEN
, "%s/%s",
511 d
->fullpath
, d
->entry
->d_name
);
512 debug(20, 3) ("storeGetNextFile: Opening %s\n", d
->fullfilename
);
513 fd
= file_open(d
->fullfilename
, O_RDONLY
, NULL
, NULL
, NULL
);
515 debug(50, 1) ("storeGetNextFile: %s: %s\n", d
->fullfilename
, xstrerror());
519 if (++d
->curlvl2
< Config
.cacheSwap
.swapDirs
[d
->dirn
].l2
)
522 if (++d
->curlvl1
< Config
.cacheSwap
.swapDirs
[d
->dirn
].l1
)
531 /* Add a new object to the cache with empty memory copy and pointer to disk
532 * use to rebuild store from disk. */
534 storeAddDiskRestore(const cache_key
* key
,
545 StoreEntry
*e
= NULL
;
546 debug(20, 5) ("StoreAddDiskRestore: %s, fileno=%08X\n", storeKeyText(key
), file_number
);
547 /* if you call this you'd better be sure file_number is not
549 e
= new_StoreEntry(STORE_ENTRY_WITHOUT_MEMOBJ
, NULL
, NULL
);
550 e
->store_status
= STORE_OK
;
551 storeSetMemStatus(e
, NOT_IN_MEMORY
);
552 e
->swap_status
= SWAPOUT_DONE
;
553 e
->swap_file_number
= file_number
;
554 e
->swap_file_sz
= swap_file_sz
;
557 e
->lastref
= lastref
;
558 e
->timestamp
= timestamp
;
559 e
->expires
= expires
;
560 e
->lastmod
= lastmod
;
561 e
->refcount
= refcount
;
563 EBIT_SET(e
->flag
, ENTRY_CACHABLE
);
564 EBIT_CLR(e
->flag
, RELEASE_REQUEST
);
565 EBIT_CLR(e
->flag
, KEY_PRIVATE
);
566 e
->ping_status
= PING_NONE
;
567 EBIT_CLR(e
->flag
, ENTRY_VALIDATED
);
568 storeDirMapBitSet(e
->swap_file_number
);
569 storeHashInsert(e
, key
); /* do it after we clear KEY_PRIVATE */
574 storeCleanup(void *datanotused
)
576 static int bucketnum
= -1;
577 static int validnum
= 0;
578 static int store_errors
= 0;
580 hash_link
*link_ptr
= NULL
;
581 hash_link
*link_next
= NULL
;
582 if (++bucketnum
>= store_hash_buckets
) {
583 debug(20, 1) (" Completed Validation Procedure\n");
584 debug(20, 1) (" Validated %d Entries\n", validnum
);
585 debug(20, 1) (" store_swap_size = %dk\n", store_swap_size
);
586 store_rebuilding
= 0;
587 if (opt_store_doublecheck
)
588 assert(store_errors
== 0);
590 storeDigestNoteStoreReady();
593 link_next
= hash_get_bucket(store_table
, bucketnum
);
594 while (NULL
!= (link_ptr
= link_next
)) {
595 link_next
= link_ptr
->next
;
596 e
= (StoreEntry
*) link_ptr
;
597 if (EBIT_TEST(e
->flag
, ENTRY_VALIDATED
))
600 * Calling storeRelease() has no effect because we're
601 * still in 'store_rebuilding' state
603 if (e
->swap_file_number
< 0)
605 if (opt_store_doublecheck
) {
607 if (stat(storeSwapFullPath(e
->swap_file_number
, NULL
), &sb
) < 0) {
609 debug(20, 0) ("storeCleanup: MISSING SWAP FILE\n");
610 debug(20, 0) ("storeCleanup: FILENO %08X\n", e
->swap_file_number
);
611 debug(20, 0) ("storeCleanup: PATH %s\n",
612 storeSwapFullPath(e
->swap_file_number
, NULL
));
613 storeEntryDump(e
, 0);
616 if (e
->swap_file_sz
!= sb
.st_size
) {
618 debug(20, 0) ("storeCleanup: SIZE MISMATCH\n");
619 debug(20, 0) ("storeCleanup: FILENO %08X\n", e
->swap_file_number
);
620 debug(20, 0) ("storeCleanup: PATH %s\n",
621 storeSwapFullPath(e
->swap_file_number
, NULL
));
622 debug(20, 0) ("storeCleanup: ENTRY SIZE: %d, FILE SIZE: %d\n",
623 e
->swap_file_sz
, (int) sb
.st_size
);
624 storeEntryDump(e
, 0);
628 EBIT_SET(e
->flag
, ENTRY_VALIDATED
);
629 /* Only set the file bit if we know its a valid entry */
630 /* otherwise, set it in the validation procedure */
631 storeDirUpdateSwapSize(e
->swap_file_number
, e
->swap_file_sz
, 1);
632 if ((++validnum
& 0xFFFF) == 0)
633 debug(20, 1) (" %7d Entries Validated so far.\n", validnum
);
635 eventAdd("storeCleanup", storeCleanup
, NULL
, 0.0, 1);
639 storeValidate(StoreEntry
* e
, STVLDCB
* callback
, void *callback_data
, void *tag
)
647 assert(!EBIT_TEST(e
->flag
, ENTRY_VALIDATED
));
648 if (e
->swap_file_number
< 0) {
649 EBIT_CLR(e
->flag
, ENTRY_VALIDATED
);
650 callback(callback_data
, 0, 0);
653 path
= storeSwapFullPath(e
->swap_file_number
, NULL
);
654 sb
= xmalloc(sizeof(struct stat
));
655 ctrlp
= xmalloc(sizeof(valid_ctrl_t
));
658 ctrlp
->callback
= callback
;
659 ctrlp
->callback_data
= callback_data
;
661 aioStat(path
, sb
, storeValidateComplete
, ctrlp
, tag
);
664 * When evaluating the actual arguments in a function call, the order
665 * in which the arguments and the function expression are evaluated is
669 storeValidateComplete(-1, ctrlp
, x
, errno
);
675 storeValidateComplete(int fd
, void *data
, int retcode
, int errcode
)
677 valid_ctrl_t
*ctrlp
= data
;
678 struct stat
*sb
= ctrlp
->sb
;
679 StoreEntry
*e
= ctrlp
->e
;
682 if (retcode
== -2 && errcode
== -2) {
685 ctrlp
->callback(ctrlp
->callback_data
, retcode
, errcode
);
688 if (retcode
< 0 && errcode
== EWOULDBLOCK
) {
689 path
= storeSwapFullPath(e
->swap_file_number
, NULL
);
690 retcode
= stat(path
, sb
);
692 if (retcode
< 0 || sb
->st_size
== 0 || sb
->st_size
!= e
->swap_file_sz
) {
693 EBIT_CLR(e
->flag
, ENTRY_VALIDATED
);
695 EBIT_SET(e
->flag
, ENTRY_VALIDATED
);
696 storeDirUpdateSwapSize(e
->swap_file_number
, e
->swap_file_sz
, 1);
699 ctrlp
->callback(ctrlp
->callback_data
, retcode
, errcode
);
704 /* meta data recreated from disk image in swap directory */
706 storeRebuildComplete(void)
710 stop
= squid_curtime
;
711 r
= stop
- RebuildState
.start
;
712 debug(20, 1) ("Finished rebuilding storage disk.\n");
713 debug(20, 1) (" %7d Entries read from previous logfile.\n",
714 RebuildState
.linecount
);
715 debug(20, 1) (" %7d Entries scanned from swap files.\n",
716 RebuildState
.statcount
);
717 debug(20, 1) (" %7d Invalid entries.\n", RebuildState
.invalid
);
718 debug(20, 1) (" %7d With invalid flags.\n", RebuildState
.badflags
);
719 debug(20, 1) (" %7d Objects loaded.\n", RebuildState
.objcount
);
720 debug(20, 1) (" %7d Objects expired.\n", RebuildState
.expcount
);
721 debug(20, 1) (" %7d Objects cancelled.\n", RebuildState
.cancelcount
);
722 debug(20, 1) (" %7d Duplicate URLs purged.\n", RebuildState
.dupcount
);
723 debug(20, 1) (" %7d Swapfile clashes avoided.\n", RebuildState
.clashcount
);
724 debug(20, 1) (" Took %d seconds (%6.1f objects/sec).\n",
726 (double) RebuildState
.objcount
/ (r
> 0 ? r
: 1));
727 debug(20, 1) ("Beginning Validation Procedure\n");
728 eventAdd("storeCleanup", storeCleanup
, NULL
, 0.0, 1);
732 storeRebuildStart(void)
739 memset(&RebuildState
, '\0', sizeof(RebuildState
));
740 RebuildState
.start
= squid_curtime
;
741 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
742 d
= xcalloc(1, sizeof(rebuild_dir
));
744 d
->speed
= opt_foreground_rebuild
? 1 << 30 : 50;
746 * If the swap.state file exists in the cache_dir, then
747 * we'll use storeRebuildFromSwapLog(), otherwise we'll
748 * use storeRebuildFromDirectory() to open up each file
749 * and suck in the meta data.
751 fp
= storeDirOpenTmpSwapLog(i
, &clean
, &zero
);
752 if (fp
== NULL
|| zero
) {
755 d
->rebuild_func
= storeRebuildFromDirectory
;
757 d
->rebuild_func
= storeRebuildFromSwapLog
;
761 d
->next
= RebuildState
.rebuild_dir
;
762 RebuildState
.rebuild_dir
= d
;
764 RebuildState
.need_to_validate
= 1;
765 debug(20, 1) ("Rebuilding storage in Cache Dir #%d (%s)\n",
766 i
, clean
? "CLEAN" : "DIRTY");
768 eventAdd("storeRebuild", storeRebuildADirectory
, NULL
, 0.0, 1);