3 * $Id: store_rebuild.cc,v 1.52 1998/10/09 17:46:37 wessels Exp $
5 * DEBUG: section 20 Store Rebuild Routines
6 * AUTHOR: Duane Wessels
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * ----------------------------------------------------------
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * the CREDITS file for full details.
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
38 #define STORE_META_BUFSZ 4096
41 typedef struct _rebuild_dir rebuild_dir
;
42 typedef int RBHD(rebuild_dir
* d
);
60 char fullpath
[SQUID_MAXPATHLEN
];
61 char fullfilename
[SQUID_MAXPATHLEN
];
64 struct storeRebuildState
{
65 rebuild_dir
*rebuild_dir
;
66 int objcount
; /* # objects successfully reloaded */
67 int expcount
; /* # objects expired */
68 int linecount
; /* # lines parsed from cache logfile */
69 int statcount
; /* # entries from directory walking */
70 int clashcount
; /* # swapfile clashes avoided */
71 int dupcount
; /* # duplicates purged */
72 int cancelcount
; /* # SWAP_LOG_DEL objects purged */
73 int invalid
; /* # bad lines */
74 int badflags
; /* # bad e->flags */
82 typedef struct valid_ctrl_t
{
89 static RBHD storeRebuildFromDirectory
;
90 static RBHD storeRebuildFromSwapLog
;
91 static void storeRebuildComplete(void);
92 static EVH storeRebuildADirectory
;
93 static int storeGetNextFile(rebuild_dir
*, int *sfileno
, int *size
);
94 static StoreEntry
*storeAddDiskRestore(const cache_key
* key
,
104 static AIOCB storeValidateComplete
;
107 storeRebuildFromDirectory(rebuild_dir
* d
)
109 LOCAL_ARRAY(char, hdr_buf
, DISK_PAGE_SIZE
);
110 StoreEntry
*e
= NULL
;
112 cache_key key
[MD5_DIGEST_CHARS
];
122 debug(20, 3) ("storeRebuildFromDirectory: DIR #%d\n", d
->dirn
);
123 for (count
= 0; count
< d
->speed
; count
++) {
125 fd
= storeGetNextFile(d
, &sfileno
, &size
);
127 debug(20, 1) ("storeRebuildFromDirectory: DIR #%d done!\n", d
->dirn
);
128 storeDirCloseTmpSwapLog(d
->dirn
);
129 store_rebuilding
= 0;
135 /* lets get file stats here */
136 if (fstat(fd
, &sb
) < 0) {
137 debug(20, 1) ("storeRebuildFromDirectory: fstat(FD %d): %s\n",
143 if ((++RebuildState
.statcount
& 0xFFFF) == 0)
144 debug(20, 1) (" %7d files opened so far.\n",
145 RebuildState
.statcount
);
146 debug(20, 9) ("file_in: fd=%d %08X\n", fd
, sfileno
);
147 Counter
.syscalls
.disk
.reads
++;
148 if (read(fd
, hdr_buf
, DISK_PAGE_SIZE
) < 0) {
149 debug(20, 1) ("storeRebuildFromDirectory: read(FD %d): %s\n",
158 tlv_list
= storeSwapMetaUnpack(hdr_buf
, &swap_hdr_len
);
159 if (tlv_list
== NULL
) {
160 debug(20, 1) ("storeRebuildFromDirectory: failed to get meta data\n");
161 storeUnlinkFileno(sfileno
);
164 debug(20, 3) ("storeRebuildFromDirectory: successful swap meta unpacking\n");
165 memset(key
, '\0', MD5_DIGEST_CHARS
);
166 memset(&tmpe
, '\0', sizeof(StoreEntry
));
167 for (t
= tlv_list
; t
; t
= t
->next
) {
170 assert(t
->length
== MD5_DIGEST_CHARS
);
171 xmemcpy(key
, t
->value
, MD5_DIGEST_CHARS
);
174 assert(t
->length
== STORE_HDR_METASIZE
);
175 xmemcpy(&tmpe
.timestamp
, t
->value
, STORE_HDR_METASIZE
);
181 storeSwapTLVFree(tlv_list
);
183 if (storeKeyNull(key
)) {
184 debug(20, 1) ("storeRebuildFromDirectory: NULL key\n");
185 storeUnlinkFileno(sfileno
);
190 if (tmpe
.swap_file_sz
== 0) {
191 tmpe
.swap_file_sz
= sb
.st_size
;
192 } else if (tmpe
.swap_file_sz
== sb
.st_size
- swap_hdr_len
) {
193 tmpe
.swap_file_sz
= sb
.st_size
;
194 } else if (tmpe
.swap_file_sz
!= sb
.st_size
) {
195 debug(20, 1) ("storeRebuildFromDirectory: SIZE MISMATCH %d!=%d\n",
196 tmpe
.swap_file_sz
, (int) sb
.st_size
);
197 storeUnlinkFileno(sfileno
);
200 if (EBIT_TEST(tmpe
.flags
, KEY_PRIVATE
)) {
201 storeUnlinkFileno(sfileno
);
202 RebuildState
.badflags
++;
206 if (e
&& e
->lastref
>= tmpe
.lastref
) {
207 /* key already exists, current entry is newer */
208 /* keep old, ignore new */
209 RebuildState
.dupcount
++;
211 } else if (NULL
!= e
) {
212 /* URL already exists, this swapfile not being used */
213 /* junk old, load new */
214 storeRelease(e
); /* release old entry */
215 RebuildState
.dupcount
++;
217 RebuildState
.objcount
++;
218 storeEntryDump(&tmpe
, 5);
219 e
= storeAddDiskRestore(key
,
226 tmpe
.refcount
, /* refcount */
227 tmpe
.flags
, /* flags */
234 storeRebuildFromSwapLog(rebuild_dir
* d
)
236 StoreEntry
*e
= NULL
;
238 size_t ss
= sizeof(storeSwapLogData
);
240 int used
; /* is swapfile already in use? */
241 int disk_entry_newer
; /* is the log entry newer than current entry? */
244 /* load a number of objects per invocation */
245 for (count
= 0; count
< d
->speed
; count
++) {
246 if (fread(&s
, ss
, 1, d
->log
) != 1) {
247 debug(20, 1) ("Done reading Cache Dir #%d swaplog (%d entries)\n",
251 storeDirCloseTmpSwapLog(d
->dirn
);
255 if (s
.op
<= SWAP_LOG_NOP
)
257 if (s
.op
>= SWAP_LOG_MAX
)
259 s
.swap_file_number
= storeDirProperFileno(d
->dirn
, s
.swap_file_number
);
260 debug(20, 3) ("storeRebuildFromSwapLog: %s %s %08X\n",
261 swap_log_op_str
[(int) s
.op
],
264 if (s
.op
== SWAP_LOG_ADD
) {
266 } else if (s
.op
== SWAP_LOG_DEL
) {
267 if ((e
= storeGet(s
.key
)) != NULL
) {
269 * Make sure we don't unlink the file, it might be
270 * in use by a subsequent entry. Also note that
271 * we don't have to subtract from store_swap_size
272 * because adding to store_swap_size happens in
273 * the cleanup procedure.
276 storeReleaseRequest(e
);
277 if (e
->swap_file_number
> -1) {
278 storeDirMapBitReset(e
->swap_file_number
);
279 e
->swap_file_number
= -1;
281 RebuildState
.objcount
--;
282 RebuildState
.cancelcount
++;
286 x
= log(++RebuildState
.bad_log_op
) / log(10.0);
287 if (0.0 == x
- (double) (int) x
)
288 debug(20, 1) ("WARNING: %d invalid swap log entries found\n",
289 RebuildState
.bad_log_op
);
290 RebuildState
.invalid
++;
293 if ((++RebuildState
.linecount
& 0xFFFF) == 0)
294 debug(20, 1) (" %7d Entries read so far.\n",
295 RebuildState
.linecount
);
296 if (!storeDirValidFileno(s
.swap_file_number
)) {
297 RebuildState
.invalid
++;
300 if (EBIT_TEST(s
.flags
, KEY_PRIVATE
)) {
301 RebuildState
.badflags
++;
305 used
= storeDirMapBitTest(s
.swap_file_number
);
306 /* If this URL already exists in the cache, does the swap log
307 * appear to have a newer entry? Compare 'lastref' from the
308 * swap log to e->lastref. */
309 disk_entry_newer
= e
? (s
.lastref
> e
->lastref
? 1 : 0) : 0;
310 if (used
&& !disk_entry_newer
) {
311 /* log entry is old, ignore it */
312 RebuildState
.clashcount
++;
314 } else if (used
&& e
&& e
->swap_file_number
== s
.swap_file_number
) {
315 /* swapfile taken, same URL, newer, update meta */
316 if (e
->store_status
== STORE_OK
) {
317 e
->lastref
= s
.timestamp
;
318 e
->timestamp
= s
.timestamp
;
319 e
->expires
= s
.expires
;
320 e
->lastmod
= s
.lastmod
;
322 e
->refcount
+= s
.refcount
;
324 debug_trap("storeRebuildFromSwapLog: bad condition");
325 debug(20, 1) ("\tSee %s:%d\n", __FILE__
, __LINE__
);
329 /* swapfile in use, not by this URL, log entry is newer */
330 /* This is sorta bad: the log entry should NOT be newer at this
331 * point. If the log is dirty, the filesize check should have
332 * caught this. If the log is clean, there should never be a
334 debug(20, 1) ("WARNING: newer swaplog entry for fileno %08X\n",
336 /* I'm tempted to remove the swapfile here just to be safe,
337 * but there is a bad race condition in the NOVM version if
338 * the swapfile has recently been opened for writing, but
339 * not yet opened for reading. Because we can't map
340 * swapfiles back to StoreEntrys, we don't know the state
341 * of the entry using that file. */
342 /* We'll assume the existing entry is valid, probably because
343 * were in a slow rebuild and the the swap file number got taken
344 * and the validation procedure hasn't run. */
345 assert(RebuildState
.need_to_validate
);
346 RebuildState
.clashcount
++;
348 } else if (e
&& !disk_entry_newer
) {
349 /* key already exists, current entry is newer */
350 /* keep old, ignore new */
351 RebuildState
.dupcount
++;
354 /* key already exists, this swapfile not being used */
355 /* junk old, load new */
357 storeReleaseRequest(e
);
358 if (e
->swap_file_number
> -1) {
359 storeDirMapBitReset(e
->swap_file_number
);
360 e
->swap_file_number
= -1;
362 RebuildState
.dupcount
++;
364 /* URL doesnt exist, swapfile not in use */
368 /* update store_swap_size */
369 RebuildState
.objcount
++;
370 e
= storeAddDiskRestore(s
.key
,
380 storeDirSwapLog(e
, SWAP_LOG_ADD
);
386 storeRebuildADirectory(void *unused
)
391 if ((d
= RebuildState
.rebuild_dir
) == NULL
) {
392 storeRebuildComplete();
395 count
= d
->rebuild_func(d
);
396 RebuildState
.rebuild_dir
= d
->next
;
400 for (D
= &RebuildState
.rebuild_dir
; *D
; D
= &(*D
)->next
);
404 if (opt_foreground_rebuild
)
405 storeRebuildADirectory(NULL
);
407 eventAdd("storeRebuild", storeRebuildADirectory
, NULL
, 0.0, 1);
412 storeConvertFile(const cache_key
* key
,
425 LOCAL_ARRAY(char, swapfilename
, SQUID_MAXPATHLEN
);
426 LOCAL_ARRAY(char, copybuf
, DISK_PAGE_SIZE
);
431 e
.swap_file_sz
= swap_file_sz
;
434 e
.refcount
= refcount
;
436 storeSwapFullPath(file_number
, swapfilename
);
437 fd_r
= file_open(swapfilename
, O_RDONLY
, NULL
, NULL
, NULL
);
440 safeunlink(swapfilename
, 1);
441 fd_w
= file_open(swapfilename
, O_CREAT
| O_WRONLY
| O_TRUNC
, NULL
, NULL
, NULL
);
442 tlv_list
= storeSwapMetaBuild(&e
);
443 buf
= storeSwapMetaPack(tlv_list
, &hdr_len
);
444 x
= write(fd_w
, buf
, hdr_len
);
446 y
= read(fd_r
, copybuf
, DISK_PAGE_SIZE
);
447 x
= write(fd_w
, copybuf
, y
);
452 storeSwapTLVFree(tlv_list
);
457 storeGetNextFile(rebuild_dir
* d
, int *sfileno
, int *size
)
461 debug(20, 3) ("storeGetNextFile: flag=%d, %d: /%02X/%02X\n",
468 while (fd
< 0 && d
->done
== 0) {
470 if (0 == d
->flag
) { /* initialize, open first file */
476 assert(Config
.cacheSwap
.n_configured
> 0);
478 if (0 == d
->in_dir
) { /* we need to read in a new directory */
479 snprintf(d
->fullpath
, SQUID_MAXPATHLEN
, "%s/%02X/%02X",
480 Config
.cacheSwap
.swapDirs
[d
->dirn
].path
,
481 d
->curlvl1
, d
->curlvl2
);
482 if (d
->flag
&& d
->td
!= NULL
)
484 d
->td
= opendir(d
->fullpath
);
486 debug(50, 1) ("storeGetNextFile: opendir: %s: %s\n",
487 d
->fullpath
, xstrerror());
490 d
->entry
= readdir(d
->td
); /* skip . and .. */
491 d
->entry
= readdir(d
->td
);
492 if (d
->entry
== NULL
&& errno
== ENOENT
)
493 debug(20, 1) ("storeGetNextFile: directory does not exist!.\n");
494 debug(20, 3) ("storeGetNextFile: Directory %s\n", d
->fullpath
);
496 if (d
->td
!= NULL
&& (d
->entry
= readdir(d
->td
)) != NULL
) {
498 if (sscanf(d
->entry
->d_name
, "%x", &d
->fn
) != 1) {
499 debug(20, 3) ("storeGetNextFile: invalid %s\n",
503 if (!storeFilenoBelongsHere(d
->fn
, d
->dirn
, d
->curlvl1
, d
->curlvl2
)) {
504 debug(20, 3) ("storeGetNextFile: %08X does not belong in %d/%d/%d\n",
505 d
->fn
, d
->dirn
, d
->curlvl1
, d
->curlvl2
);
508 d
->fn
= storeDirProperFileno(d
->dirn
, d
->fn
);
509 used
= storeDirMapBitTest(d
->fn
);
511 debug(20, 3) ("storeGetNextFile: Locked, continuing with next.\n");
514 snprintf(d
->fullfilename
, SQUID_MAXPATHLEN
, "%s/%s",
515 d
->fullpath
, d
->entry
->d_name
);
516 debug(20, 3) ("storeGetNextFile: Opening %s\n", d
->fullfilename
);
517 fd
= file_open(d
->fullfilename
, O_RDONLY
, NULL
, NULL
, NULL
);
519 debug(50, 1) ("storeGetNextFile: %s: %s\n", d
->fullfilename
, xstrerror());
523 if (++d
->curlvl2
< Config
.cacheSwap
.swapDirs
[d
->dirn
].l2
)
526 if (++d
->curlvl1
< Config
.cacheSwap
.swapDirs
[d
->dirn
].l1
)
535 /* Add a new object to the cache with empty memory copy and pointer to disk
536 * use to rebuild store from disk. */
538 storeAddDiskRestore(const cache_key
* key
,
549 StoreEntry
*e
= NULL
;
550 debug(20, 5) ("StoreAddDiskRestore: %s, fileno=%08X\n", storeKeyText(key
), file_number
);
551 /* if you call this you'd better be sure file_number is not
553 e
= new_StoreEntry(STORE_ENTRY_WITHOUT_MEMOBJ
, NULL
, NULL
);
554 e
->store_status
= STORE_OK
;
555 storeSetMemStatus(e
, NOT_IN_MEMORY
);
556 e
->swap_status
= SWAPOUT_DONE
;
557 e
->swap_file_number
= file_number
;
558 e
->swap_file_sz
= swap_file_sz
;
561 e
->lastref
= lastref
;
562 e
->timestamp
= timestamp
;
563 e
->expires
= expires
;
564 e
->lastmod
= lastmod
;
565 e
->refcount
= refcount
;
567 EBIT_SET(e
->flags
, ENTRY_CACHABLE
);
568 EBIT_CLR(e
->flags
, RELEASE_REQUEST
);
569 EBIT_CLR(e
->flags
, KEY_PRIVATE
);
570 e
->ping_status
= PING_NONE
;
571 EBIT_CLR(e
->flags
, ENTRY_VALIDATED
);
572 storeDirMapBitSet(e
->swap_file_number
);
573 storeHashInsert(e
, key
); /* do it after we clear KEY_PRIVATE */
578 storeCleanup(void *datanotused
)
580 static int bucketnum
= -1;
581 static int validnum
= 0;
582 static int store_errors
= 0;
584 hash_link
*link_ptr
= NULL
;
585 hash_link
*link_next
= NULL
;
586 if (++bucketnum
>= store_hash_buckets
) {
587 debug(20, 1) (" Completed Validation Procedure\n");
588 debug(20, 1) (" Validated %d Entries\n", validnum
);
589 debug(20, 1) (" store_swap_size = %dk\n", store_swap_size
);
590 store_rebuilding
= 0;
591 if (opt_store_doublecheck
)
592 assert(store_errors
== 0);
594 storeDigestNoteStoreReady();
597 link_next
= hash_get_bucket(store_table
, bucketnum
);
598 while (NULL
!= (link_ptr
= link_next
)) {
599 link_next
= link_ptr
->next
;
600 e
= (StoreEntry
*) link_ptr
;
601 if (EBIT_TEST(e
->flags
, ENTRY_VALIDATED
))
604 * Calling storeRelease() has no effect because we're
605 * still in 'store_rebuilding' state
607 if (e
->swap_file_number
< 0)
609 if (opt_store_doublecheck
) {
611 if (stat(storeSwapFullPath(e
->swap_file_number
, NULL
), &sb
) < 0) {
613 debug(20, 0) ("storeCleanup: MISSING SWAP FILE\n");
614 debug(20, 0) ("storeCleanup: FILENO %08X\n", e
->swap_file_number
);
615 debug(20, 0) ("storeCleanup: PATH %s\n",
616 storeSwapFullPath(e
->swap_file_number
, NULL
));
617 storeEntryDump(e
, 0);
620 if (e
->swap_file_sz
!= sb
.st_size
) {
622 debug(20, 0) ("storeCleanup: SIZE MISMATCH\n");
623 debug(20, 0) ("storeCleanup: FILENO %08X\n", e
->swap_file_number
);
624 debug(20, 0) ("storeCleanup: PATH %s\n",
625 storeSwapFullPath(e
->swap_file_number
, NULL
));
626 debug(20, 0) ("storeCleanup: ENTRY SIZE: %d, FILE SIZE: %d\n",
627 e
->swap_file_sz
, (int) sb
.st_size
);
628 storeEntryDump(e
, 0);
632 EBIT_SET(e
->flags
, ENTRY_VALIDATED
);
633 /* Only set the file bit if we know its a valid entry */
634 /* otherwise, set it in the validation procedure */
635 storeDirUpdateSwapSize(e
->swap_file_number
, e
->swap_file_sz
, 1);
636 if ((++validnum
& 0xFFFF) == 0)
637 debug(20, 1) (" %7d Entries Validated so far.\n", validnum
);
639 eventAdd("storeCleanup", storeCleanup
, NULL
, 0.0, 1);
643 storeValidate(StoreEntry
* e
, STVLDCB
* callback
, void *callback_data
, void *tag
)
651 assert(!EBIT_TEST(e
->flags
, ENTRY_VALIDATED
));
652 if (e
->swap_file_number
< 0) {
653 EBIT_CLR(e
->flags
, ENTRY_VALIDATED
);
654 callback(callback_data
, 0, 0);
657 path
= storeSwapFullPath(e
->swap_file_number
, NULL
);
658 sb
= xmalloc(sizeof(struct stat
));
659 ctrlp
= xmalloc(sizeof(valid_ctrl_t
));
662 ctrlp
->callback
= callback
;
663 ctrlp
->callback_data
= callback_data
;
665 aioStat(path
, sb
, storeValidateComplete
, ctrlp
, tag
);
668 * When evaluating the actual arguments in a function call, the order
669 * in which the arguments and the function expression are evaluated is
673 storeValidateComplete(-1, ctrlp
, x
, errno
);
679 storeValidateComplete(int fd
, void *data
, int retcode
, int errcode
)
681 valid_ctrl_t
*ctrlp
= data
;
682 struct stat
*sb
= ctrlp
->sb
;
683 StoreEntry
*e
= ctrlp
->e
;
686 if (retcode
== -2 && errcode
== -2) {
689 ctrlp
->callback(ctrlp
->callback_data
, retcode
, errcode
);
692 if (retcode
< 0 && errcode
== EWOULDBLOCK
) {
693 path
= storeSwapFullPath(e
->swap_file_number
, NULL
);
694 retcode
= stat(path
, sb
);
696 if (retcode
< 0 || sb
->st_size
== 0 || sb
->st_size
!= e
->swap_file_sz
) {
697 EBIT_CLR(e
->flags
, ENTRY_VALIDATED
);
699 EBIT_SET(e
->flags
, ENTRY_VALIDATED
);
700 storeDirUpdateSwapSize(e
->swap_file_number
, e
->swap_file_sz
, 1);
703 ctrlp
->callback(ctrlp
->callback_data
, retcode
, errcode
);
708 /* meta data recreated from disk image in swap directory */
710 storeRebuildComplete(void)
714 stop
= squid_curtime
;
715 r
= stop
- RebuildState
.start
;
716 debug(20, 1) ("Finished rebuilding storage disk.\n");
717 debug(20, 1) (" %7d Entries read from previous logfile.\n",
718 RebuildState
.linecount
);
719 debug(20, 1) (" %7d Entries scanned from swap files.\n",
720 RebuildState
.statcount
);
721 debug(20, 1) (" %7d Invalid entries.\n", RebuildState
.invalid
);
722 debug(20, 1) (" %7d With invalid flags.\n", RebuildState
.badflags
);
723 debug(20, 1) (" %7d Objects loaded.\n", RebuildState
.objcount
);
724 debug(20, 1) (" %7d Objects expired.\n", RebuildState
.expcount
);
725 debug(20, 1) (" %7d Objects cancelled.\n", RebuildState
.cancelcount
);
726 debug(20, 1) (" %7d Duplicate URLs purged.\n", RebuildState
.dupcount
);
727 debug(20, 1) (" %7d Swapfile clashes avoided.\n", RebuildState
.clashcount
);
728 debug(20, 1) (" Took %d seconds (%6.1f objects/sec).\n",
730 (double) RebuildState
.objcount
/ (r
> 0 ? r
: 1));
731 debug(20, 1) ("Beginning Validation Procedure\n");
732 eventAdd("storeCleanup", storeCleanup
, NULL
, 0.0, 1);
736 storeRebuildStart(void)
743 memset(&RebuildState
, '\0', sizeof(RebuildState
));
744 RebuildState
.start
= squid_curtime
;
745 for (i
= 0; i
< Config
.cacheSwap
.n_configured
; i
++) {
746 d
= xcalloc(1, sizeof(rebuild_dir
));
748 d
->speed
= opt_foreground_rebuild
? 1 << 30 : 50;
750 * If the swap.state file exists in the cache_dir, then
751 * we'll use storeRebuildFromSwapLog(), otherwise we'll
752 * use storeRebuildFromDirectory() to open up each file
753 * and suck in the meta data.
755 fp
= storeDirOpenTmpSwapLog(i
, &clean
, &zero
);
756 if (fp
== NULL
|| zero
) {
759 d
->rebuild_func
= storeRebuildFromDirectory
;
761 d
->rebuild_func
= storeRebuildFromSwapLog
;
765 d
->next
= RebuildState
.rebuild_dir
;
766 RebuildState
.rebuild_dir
= d
;
768 RebuildState
.need_to_validate
= 1;
769 debug(20, 1) ("Rebuilding storage in Cache Dir #%d (%s)\n",
770 i
, clean
? "CLEAN" : "DIRTY");
772 eventAdd("storeRebuild", storeRebuildADirectory
, NULL
, 0.0, 1);