]>
Commit | Line | Data |
---|---|---|
4d6d905e | 1 | /* |
bbc27441 | 2 | * Copyright (C) 1996-2014 The Squid Software Foundation and contributors |
4d6d905e | 3 | * |
bbc27441 AJ |
4 | * Squid software is distributed under GPLv2+ license and includes |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
4d6d905e | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 47 Store Directory Routines */ |
10 | ||
f7f3304a | 11 | #include "squid.h" |
438b04d4 | 12 | #include "disk.h" |
67679543 | 13 | #include "globals.h" |
58373ff8 | 14 | #include "RebuildState.h" |
4d5904f7 | 15 | #include "SquidConfig.h" |
cbc676b9 | 16 | #include "SquidTime.h" |
fb548aaf | 17 | #include "store_key_md5.h" |
687f5275 | 18 | #include "store_rebuild.h" |
67679543 | 19 | #include "StoreSwapLogData.h" |
5bed43d6 | 20 | #include "tools.h" |
58373ff8 | 21 | #include "UFSSwapLogParser.h" |
4b981814 | 22 | |
074d6a40 AJ |
23 | #include <cerrno> |
24 | #include <cmath> | |
582c2af2 FC |
25 | #if HAVE_SYS_STAT_H |
26 | #include <sys/stat.h> | |
27 | #endif | |
28 | ||
58373ff8 | 29 | CBDATA_NAMESPACED_CLASS_INIT(Fs::Ufs,RebuildState); |
4d6d905e | 30 | |
58373ff8 | 31 | Fs::Ufs::RebuildState::RebuildState(RefCount<UFSSwapDir> aSwapDir) : |
f53969cc | 32 | sd (aSwapDir), LogParser(NULL), e(NULL), fromLog(true), _done (false) |
4d6d905e | 33 | { |
c8f4eac4 | 34 | /* |
35 | * If the swap.state file exists in the cache_dir, then | |
36 | * we'll use commonUfsDirRebuildFromSwapLog(), otherwise we'll | |
37 | * use commonUfsDirRebuildFromDirectory() to open up each file | |
38 | * and suck in the meta data. | |
39 | */ | |
1a6347cd | 40 | int clean = 0; //TODO: change to bool |
c8f4eac4 | 41 | int zeroLengthLog = 0; |
42 | FILE *fp = sd->openTmpSwapLog(&clean, &zeroLengthLog); | |
43 | ||
47f6e231 | 44 | if (fp && !zeroLengthLog) |
58373ff8 | 45 | LogParser = Fs::Ufs::UFSSwapLogParser::GetUFSSwapLogParser(fp); |
47f6e231 | 46 | |
47 | if (LogParser == NULL ) { | |
c8f4eac4 | 48 | fromLog = false; |
49 | ||
50 | if (fp != NULL) | |
51 | fclose(fp); | |
52 | ||
53 | } else { | |
26ac0430 | 54 | fromLog = true; |
1a6347cd | 55 | flags.clean = (clean != 0); |
c8f4eac4 | 56 | } |
62e76326 | 57 | |
c8f4eac4 | 58 | if (!clean) |
1a6347cd | 59 | flags.need_to_validate = true; |
c8f4eac4 | 60 | |
786f6516 | 61 | debugs(47, DBG_IMPORTANT, "Rebuilding storage in " << sd->path << " (" << |
d178daf7 | 62 | (clean ? "clean log" : (LogParser ? "dirty log" : "no log")) << ")"); |
4d6d905e | 63 | } |
64 | ||
58373ff8 | 65 | Fs::Ufs::RebuildState::~RebuildState() |
4d6d905e | 66 | { |
d3b3ab85 | 67 | sd->closeTmpSwapLog(); |
47f6e231 | 68 | |
69 | if (LogParser) | |
26ac0430 | 70 | delete LogParser; |
4d6d905e | 71 | } |
72 | ||
73 | void | |
58373ff8 | 74 | Fs::Ufs::RebuildState::RebuildStep(void *data) |
4d6d905e | 75 | { |
d3b3ab85 | 76 | RebuildState *rb = (RebuildState *)data; |
c8f4eac4 | 77 | rb->rebuildStep(); |
78 | ||
79 | if (!rb->isDone()) | |
cbc676b9 | 80 | eventAdd("storeRebuild", RebuildStep, rb, 0.01, 1); |
c8f4eac4 | 81 | else { |
cb4185f1 | 82 | -- StoreController::store_dirs_rebuilding; |
c8f4eac4 | 83 | storeRebuildComplete(&rb->counts); |
84 | delete rb; | |
85 | } | |
86 | } | |
87 | ||
cbc676b9 | 88 | /// load entries from swap.state or files until we run out of entries or time |
c8f4eac4 | 89 | void |
58373ff8 | 90 | Fs::Ufs::RebuildState::rebuildStep() |
c8f4eac4 | 91 | { |
cbc676b9 AR |
92 | currentEntry(NULL); |
93 | ||
94 | // Balance our desire to maximize the number of entries processed at once | |
95 | // (and, hence, minimize overheads and total rebuild time) with a | |
96 | // requirement to also process Coordinator events, disk I/Os, etc. | |
97 | const int maxSpentMsec = 50; // keep small: most RAM I/Os are under 1ms | |
98 | const timeval loopStart = current_time; | |
99 | ||
100 | const int totalEntries = LogParser ? LogParser->SwapLogEntries() : -1; | |
101 | ||
102 | while (!isDone()) { | |
103 | if (fromLog) | |
104 | rebuildFromSwapLog(); | |
105 | else | |
106 | rebuildFromDirectory(); | |
107 | ||
108 | // TODO: teach storeRebuildProgress to handle totalEntries <= 0 | |
109 | if (totalEntries > 0 && (n_read % 4000 == 0)) | |
110 | storeRebuildProgress(sd->index, totalEntries, n_read); | |
111 | ||
112 | if (opt_foreground_rebuild) | |
113 | continue; // skip "few entries at a time" check below | |
114 | ||
115 | getCurrentTime(); | |
116 | const double elapsedMsec = tvSubMsec(loopStart, current_time); | |
117 | if (elapsedMsec > maxSpentMsec || elapsedMsec < 0) { | |
118 | debugs(47, 5, HERE << "pausing after " << n_read << " entries in " << | |
119 | elapsedMsec << "ms; " << (elapsedMsec/n_read) << "ms per entry"); | |
120 | break; | |
121 | } | |
122 | } | |
4d6d905e | 123 | } |
124 | ||
cbc676b9 | 125 | /// process one cache file |
4d6d905e | 126 | void |
58373ff8 | 127 | Fs::Ufs::RebuildState::rebuildFromDirectory() |
4d6d905e | 128 | { |
c3031d67 | 129 | cache_key key[SQUID_MD5_DIGEST_LENGTH]; |
62e76326 | 130 | |
4d6d905e | 131 | struct stat sb; |
4d6d905e | 132 | int fd = -1; |
58373ff8 | 133 | debugs(47, 3, HERE << "DIR #" << sd->index); |
62e76326 | 134 | |
e901206a A |
135 | assert(fd == -1); |
136 | sfileno filn = 0; | |
137 | int size; | |
138 | fd = getNextFile(&filn, &size); | |
139 | ||
140 | if (fd == -2) { | |
786f6516 AR |
141 | debugs(47, DBG_IMPORTANT, "Done scanning " << sd->path << " dir (" << |
142 | n_read << " entries)"); | |
e901206a A |
143 | _done = true; |
144 | return; | |
145 | } else if (fd < 0) { | |
146 | return; | |
147 | } | |
aa1a691e | 148 | |
e901206a A |
149 | assert(fd > -1); |
150 | /* lets get file stats here */ | |
62e76326 | 151 | |
cb4185f1 | 152 | ++n_read; |
786f6516 | 153 | |
e901206a | 154 | if (fstat(fd, &sb) < 0) { |
58373ff8 | 155 | debugs(47, DBG_IMPORTANT, HERE << "fstat(FD " << fd << "): " << xstrerror()); |
62e76326 | 156 | file_close(fd); |
cb4185f1 | 157 | --store_open_disk_fd; |
62e76326 | 158 | fd = -1; |
e901206a A |
159 | return; |
160 | } | |
62e76326 | 161 | |
e901206a A |
162 | MemBuf buf; |
163 | buf.init(SM_PAGE_SIZE, SM_PAGE_SIZE); | |
164 | if (!storeRebuildLoadEntry(fd, sd->index, buf, counts)) | |
165 | return; | |
166 | ||
52dce2fe | 167 | const uint64_t expectedSize = sb.st_size > 0 ? |
9d4e9cfb | 168 | static_cast<uint64_t>(sb.st_size) : 0; |
52dce2fe | 169 | |
e901206a | 170 | StoreEntry tmpe; |
52dce2fe AR |
171 | const bool parsed = storeRebuildParseEntry(buf, tmpe, key, counts, |
172 | expectedSize); | |
e901206a A |
173 | |
174 | file_close(fd); | |
cb4185f1 | 175 | --store_open_disk_fd; |
e901206a | 176 | fd = -1; |
62e76326 | 177 | |
52dce2fe AR |
178 | bool accepted = parsed && tmpe.swap_file_sz > 0; |
179 | if (parsed && !accepted) { | |
180 | debugs(47, DBG_IMPORTANT, "WARNING: Ignoring ufs cache entry with " << | |
181 | "unknown size: " << tmpe); | |
182 | accepted = false; | |
183 | } | |
184 | ||
185 | if (!accepted) { | |
e901206a A |
186 | // XXX: shouldn't this be a call to commonUfsUnlink? |
187 | sd->unlinkFile(filn); // should we unlink in all failure cases? | |
188 | return; | |
189 | } | |
190 | ||
191 | if (!storeRebuildKeepEntry(tmpe, key, counts)) | |
192 | return; | |
193 | ||
cb4185f1 | 194 | ++counts.objcount; |
e901206a A |
195 | // tmpe.dump(5); |
196 | currentEntry(sd->addDiskRestore(key, | |
197 | filn, | |
198 | tmpe.swap_file_sz, | |
199 | tmpe.expires, | |
200 | tmpe.timestamp, | |
201 | tmpe.lastref, | |
202 | tmpe.lastmod, | |
58373ff8 FC |
203 | tmpe.refcount, /* refcount */ |
204 | tmpe.flags, /* flags */ | |
e901206a A |
205 | (int) flags.clean)); |
206 | storeDirSwapLog(currentEntry(), SWAP_LOG_ADD); | |
d3b3ab85 | 207 | } |
208 | ||
822b78b5 | 209 | StoreEntry * |
58373ff8 | 210 | Fs::Ufs::RebuildState::currentEntry() const |
822b78b5 | 211 | { |
212 | return e; | |
213 | } | |
214 | ||
215 | void | |
58373ff8 | 216 | Fs::Ufs::RebuildState::currentEntry(StoreEntry *newValue) |
822b78b5 | 217 | { |
218 | e = newValue; | |
219 | } | |
220 | ||
cbc676b9 | 221 | /// process one swap log entry |
d3b3ab85 | 222 | void |
58373ff8 | 223 | Fs::Ufs::RebuildState::rebuildFromSwapLog() |
d3b3ab85 | 224 | { |
e901206a | 225 | StoreSwapLogData swapData; |
62e76326 | 226 | |
e901206a | 227 | if (LogParser->ReadRecord(swapData) != 1) { |
58373ff8 | 228 | debugs(47, DBG_IMPORTANT, "Done reading " << sd->path << " swaplog (" << n_read << " entries)"); |
e901206a A |
229 | LogParser->Close(); |
230 | delete LogParser; | |
231 | LogParser = NULL; | |
232 | _done = true; | |
233 | return; | |
234 | } | |
62e76326 | 235 | |
cb4185f1 | 236 | ++n_read; |
62e76326 | 237 | |
3f9d4dc2 | 238 | if (!swapData.sane()) { |
cb4185f1 | 239 | ++counts.invalid; |
e901206a | 240 | return; |
3f9d4dc2 | 241 | } |
62e76326 | 242 | |
e901206a A |
243 | /* |
244 | * BC: during 2.4 development, we changed the way swap file | |
245 | * numbers are assigned and stored. The high 16 bits used | |
246 | * to encode the SD index number. There used to be a call | |
247 | * to storeDirProperFileno here that re-assigned the index | |
248 | * bits. Now, for backwards compatibility, we just need | |
249 | * to mask it off. | |
250 | */ | |
251 | swapData.swap_filen &= 0x00FFFFFF; | |
252 | ||
58373ff8 | 253 | debugs(47, 3, HERE << swap_log_op_str[(int) swapData.op] << " " << |
e901206a A |
254 | storeKeyText(swapData.key) << " "<< std::setfill('0') << |
255 | std::hex << std::uppercase << std::setw(8) << | |
256 | swapData.swap_filen); | |
257 | ||
258 | if (swapData.op == SWAP_LOG_ADD) { | |
259 | (void) 0; | |
260 | } else if (swapData.op == SWAP_LOG_DEL) { | |
261 | /* Delete unless we already have a newer copy anywhere in any store */ | |
c8f4eac4 | 262 | /* this needs to become |
263 | * 1) unpack url | |
264 | * 2) make synthetic request with headers ?? or otherwise search | |
265 | * for a matching object in the store | |
266 | * TODO FIXME change to new async api | |
267 | */ | |
268 | currentEntry (Store::Root().get(swapData.key)); | |
269 | ||
e901206a | 270 | if (currentEntry() != NULL && swapData.lastref >= e->lastref) { |
c6e51aff | 271 | undoAdd(); |
cb4185f1 FC |
272 | --counts.objcount; |
273 | ++counts.cancelcount; | |
e901206a A |
274 | } |
275 | return; | |
276 | } else { | |
277 | const double | |
278 | x = ::log(static_cast<double>(++counts.bad_log_op)) / ::log(10.0); | |
279 | ||
280 | if (0.0 == x - (double) (int) x) | |
58373ff8 | 281 | debugs(47, DBG_IMPORTANT, "WARNING: " << counts.bad_log_op << " invalid swap log entries found"); |
e901206a | 282 | |
cb4185f1 | 283 | ++counts.invalid; |
e901206a A |
284 | |
285 | return; | |
286 | } | |
287 | ||
288 | ++counts.scancount; // XXX: should not this be incremented earlier? | |
289 | ||
290 | if (!sd->validFileno(swapData.swap_filen, 0)) { | |
cb4185f1 | 291 | ++counts.invalid; |
e901206a A |
292 | return; |
293 | } | |
294 | ||
295 | if (EBIT_TEST(swapData.flags, KEY_PRIVATE)) { | |
cb4185f1 | 296 | ++counts.badflags; |
e901206a A |
297 | return; |
298 | } | |
299 | ||
300 | /* this needs to become | |
301 | * 1) unpack url | |
302 | * 2) make synthetic request with headers ?? or otherwise search | |
303 | * for a matching object in the store | |
304 | * TODO FIXME change to new async api | |
305 | */ | |
306 | currentEntry (Store::Root().get(swapData.key)); | |
307 | ||
58373ff8 | 308 | int used; /* is swapfile already in use? */ |
e901206a A |
309 | |
310 | used = sd->mapBitTest(swapData.swap_filen); | |
311 | ||
312 | /* If this URL already exists in the cache, does the swap log | |
313 | * appear to have a newer entry? Compare 'lastref' from the | |
314 | * swap log to e->lastref. */ | |
315 | /* is the log entry newer than current entry? */ | |
316 | int disk_entry_newer = currentEntry() ? (swapData.lastref > currentEntry()->lastref ? 1 : 0) : 0; | |
317 | ||
318 | if (used && !disk_entry_newer) { | |
319 | /* log entry is old, ignore it */ | |
cb4185f1 | 320 | ++counts.clashcount; |
e901206a A |
321 | return; |
322 | } else if (used && currentEntry() && currentEntry()->swap_filen == swapData.swap_filen && currentEntry()->swap_dirn == sd->index) { | |
323 | /* swapfile taken, same URL, newer, update meta */ | |
324 | ||
325 | if (currentEntry()->store_status == STORE_OK) { | |
326 | currentEntry()->lastref = swapData.timestamp; | |
327 | currentEntry()->timestamp = swapData.timestamp; | |
328 | currentEntry()->expires = swapData.expires; | |
329 | currentEntry()->lastmod = swapData.lastmod; | |
330 | currentEntry()->flags = swapData.flags; | |
331 | currentEntry()->refcount += swapData.refcount; | |
54347cbd | 332 | sd->dereference(*currentEntry(), false); |
62e76326 | 333 | } else { |
e901206a | 334 | debug_trap("commonUfsDirRebuildFromSwapLog: bad condition"); |
58373ff8 | 335 | debugs(47, DBG_IMPORTANT, HERE << "bad condition"); |
e901206a A |
336 | } |
337 | return; | |
338 | } else if (used) { | |
339 | /* swapfile in use, not by this URL, log entry is newer */ | |
340 | /* This is sorta bad: the log entry should NOT be newer at this | |
341 | * point. If the log is dirty, the filesize check should have | |
342 | * caught this. If the log is clean, there should never be a | |
343 | * newer entry. */ | |
58373ff8 | 344 | debugs(47, DBG_IMPORTANT, "WARNING: newer swaplog entry for dirno " << |
e901206a A |
345 | sd->index << ", fileno "<< std::setfill('0') << std::hex << |
346 | std::uppercase << std::setw(8) << swapData.swap_filen); | |
347 | ||
348 | /* I'm tempted to remove the swapfile here just to be safe, | |
349 | * but there is a bad race condition in the NOVM version if | |
350 | * the swapfile has recently been opened for writing, but | |
351 | * not yet opened for reading. Because we can't map | |
352 | * swapfiles back to StoreEntrys, we don't know the state | |
353 | * of the entry using that file. */ | |
354 | /* We'll assume the existing entry is valid, probably because | |
355 | * were in a slow rebuild and the the swap file number got taken | |
356 | * and the validation procedure hasn't run. */ | |
357 | assert(flags.need_to_validate); | |
cb4185f1 | 358 | ++counts.clashcount; |
e901206a A |
359 | return; |
360 | } else if (currentEntry() && !disk_entry_newer) { | |
361 | /* key already exists, current entry is newer */ | |
362 | /* keep old, ignore new */ | |
cb4185f1 | 363 | ++counts.dupcount; |
e901206a A |
364 | return; |
365 | } else if (currentEntry()) { | |
366 | /* key already exists, this swapfile not being used */ | |
367 | /* junk old, load new */ | |
c6e51aff | 368 | undoAdd(); |
133c3ad6 | 369 | --counts.objcount; |
cb4185f1 | 370 | ++counts.dupcount; |
e901206a A |
371 | } else { |
372 | /* URL doesnt exist, swapfile not in use */ | |
373 | /* load new */ | |
374 | (void) 0; | |
375 | } | |
376 | ||
cb4185f1 | 377 | ++counts.objcount; |
62e76326 | 378 | |
e901206a A |
379 | currentEntry(sd->addDiskRestore(swapData.key, |
380 | swapData.swap_filen, | |
381 | swapData.swap_file_sz, | |
382 | swapData.expires, | |
383 | swapData.timestamp, | |
384 | swapData.lastref, | |
385 | swapData.lastmod, | |
386 | swapData.refcount, | |
387 | swapData.flags, | |
388 | (int) flags.clean)); | |
62e76326 | 389 | |
e901206a | 390 | storeDirSwapLog(currentEntry(), SWAP_LOG_ADD); |
4d6d905e | 391 | } |
392 | ||
c6e51aff AR |
393 | /// undo the effects of adding an entry in rebuildFromSwapLog() |
394 | void | |
58373ff8 | 395 | Fs::Ufs::RebuildState::undoAdd() |
c6e51aff AR |
396 | { |
397 | StoreEntry *added = currentEntry(); | |
398 | assert(added); | |
399 | currentEntry(NULL); | |
400 | ||
401 | // TODO: Why bother with these two if we are going to release?! | |
402 | added->expireNow(); | |
403 | added->releaseRequest(); | |
404 | ||
405 | if (added->swap_filen > -1) { | |
128122d1 AR |
406 | SwapDir *someDir = INDEXSD(added->swap_dirn); |
407 | assert(someDir); | |
408 | if (UFSSwapDir *ufsDir = dynamic_cast<UFSSwapDir*>(someDir)) | |
409 | ufsDir->undoAddDiskRestore(added); | |
410 | // else the entry was loaded from and/or is currently in a non-UFS dir | |
411 | // Thus, there is no use in preserving its disk file (the only purpose | |
412 | // of undoAddDiskRestore!), even if we could. Instead, we release the | |
413 | // the entry and [eventually] unlink its disk file or free its slot. | |
c6e51aff AR |
414 | } |
415 | ||
416 | added->release(); | |
417 | } | |
418 | ||
4d6d905e | 419 | int |
58373ff8 | 420 | Fs::Ufs::RebuildState::getNextFile(sfileno * filn_p, int *size) |
4d6d905e | 421 | { |
4d6d905e | 422 | int fd = -1; |
4d6d905e | 423 | int dirs_opened = 0; |
58373ff8 | 424 | debugs(47, 3, HERE << "flag=" << flags.init << ", " << |
bf8fe701 | 425 | sd->index << ": /"<< std::setfill('0') << std::hex << |
426 | std::uppercase << std::setw(2) << curlvl1 << "/" << std::setw(2) << | |
427 | curlvl2); | |
62e76326 | 428 | |
d3b3ab85 | 429 | if (done) |
62e76326 | 430 | return -2; |
431 | ||
d3b3ab85 | 432 | while (fd < 0 && done == 0) { |
62e76326 | 433 | fd = -1; |
434 | ||
1a6347cd | 435 | if (!flags.init) { /* initialize, open first file */ |
62e76326 | 436 | done = 0; |
437 | curlvl1 = 0; | |
438 | curlvl2 = 0; | |
439 | in_dir = 0; | |
1a6347cd | 440 | flags.init = true; |
62e76326 | 441 | assert(Config.cacheSwap.n_configured > 0); |
442 | } | |
443 | ||
58373ff8 | 444 | if (0 == in_dir) { /* we need to read in a new directory */ |
ef364f64 | 445 | snprintf(fullpath, MAXPATHLEN, "%s/%02X/%02X", |
62e76326 | 446 | sd->path, |
447 | curlvl1, curlvl2); | |
448 | ||
449 | if (dirs_opened) | |
450 | return -1; | |
451 | ||
452 | td = opendir(fullpath); | |
453 | ||
cb4185f1 | 454 | ++dirs_opened; |
62e76326 | 455 | |
456 | if (td == NULL) { | |
58373ff8 | 457 | debugs(47, DBG_IMPORTANT, HERE << "error in opendir (" << fullpath << "): " << xstrerror()); |
62e76326 | 458 | } else { |
58373ff8 | 459 | entry = readdir(td); /* skip . and .. */ |
62e76326 | 460 | entry = readdir(td); |
461 | ||
462 | if (entry == NULL && errno == ENOENT) | |
58373ff8 FC |
463 | debugs(47, DBG_IMPORTANT, HERE << "WARNING: directory does not exist!"); |
464 | debugs(47, 3, HERE << "Directory " << fullpath); | |
62e76326 | 465 | } |
466 | } | |
467 | ||
468 | if (td != NULL && (entry = readdir(td)) != NULL) { | |
cb4185f1 | 469 | ++in_dir; |
62e76326 | 470 | |
471 | if (sscanf(entry->d_name, "%x", &fn) != 1) { | |
58373ff8 | 472 | debugs(47, 3, HERE << "invalid entry " << entry->d_name); |
62e76326 | 473 | continue; |
474 | } | |
475 | ||
476 | if (!UFSSwapDir::FilenoBelongsHere(fn, sd->index, curlvl1, curlvl2)) { | |
58373ff8 | 477 | debugs(47, 3, HERE << std::setfill('0') << |
bf8fe701 | 478 | std::hex << std::uppercase << std::setw(8) << fn << |
479 | " does not belong in " << std::dec << sd->index << "/" << | |
480 | curlvl1 << "/" << curlvl2); | |
481 | ||
62e76326 | 482 | continue; |
483 | } | |
484 | ||
485 | if (sd->mapBitTest(fn)) { | |
58373ff8 | 486 | debugs(47, 3, HERE << "Locked, continuing with next."); |
62e76326 | 487 | continue; |
488 | } | |
489 | ||
ef364f64 | 490 | snprintf(fullfilename, MAXPATHLEN, "%s/%s", |
62e76326 | 491 | fullpath, entry->d_name); |
58373ff8 | 492 | debugs(47, 3, HERE << "Opening " << fullfilename); |
62e76326 | 493 | fd = file_open(fullfilename, O_RDONLY | O_BINARY); |
494 | ||
495 | if (fd < 0) | |
58373ff8 | 496 | debugs(47, DBG_IMPORTANT, HERE << "error opening " << fullfilename << ": " << xstrerror()); |
62e76326 | 497 | else |
cb4185f1 | 498 | ++store_open_disk_fd; |
62e76326 | 499 | |
500 | continue; | |
501 | } | |
502 | ||
503 | if (td != NULL) | |
504 | closedir(td); | |
505 | ||
506 | td = NULL; | |
507 | ||
508 | in_dir = 0; | |
509 | ||
510 | if (sd->validL2(++curlvl2)) | |
511 | continue; | |
512 | ||
513 | curlvl2 = 0; | |
514 | ||
515 | if (sd->validL1(++curlvl1)) | |
516 | continue; | |
517 | ||
518 | curlvl1 = 0; | |
519 | ||
520 | done = 1; | |
4d6d905e | 521 | } |
62e76326 | 522 | |
d3b3ab85 | 523 | *filn_p = fn; |
4d6d905e | 524 | return fd; |
525 | } | |
528b2c61 | 526 | |
c8f4eac4 | 527 | bool |
58373ff8 | 528 | Fs::Ufs::RebuildState::error() const |
c8f4eac4 | 529 | { |
530 | return false; | |
531 | } | |
532 | ||
533 | bool | |
58373ff8 | 534 | Fs::Ufs::RebuildState::isDone() const |
c8f4eac4 | 535 | { |
536 | return _done; | |
537 | } | |
538 | ||
539 | StoreEntry * | |
58373ff8 | 540 | Fs::Ufs::RebuildState::currentItem() |
c8f4eac4 | 541 | { |
542 | return currentEntry(); | |
543 | } | |
f53969cc | 544 |