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