]> git.ipfire.org Git - thirdparty/squid.git/blame - src/fs/ufs/ufscommon.cc
Bug #2016 fix: Prevent BodyPipe async calls from getting seemingly
[thirdparty/squid.git] / src / fs / ufs / ufscommon.cc
CommitLineData
4d6d905e 1/*
7847e1fb 2 * $Id: ufscommon.cc,v 1.12 2007/08/01 23:21:07 amosjeffries Exp $
c8f4eac4 3 * vim: set et :
4d6d905e 4 *
5 * DEBUG: section 47 Store Directory Routines
51ee7c82 6 * AUTHOR: Robert Collins
4d6d905e 7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
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.
24 *
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.
29 *
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.
33 *
51ee7c82 34 * Copyright (c) 2003, Robert Collins <robertc@squid-cache.org>
4d6d905e 35 */
36
37#include "ufscommon.h"
e6ccf245 38#include "Store.h"
528b2c61 39#include "fde.h"
40#include "StoreMeta.h"
41#include "Generic.h"
42#include "StoreMetaUnpacker.h"
d3b3ab85 43#include "RefCount.h"
51ee7c82 44#include "StoreSwapLogData.h"
4d6d905e 45
d3b3ab85 46CBDATA_CLASS_INIT(RebuildState);
4d6d905e 47
c8f4eac4 48RebuildState::RebuildState (RefCount<UFSSwapDir> aSwapDir) : sd (aSwapDir), e(NULL), fromLog(true), _done (false)
4d6d905e 49{
c8f4eac4 50 speed = opt_foreground_rebuild ? 1 << 30 : 50;
51 /*
52 * If the swap.state file exists in the cache_dir, then
53 * we'll use commonUfsDirRebuildFromSwapLog(), otherwise we'll
54 * use commonUfsDirRebuildFromDirectory() to open up each file
55 * and suck in the meta data.
56 */
57 int clean = 0;
58 int zeroLengthLog = 0;
59 FILE *fp = sd->openTmpSwapLog(&clean, &zeroLengthLog);
60
61 if (fp == NULL || zeroLengthLog) {
62 fromLog = false;
63
64 if (fp != NULL)
65 fclose(fp);
66
67 } else {
68 fromLog = true;
69 log = fp;
70 flags.clean = (unsigned int) clean;
71 }
62e76326 72
c8f4eac4 73 if (!clean)
74 flags.need_to_validate = 1;
75
bf8fe701 76 debugs(47, 1, "Rebuilding storage in " << sd->path << " (" << (clean ? "CLEAN" : "DIRTY") << ")");
4d6d905e 77}
78
d3b3ab85 79RebuildState::~RebuildState()
4d6d905e 80{
d3b3ab85 81 sd->closeTmpSwapLog();
7847e1fb 82 /* now thats closed we DONT want to keep our secondary pointer to it */
83 log = NULL;
4d6d905e 84}
85
86void
c8f4eac4 87RebuildState::RebuildStep(void *data)
4d6d905e 88{
d3b3ab85 89 RebuildState *rb = (RebuildState *)data;
c8f4eac4 90 rb->rebuildStep();
91
92 if (!rb->isDone())
93 eventAdd("storeRebuild", RebuildStep, rb, 0.0, 1);
94 else {
bef81ea5 95 StoreController::store_dirs_rebuilding--;
c8f4eac4 96 storeRebuildComplete(&rb->counts);
97 delete rb;
98 }
99}
100
101void
102RebuildState::rebuildStep()
103{
104 if (fromLog)
105 rebuildFromSwapLog();
106 else
107 rebuildFromDirectory();
4d6d905e 108}
109
528b2c61 110struct InitStoreEntry : public unary_function<StoreMeta, void>
111{
112 InitStoreEntry(StoreEntry *anEntry, cache_key *aKey):what(anEntry),index(aKey){}
62e76326 113
114 void operator()(StoreMeta const &x)
115 {
116 switch (x.getType()) {
117
118 case STORE_META_KEY:
119 assert(x.length == MD5_DIGEST_CHARS);
120 xmemcpy(index, x.value, MD5_DIGEST_CHARS);
121 break;
122
123 case STORE_META_STD:
124 assert(x.length == STORE_HDR_METASIZE);
125 xmemcpy(&what->timestamp, x.value, STORE_HDR_METASIZE);
126 break;
127
128 default:
129 break;
130 }
528b2c61 131 }
62e76326 132
528b2c61 133 StoreEntry *what;
134 cache_key *index;
135};
136
4d6d905e 137void
d3b3ab85 138RebuildState::rebuildFromDirectory()
4d6d905e 139{
4d6d905e 140 LOCAL_ARRAY(char, hdr_buf, SM_PAGE_SIZE);
822b78b5 141 currentEntry(NULL);
4d6d905e 142 StoreEntry tmpe;
143 cache_key key[MD5_DIGEST_CHARS];
62e76326 144
4d6d905e 145 struct stat sb;
146 int swap_hdr_len;
147 int fd = -1;
528b2c61 148 StoreMeta *tlv_list;
d3b3ab85 149 assert(this != NULL);
bf8fe701 150 debugs(47, 3, "commonUfsDirRebuildFromDirectory: DIR #" << sd->index);
62e76326 151
d3b3ab85 152 for (int count = 0; count < speed; count++) {
62e76326 153 assert(fd == -1);
154 sfileno filn = 0;
155 int size;
156 fd = getNextFile(&filn, &size);
157
158 if (fd == -2) {
bf8fe701 159 debugs(47, 1, "Done scanning " << sd->path << " swaplog (" << n_read << " entries)");
c8f4eac4 160 _done = true;
62e76326 161 return;
162 } else if (fd < 0) {
163 continue;
164 }
165
166 assert(fd > -1);
167 /* lets get file stats here */
168
169 if (fstat(fd, &sb) < 0) {
bf8fe701 170 debugs(47, 1, "commonUfsDirRebuildFromDirectory: fstat(FD " << fd << "): " << xstrerror());
62e76326 171 file_close(fd);
172 store_open_disk_fd--;
173 fd = -1;
174 continue;
175 }
176
177 if ((++counts.scancount & 0xFFFF) == 0)
bf8fe701 178 debugs(47, 3, " " << sd->path << " " << std::setw(7) << counts.scancount << " files opened so far.");
179 debugs(47, 9, "file_in: fd=" << fd << " "<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << filn);
62e76326 180
62e76326 181
182 statCounter.syscalls.disk.reads++;
183
184 int len;
185
186 if ((len = FD_READ_METHOD(fd, hdr_buf, SM_PAGE_SIZE)) < 0) {
bf8fe701 187 debugs(47, 1, "commonUfsDirRebuildFromDirectory: read(FD " << fd << "): " << xstrerror());
62e76326 188 file_close(fd);
189 store_open_disk_fd--;
190 fd = -1;
191 continue;
192 }
193
194 file_close(fd);
195 store_open_disk_fd--;
196 fd = -1;
197 swap_hdr_len = 0;
62e76326 198
528b2c61 199 StoreMetaUnpacker aBuilder(hdr_buf, len, &swap_hdr_len);
62e76326 200
201 if (!aBuilder.isBufferSane()) {
bf8fe701 202 debugs(47, 1, "commonUfsDirRebuildFromDirectory: Swap data buffer length is not sane.");
62e76326 203 /* XXX shouldn't this be a call to commonUfsUnlink ? */
204 sd->unlinkFile ( filn);
205 continue;
206 }
207
528b2c61 208 tlv_list = aBuilder.createStoreMeta ();
62e76326 209
210 if (tlv_list == NULL) {
bf8fe701 211 debugs(47, 1, "commonUfsDirRebuildFromDirectory: failed to get meta data");
62e76326 212 /* XXX shouldn't this be a call to commonUfsUnlink ? */
213 sd->unlinkFile (filn);
214 continue;
215 }
216
bf8fe701 217 debugs(47, 3, "commonUfsDirRebuildFromDirectory: successful swap meta unpacking");
62e76326 218 memset(key, '\0', MD5_DIGEST_CHARS);
219 memset(&tmpe, '\0', sizeof(StoreEntry));
220 InitStoreEntry visitor(&tmpe, key);
221 for_each(*tlv_list, visitor);
222 storeSwapTLVFree(tlv_list);
223 tlv_list = NULL;
224
225 if (storeKeyNull(key)) {
bf8fe701 226 debugs(47, 1, "commonUfsDirRebuildFromDirectory: NULL key");
62e76326 227 sd->unlinkFile(filn);
228 continue;
229 }
230
231 tmpe.key = key;
232 /* check sizes */
233
234 if (tmpe.swap_file_sz == 0) {
235 tmpe.swap_file_sz = (size_t) sb.st_size;
236 } else if (tmpe.swap_file_sz == (size_t)(sb.st_size - swap_hdr_len)) {
237 tmpe.swap_file_sz = (size_t) sb.st_size;
238 } else if (tmpe.swap_file_sz != (size_t)sb.st_size) {
bf8fe701 239 debugs(47, 1, "commonUfsDirRebuildFromDirectory: SIZE MISMATCH " <<
240 (long int) tmpe.swap_file_sz << "!=" <<
241 (long int) sb.st_size);
242
62e76326 243 sd->unlinkFile(filn);
244 continue;
245 }
246
247 if (EBIT_TEST(tmpe.flags, KEY_PRIVATE)) {
248 sd->unlinkFile(filn);
249 counts.badflags++;
250 continue;
251 }
252
c8f4eac4 253 /* this needs to become
254 * 1) unpack url
255 * 2) make synthetic request with headers ?? or otherwise search
256 * for a matching object in the store
257 * TODO FIXME change to new async api
258 * TODO FIXME I think there is a race condition here with the
259 * async api :
260 * store A reads in object foo, searchs for it, and finds nothing.
261 * store B reads in object foo, searchs for it, finds nothing.
262 * store A gets called back with nothing, so registers the object
263 * store B gets called back with nothing, so registers the object,
264 * which will conflict when the in core index gets around to scanning
265 * store B.
266 *
267 * this suggests that rather than searching for duplicates, the
268 * index rebuild should just assume its the most recent accurate
269 * store entry and whoever indexes the stores handles duplicates.
270 */
271 e = Store::Root().get(key);
62e76326 272
273 if (e && e->lastref >= tmpe.lastref) {
274 /* key already exists, current entry is newer */
275 /* keep old, ignore new */
276 counts.dupcount++;
277 continue;
278 } else if (NULL != e) {
279 /* URL already exists, this swapfile not being used */
280 /* junk old, load new */
5f33b71d 281 e->release(); /* release old entry */
62e76326 282 counts.dupcount++;
283 }
284
285 counts.objcount++;
3900307b 286 tmpe.dump(5);
c8f4eac4 287 currentEntry(sd->addDiskRestore(key,
288 filn,
289 tmpe.swap_file_sz,
290 tmpe.expires,
291 tmpe.timestamp,
292 tmpe.lastref,
293 tmpe.lastmod,
294 tmpe.refcount, /* refcount */
295 tmpe.flags, /* flags */
296 (int) flags.clean));
297 storeDirSwapLog(currentEntry(), SWAP_LOG_ADD);
4d6d905e 298 }
62e76326 299
d3b3ab85 300}
301
822b78b5 302StoreEntry *
303RebuildState::currentEntry() const
304{
305 return e;
306}
307
308void
309RebuildState::currentEntry(StoreEntry *newValue)
310{
311 e = newValue;
312}
313
d3b3ab85 314void
315RebuildState::rebuildFromSwapLog()
316{
822b78b5 317 currentEntry (NULL);
4d6d905e 318 double x;
4d6d905e 319 /* load a number of objects per invocation */
62e76326 320
d3b3ab85 321 for (int count = 0; count < speed; count++) {
c8f4eac4 322 StoreSwapLogData swapData;
51ee7c82 323 size_t ss = sizeof(StoreSwapLogData);
62e76326 324
c8f4eac4 325 if (fread(&swapData, ss, 1, log) != 1) {
bf8fe701 326 debugs(47, 1, "Done reading " << sd->path << " swaplog (" << n_read << " entries)");
62e76326 327 fclose(log);
328 log = NULL;
c8f4eac4 329 _done = true;
62e76326 330 return;
331 }
332
333 n_read++;
334
c8f4eac4 335 if (swapData.op <= SWAP_LOG_NOP)
62e76326 336 continue;
337
c8f4eac4 338 if (swapData.op >= SWAP_LOG_MAX)
62e76326 339 continue;
340
341 /*
342 * BC: during 2.4 development, we changed the way swap file
343 * numbers are assigned and stored. The high 16 bits used
344 * to encode the SD index number. There used to be a call
345 * to storeDirProperFileno here that re-assigned the index
346 * bits. Now, for backwards compatibility, we just need
347 * to mask it off.
348 */
c8f4eac4 349 swapData.swap_filen &= 0x00FFFFFF;
62e76326 350
bf8fe701 351 debugs(47, 3, "commonUfsDirRebuildFromSwapLog: " <<
352 swap_log_op_str[(int) swapData.op] << " " <<
353 storeKeyText(swapData.key) << " "<< std::setfill('0') <<
354 std::hex << std::uppercase << std::setw(8) <<
355 swapData.swap_filen);
62e76326 356
c8f4eac4 357 if (swapData.op == SWAP_LOG_ADD) {
62e76326 358 (void) 0;
c8f4eac4 359 } else if (swapData.op == SWAP_LOG_DEL) {
360 /* Delete unless we already have a newer copy anywhere in any store */
361 /* this needs to become
362 * 1) unpack url
363 * 2) make synthetic request with headers ?? or otherwise search
364 * for a matching object in the store
365 * TODO FIXME change to new async api
366 */
367 currentEntry (Store::Root().get(swapData.key));
368
77fde53f 369 if (currentEntry() != NULL && swapData.lastref >= e->lastref) {
62e76326 370 /*
371 * Make sure we don't unlink the file, it might be
372 * in use by a subsequent entry. Also note that
373 * we don't have to subtract from store_swap_size
374 * because adding to store_swap_size happens in
375 * the cleanup procedure.
376 */
d88e3c49 377 currentEntry()->expireNow();
378 currentEntry()->releaseRequest();
62e76326 379
380 if (currentEntry()->swap_filen > -1) {
381 UFSSwapDir *sdForThisEntry = dynamic_cast<UFSSwapDir *>(INDEXSD(currentEntry()->swap_dirn));
382 assert (sdForThisEntry);
383 sdForThisEntry->replacementRemove(currentEntry());
384 sdForThisEntry->mapBitReset(currentEntry()->swap_filen);
385 currentEntry()->swap_filen = -1;
386 currentEntry()->swap_dirn = -1;
387 }
388
5f33b71d 389 currentEntry()->release();
62e76326 390 counts.objcount--;
391 counts.cancelcount++;
392 }
393
394 continue;
395 } else {
411c6ea3 396 x = ::log(static_cast<double>(++counts.bad_log_op)) / ::log(10.0);
62e76326 397
398 if (0.0 == x - (double) (int) x)
bf8fe701 399 debugs(47, 1, "WARNING: " << counts.bad_log_op << " invalid swap log entries found");
62e76326 400
401 counts.invalid++;
402
403 continue;
404 }
405
406 if ((++counts.scancount & 0xFFF) == 0) {
407
408 struct stat sb;
409
410 if (0 == fstat(fileno(log), &sb))
411 storeRebuildProgress(sd->index,
412 (int) sb.st_size / ss, n_read);
413 }
414
c8f4eac4 415 if (!sd->validFileno(swapData.swap_filen, 0)) {
62e76326 416 counts.invalid++;
417 continue;
418 }
419
c8f4eac4 420 if (EBIT_TEST(swapData.flags, KEY_PRIVATE)) {
62e76326 421 counts.badflags++;
422 continue;
423 }
424
c8f4eac4 425 /* this needs to become
426 * 1) unpack url
427 * 2) make synthetic request with headers ?? or otherwise search
428 * for a matching object in the store
429 * TODO FIXME change to new async api
430 */
431 currentEntry (Store::Root().get(swapData.key));
432
d3b3ab85 433 int used; /* is swapfile already in use? */
c8f4eac4 434
435 used = sd->mapBitTest(swapData.swap_filen);
436
62e76326 437 /* If this URL already exists in the cache, does the swap log
438 * appear to have a newer entry? Compare 'lastref' from the
439 * swap log to e->lastref. */
440 /* is the log entry newer than current entry? */
c8f4eac4 441 int disk_entry_newer = currentEntry() ? (swapData.lastref > currentEntry()->lastref ? 1 : 0) : 0;
62e76326 442
443 if (used && !disk_entry_newer) {
444 /* log entry is old, ignore it */
445 counts.clashcount++;
446 continue;
c8f4eac4 447 } else if (used && currentEntry() && currentEntry()->swap_filen == swapData.swap_filen && currentEntry()->swap_dirn == sd->index) {
62e76326 448 /* swapfile taken, same URL, newer, update meta */
449
450 if (currentEntry()->store_status == STORE_OK) {
c8f4eac4 451 currentEntry()->lastref = swapData.timestamp;
452 currentEntry()->timestamp = swapData.timestamp;
453 currentEntry()->expires = swapData.expires;
454 currentEntry()->lastmod = swapData.lastmod;
455 currentEntry()->flags = swapData.flags;
456 currentEntry()->refcount += swapData.refcount;
62e76326 457 sd->dereference(*currentEntry());
458 } else {
459 debug_trap("commonUfsDirRebuildFromSwapLog: bad condition");
bf8fe701 460 debugs(47, 1, "\tSee " << __FILE__ << ":" << __LINE__);
62e76326 461 }
462
463 continue;
464 } else if (used) {
465 /* swapfile in use, not by this URL, log entry is newer */
466 /* This is sorta bad: the log entry should NOT be newer at this
467 * point. If the log is dirty, the filesize check should have
468 * caught this. If the log is clean, there should never be a
469 * newer entry. */
bf8fe701 470 debugs(47, 1, "WARNING: newer swaplog entry for dirno " <<
471 sd->index << ", fileno "<< std::setfill('0') << std::hex <<
472 std::uppercase << std::setw(8) << swapData.swap_filen);
473
62e76326 474 /* I'm tempted to remove the swapfile here just to be safe,
475 * but there is a bad race condition in the NOVM version if
476 * the swapfile has recently been opened for writing, but
477 * not yet opened for reading. Because we can't map
478 * swapfiles back to StoreEntrys, we don't know the state
479 * of the entry using that file. */
480 /* We'll assume the existing entry is valid, probably because
481 * were in a slow rebuild and the the swap file number got taken
482 * and the validation procedure hasn't run. */
483 assert(flags.need_to_validate);
484 counts.clashcount++;
485 continue;
486 } else if (currentEntry() && !disk_entry_newer) {
487 /* key already exists, current entry is newer */
488 /* keep old, ignore new */
489 counts.dupcount++;
490 continue;
491 } else if (currentEntry()) {
492 /* key already exists, this swapfile not being used */
493 /* junk old, load new */
d88e3c49 494 currentEntry()->expireNow();
495 currentEntry()->releaseRequest();
62e76326 496
497 if (currentEntry()->swap_filen > -1) {
498 UFSSwapDir *sdForThisEntry = dynamic_cast<UFSSwapDir *>(INDEXSD(currentEntry()->swap_dirn));
499 sdForThisEntry->replacementRemove(currentEntry());
500 /* Make sure we don't actually unlink the file */
501 sdForThisEntry->mapBitReset(currentEntry()->swap_filen);
502 currentEntry()->swap_filen = -1;
503 currentEntry()->swap_dirn = -1;
504 }
505
5f33b71d 506 currentEntry()->release();
62e76326 507 counts.dupcount++;
508 } else {
509 /* URL doesnt exist, swapfile not in use */
510 /* load new */
511 (void) 0;
512 }
513
514 /* update store_swap_size */
515 counts.objcount++;
516
c8f4eac4 517 currentEntry(sd->addDiskRestore(swapData.key,
518 swapData.swap_filen,
519 swapData.swap_file_sz,
520 swapData.expires,
521 swapData.timestamp,
522 swapData.lastref,
523 swapData.lastmod,
524 swapData.refcount,
525 swapData.flags,
62e76326 526 (int) flags.clean));
527
528 storeDirSwapLog(currentEntry(), SWAP_LOG_ADD);
4d6d905e 529 }
62e76326 530
4d6d905e 531}
532
533int
d3b3ab85 534RebuildState::getNextFile(sfileno * filn_p, int *size)
4d6d905e 535{
4d6d905e 536 int fd = -1;
4d6d905e 537 int dirs_opened = 0;
bf8fe701 538 debugs(47, 3, "commonUfsDirGetNextFile: flag=" << flags.init << ", " <<
539 sd->index << ": /"<< std::setfill('0') << std::hex <<
540 std::uppercase << std::setw(2) << curlvl1 << "/" << std::setw(2) <<
541 curlvl2);
62e76326 542
d3b3ab85 543 if (done)
62e76326 544 return -2;
545
d3b3ab85 546 while (fd < 0 && done == 0) {
62e76326 547 fd = -1;
548
549 if (0 == flags.init) { /* initialize, open first file */
550 done = 0;
551 curlvl1 = 0;
552 curlvl2 = 0;
553 in_dir = 0;
554 flags.init = 1;
555 assert(Config.cacheSwap.n_configured > 0);
556 }
557
558 if (0 == in_dir) { /* we need to read in a new directory */
559 snprintf(fullpath, SQUID_MAXPATHLEN, "%s/%02X/%02X",
560 sd->path,
561 curlvl1, curlvl2);
562
563 if (dirs_opened)
564 return -1;
565
566 td = opendir(fullpath);
567
568 dirs_opened++;
569
570 if (td == NULL) {
bf8fe701 571 debugs(47, 1, "commonUfsDirGetNextFile: opendir: " << fullpath << ": " << xstrerror());
62e76326 572 } else {
573 entry = readdir(td); /* skip . and .. */
574 entry = readdir(td);
575
576 if (entry == NULL && errno == ENOENT)
bf8fe701 577 debugs(47, 1, "commonUfsDirGetNextFile: directory does not exist!.");
578 debugs(47, 3, "commonUfsDirGetNextFile: Directory " << fullpath);
62e76326 579 }
580 }
581
582 if (td != NULL && (entry = readdir(td)) != NULL) {
583 in_dir++;
584
585 if (sscanf(entry->d_name, "%x", &fn) != 1) {
bf8fe701 586 debugs(47, 3, "commonUfsDirGetNextFile: invalid " << entry->d_name);
62e76326 587 continue;
588 }
589
590 if (!UFSSwapDir::FilenoBelongsHere(fn, sd->index, curlvl1, curlvl2)) {
bf8fe701 591 debugs(47, 3, "commonUfsDirGetNextFile: "<< std::setfill('0') <<
592 std::hex << std::uppercase << std::setw(8) << fn <<
593 " does not belong in " << std::dec << sd->index << "/" <<
594 curlvl1 << "/" << curlvl2);
595
62e76326 596 continue;
597 }
598
599 if (sd->mapBitTest(fn)) {
bf8fe701 600 debugs(47, 3, "commonUfsDirGetNextFile: Locked, continuing with next.");
62e76326 601 continue;
602 }
603
604 snprintf(fullfilename, SQUID_MAXPATHLEN, "%s/%s",
605 fullpath, entry->d_name);
bf8fe701 606 debugs(47, 3, "commonUfsDirGetNextFile: Opening " << fullfilename);
62e76326 607 fd = file_open(fullfilename, O_RDONLY | O_BINARY);
608
609 if (fd < 0)
bf8fe701 610 debugs(47, 1, "commonUfsDirGetNextFile: " << fullfilename << ": " << xstrerror());
62e76326 611 else
612 store_open_disk_fd++;
613
614 continue;
615 }
616
617 if (td != NULL)
618 closedir(td);
619
620 td = NULL;
621
622 in_dir = 0;
623
624 if (sd->validL2(++curlvl2))
625 continue;
626
627 curlvl2 = 0;
628
629 if (sd->validL1(++curlvl1))
630 continue;
631
632 curlvl1 = 0;
633
634 done = 1;
4d6d905e 635 }
62e76326 636
d3b3ab85 637 *filn_p = fn;
4d6d905e 638 return fd;
639}
528b2c61 640
c8f4eac4 641void
642RebuildState::next(void (callback)(void *cbdata), void *cbdata)
643{
644 /* for now, we don't cache at all */
645 speed = 1;
646 currentEntry(NULL);
647
648 while (!isDone() && currentEntry() == NULL)
649 rebuildStep();
650
651 callback(cbdata);
652}
653
654bool
655RebuildState::next()
656{
657 return false;
658}
659
660bool
661RebuildState::error() const
662{
663 return false;
664}
665
666bool
667RebuildState::isDone() const
668{
669 return _done;
670}
671
672StoreEntry *
673RebuildState::currentItem()
674{
675 return currentEntry();
676}
677
528b2c61 678#ifndef _USE_INLINE_
679#include "ufscommon.cci"
680#endif