]> git.ipfire.org Git - thirdparty/squid.git/blame - src/fs/ufs/RebuildState.cc
Boilerplate: update copyright blurbs on src/
[thirdparty/squid.git] / src / fs / ufs / RebuildState.cc
CommitLineData
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 29CBDATA_NAMESPACED_CLASS_INIT(Fs::Ufs,RebuildState);
4d6d905e 30
58373ff8 31Fs::Ufs::RebuildState::RebuildState(RefCount<UFSSwapDir> aSwapDir) :
187642d0 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 65Fs::Ufs::RebuildState::~RebuildState()
4d6d905e 66{
d3b3ab85 67 sd->closeTmpSwapLog();
47f6e231 68
69 if (LogParser)
26ac0430 70 delete LogParser;
4d6d905e 71}
72
73void
58373ff8 74Fs::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 89void
58373ff8 90Fs::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 126void
58373ff8 127Fs::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;
d3b3ab85 133 assert(this != NULL);
58373ff8 134 debugs(47, 3, HERE << "DIR #" << sd->index);
62e76326 135
e901206a
A
136 assert(fd == -1);
137 sfileno filn = 0;
138 int size;
139 fd = getNextFile(&filn, &size);
140
141 if (fd == -2) {
786f6516
AR
142 debugs(47, DBG_IMPORTANT, "Done scanning " << sd->path << " dir (" <<
143 n_read << " entries)");
e901206a
A
144 _done = true;
145 return;
146 } else if (fd < 0) {
147 return;
148 }
aa1a691e 149
e901206a
A
150 assert(fd > -1);
151 /* lets get file stats here */
62e76326 152
cb4185f1 153 ++n_read;
786f6516 154
e901206a 155 if (fstat(fd, &sb) < 0) {
58373ff8 156 debugs(47, DBG_IMPORTANT, HERE << "fstat(FD " << fd << "): " << xstrerror());
62e76326 157 file_close(fd);
cb4185f1 158 --store_open_disk_fd;
62e76326 159 fd = -1;
e901206a
A
160 return;
161 }
62e76326 162
e901206a
A
163 MemBuf buf;
164 buf.init(SM_PAGE_SIZE, SM_PAGE_SIZE);
165 if (!storeRebuildLoadEntry(fd, sd->index, buf, counts))
166 return;
167
52dce2fe 168 const uint64_t expectedSize = sb.st_size > 0 ?
9d4e9cfb 169 static_cast<uint64_t>(sb.st_size) : 0;
52dce2fe 170
e901206a 171 StoreEntry tmpe;
52dce2fe
AR
172 const bool parsed = storeRebuildParseEntry(buf, tmpe, key, counts,
173 expectedSize);
e901206a
A
174
175 file_close(fd);
cb4185f1 176 --store_open_disk_fd;
e901206a 177 fd = -1;
62e76326 178
52dce2fe
AR
179 bool accepted = parsed && tmpe.swap_file_sz > 0;
180 if (parsed && !accepted) {
181 debugs(47, DBG_IMPORTANT, "WARNING: Ignoring ufs cache entry with " <<
182 "unknown size: " << tmpe);
183 accepted = false;
184 }
185
186 if (!accepted) {
e901206a
A
187 // XXX: shouldn't this be a call to commonUfsUnlink?
188 sd->unlinkFile(filn); // should we unlink in all failure cases?
189 return;
190 }
191
192 if (!storeRebuildKeepEntry(tmpe, key, counts))
193 return;
194
cb4185f1 195 ++counts.objcount;
e901206a
A
196 // tmpe.dump(5);
197 currentEntry(sd->addDiskRestore(key,
198 filn,
199 tmpe.swap_file_sz,
200 tmpe.expires,
201 tmpe.timestamp,
202 tmpe.lastref,
203 tmpe.lastmod,
58373ff8
FC
204 tmpe.refcount, /* refcount */
205 tmpe.flags, /* flags */
e901206a
A
206 (int) flags.clean));
207 storeDirSwapLog(currentEntry(), SWAP_LOG_ADD);
d3b3ab85 208}
209
822b78b5 210StoreEntry *
58373ff8 211Fs::Ufs::RebuildState::currentEntry() const
822b78b5 212{
213 return e;
214}
215
216void
58373ff8 217Fs::Ufs::RebuildState::currentEntry(StoreEntry *newValue)
822b78b5 218{
219 e = newValue;
220}
221
cbc676b9 222/// process one swap log entry
d3b3ab85 223void
58373ff8 224Fs::Ufs::RebuildState::rebuildFromSwapLog()
d3b3ab85 225{
e901206a 226 StoreSwapLogData swapData;
62e76326 227
e901206a 228 if (LogParser->ReadRecord(swapData) != 1) {
58373ff8 229 debugs(47, DBG_IMPORTANT, "Done reading " << sd->path << " swaplog (" << n_read << " entries)");
e901206a
A
230 LogParser->Close();
231 delete LogParser;
232 LogParser = NULL;
233 _done = true;
234 return;
235 }
62e76326 236
cb4185f1 237 ++n_read;
62e76326 238
3f9d4dc2 239 if (!swapData.sane()) {
cb4185f1 240 ++counts.invalid;
e901206a 241 return;
3f9d4dc2 242 }
62e76326 243
e901206a
A
244 /*
245 * BC: during 2.4 development, we changed the way swap file
246 * numbers are assigned and stored. The high 16 bits used
247 * to encode the SD index number. There used to be a call
248 * to storeDirProperFileno here that re-assigned the index
249 * bits. Now, for backwards compatibility, we just need
250 * to mask it off.
251 */
252 swapData.swap_filen &= 0x00FFFFFF;
253
58373ff8 254 debugs(47, 3, HERE << swap_log_op_str[(int) swapData.op] << " " <<
e901206a
A
255 storeKeyText(swapData.key) << " "<< std::setfill('0') <<
256 std::hex << std::uppercase << std::setw(8) <<
257 swapData.swap_filen);
258
259 if (swapData.op == SWAP_LOG_ADD) {
260 (void) 0;
261 } else if (swapData.op == SWAP_LOG_DEL) {
262 /* Delete unless we already have a newer copy anywhere in any store */
c8f4eac4 263 /* this needs to become
264 * 1) unpack url
265 * 2) make synthetic request with headers ?? or otherwise search
266 * for a matching object in the store
267 * TODO FIXME change to new async api
268 */
269 currentEntry (Store::Root().get(swapData.key));
270
e901206a 271 if (currentEntry() != NULL && swapData.lastref >= e->lastref) {
c6e51aff 272 undoAdd();
cb4185f1
FC
273 --counts.objcount;
274 ++counts.cancelcount;
e901206a
A
275 }
276 return;
277 } else {
278 const double
279 x = ::log(static_cast<double>(++counts.bad_log_op)) / ::log(10.0);
280
281 if (0.0 == x - (double) (int) x)
58373ff8 282 debugs(47, DBG_IMPORTANT, "WARNING: " << counts.bad_log_op << " invalid swap log entries found");
e901206a 283
cb4185f1 284 ++counts.invalid;
e901206a
A
285
286 return;
287 }
288
289 ++counts.scancount; // XXX: should not this be incremented earlier?
290
291 if (!sd->validFileno(swapData.swap_filen, 0)) {
cb4185f1 292 ++counts.invalid;
e901206a
A
293 return;
294 }
295
296 if (EBIT_TEST(swapData.flags, KEY_PRIVATE)) {
cb4185f1 297 ++counts.badflags;
e901206a
A
298 return;
299 }
300
301 /* this needs to become
302 * 1) unpack url
303 * 2) make synthetic request with headers ?? or otherwise search
304 * for a matching object in the store
305 * TODO FIXME change to new async api
306 */
307 currentEntry (Store::Root().get(swapData.key));
308
58373ff8 309 int used; /* is swapfile already in use? */
e901206a
A
310
311 used = sd->mapBitTest(swapData.swap_filen);
312
313 /* If this URL already exists in the cache, does the swap log
314 * appear to have a newer entry? Compare 'lastref' from the
315 * swap log to e->lastref. */
316 /* is the log entry newer than current entry? */
317 int disk_entry_newer = currentEntry() ? (swapData.lastref > currentEntry()->lastref ? 1 : 0) : 0;
318
319 if (used && !disk_entry_newer) {
320 /* log entry is old, ignore it */
cb4185f1 321 ++counts.clashcount;
e901206a
A
322 return;
323 } else if (used && currentEntry() && currentEntry()->swap_filen == swapData.swap_filen && currentEntry()->swap_dirn == sd->index) {
324 /* swapfile taken, same URL, newer, update meta */
325
326 if (currentEntry()->store_status == STORE_OK) {
327 currentEntry()->lastref = swapData.timestamp;
328 currentEntry()->timestamp = swapData.timestamp;
329 currentEntry()->expires = swapData.expires;
330 currentEntry()->lastmod = swapData.lastmod;
331 currentEntry()->flags = swapData.flags;
332 currentEntry()->refcount += swapData.refcount;
54347cbd 333 sd->dereference(*currentEntry(), false);
62e76326 334 } else {
e901206a 335 debug_trap("commonUfsDirRebuildFromSwapLog: bad condition");
58373ff8 336 debugs(47, DBG_IMPORTANT, HERE << "bad condition");
e901206a
A
337 }
338 return;
339 } else if (used) {
340 /* swapfile in use, not by this URL, log entry is newer */
341 /* This is sorta bad: the log entry should NOT be newer at this
342 * point. If the log is dirty, the filesize check should have
343 * caught this. If the log is clean, there should never be a
344 * newer entry. */
58373ff8 345 debugs(47, DBG_IMPORTANT, "WARNING: newer swaplog entry for dirno " <<
e901206a
A
346 sd->index << ", fileno "<< std::setfill('0') << std::hex <<
347 std::uppercase << std::setw(8) << swapData.swap_filen);
348
349 /* I'm tempted to remove the swapfile here just to be safe,
350 * but there is a bad race condition in the NOVM version if
351 * the swapfile has recently been opened for writing, but
352 * not yet opened for reading. Because we can't map
353 * swapfiles back to StoreEntrys, we don't know the state
354 * of the entry using that file. */
355 /* We'll assume the existing entry is valid, probably because
356 * were in a slow rebuild and the the swap file number got taken
357 * and the validation procedure hasn't run. */
358 assert(flags.need_to_validate);
cb4185f1 359 ++counts.clashcount;
e901206a
A
360 return;
361 } else if (currentEntry() && !disk_entry_newer) {
362 /* key already exists, current entry is newer */
363 /* keep old, ignore new */
cb4185f1 364 ++counts.dupcount;
e901206a
A
365 return;
366 } else if (currentEntry()) {
367 /* key already exists, this swapfile not being used */
368 /* junk old, load new */
c6e51aff 369 undoAdd();
133c3ad6 370 --counts.objcount;
cb4185f1 371 ++counts.dupcount;
e901206a
A
372 } else {
373 /* URL doesnt exist, swapfile not in use */
374 /* load new */
375 (void) 0;
376 }
377
cb4185f1 378 ++counts.objcount;
62e76326 379
e901206a
A
380 currentEntry(sd->addDiskRestore(swapData.key,
381 swapData.swap_filen,
382 swapData.swap_file_sz,
383 swapData.expires,
384 swapData.timestamp,
385 swapData.lastref,
386 swapData.lastmod,
387 swapData.refcount,
388 swapData.flags,
389 (int) flags.clean));
62e76326 390
e901206a 391 storeDirSwapLog(currentEntry(), SWAP_LOG_ADD);
4d6d905e 392}
393
c6e51aff
AR
394/// undo the effects of adding an entry in rebuildFromSwapLog()
395void
58373ff8 396Fs::Ufs::RebuildState::undoAdd()
c6e51aff
AR
397{
398 StoreEntry *added = currentEntry();
399 assert(added);
400 currentEntry(NULL);
401
402 // TODO: Why bother with these two if we are going to release?!
403 added->expireNow();
404 added->releaseRequest();
405
406 if (added->swap_filen > -1) {
128122d1
AR
407 SwapDir *someDir = INDEXSD(added->swap_dirn);
408 assert(someDir);
409 if (UFSSwapDir *ufsDir = dynamic_cast<UFSSwapDir*>(someDir))
410 ufsDir->undoAddDiskRestore(added);
411 // else the entry was loaded from and/or is currently in a non-UFS dir
412 // Thus, there is no use in preserving its disk file (the only purpose
413 // of undoAddDiskRestore!), even if we could. Instead, we release the
414 // the entry and [eventually] unlink its disk file or free its slot.
c6e51aff
AR
415 }
416
417 added->release();
418}
419
4d6d905e 420int
58373ff8 421Fs::Ufs::RebuildState::getNextFile(sfileno * filn_p, int *size)
4d6d905e 422{
4d6d905e 423 int fd = -1;
4d6d905e 424 int dirs_opened = 0;
58373ff8 425 debugs(47, 3, HERE << "flag=" << flags.init << ", " <<
bf8fe701 426 sd->index << ": /"<< std::setfill('0') << std::hex <<
427 std::uppercase << std::setw(2) << curlvl1 << "/" << std::setw(2) <<
428 curlvl2);
62e76326 429
d3b3ab85 430 if (done)
62e76326 431 return -2;
432
d3b3ab85 433 while (fd < 0 && done == 0) {
62e76326 434 fd = -1;
435
1a6347cd 436 if (!flags.init) { /* initialize, open first file */
62e76326 437 done = 0;
438 curlvl1 = 0;
439 curlvl2 = 0;
440 in_dir = 0;
1a6347cd 441 flags.init = true;
62e76326 442 assert(Config.cacheSwap.n_configured > 0);
443 }
444
58373ff8 445 if (0 == in_dir) { /* we need to read in a new directory */
ef364f64 446 snprintf(fullpath, MAXPATHLEN, "%s/%02X/%02X",
62e76326 447 sd->path,
448 curlvl1, curlvl2);
449
450 if (dirs_opened)
451 return -1;
452
453 td = opendir(fullpath);
454
cb4185f1 455 ++dirs_opened;
62e76326 456
457 if (td == NULL) {
58373ff8 458 debugs(47, DBG_IMPORTANT, HERE << "error in opendir (" << fullpath << "): " << xstrerror());
62e76326 459 } else {
58373ff8 460 entry = readdir(td); /* skip . and .. */
62e76326 461 entry = readdir(td);
462
463 if (entry == NULL && errno == ENOENT)
58373ff8
FC
464 debugs(47, DBG_IMPORTANT, HERE << "WARNING: directory does not exist!");
465 debugs(47, 3, HERE << "Directory " << fullpath);
62e76326 466 }
467 }
468
469 if (td != NULL && (entry = readdir(td)) != NULL) {
cb4185f1 470 ++in_dir;
62e76326 471
472 if (sscanf(entry->d_name, "%x", &fn) != 1) {
58373ff8 473 debugs(47, 3, HERE << "invalid entry " << entry->d_name);
62e76326 474 continue;
475 }
476
477 if (!UFSSwapDir::FilenoBelongsHere(fn, sd->index, curlvl1, curlvl2)) {
58373ff8 478 debugs(47, 3, HERE << std::setfill('0') <<
bf8fe701 479 std::hex << std::uppercase << std::setw(8) << fn <<
480 " does not belong in " << std::dec << sd->index << "/" <<
481 curlvl1 << "/" << curlvl2);
482
62e76326 483 continue;
484 }
485
486 if (sd->mapBitTest(fn)) {
58373ff8 487 debugs(47, 3, HERE << "Locked, continuing with next.");
62e76326 488 continue;
489 }
490
ef364f64 491 snprintf(fullfilename, MAXPATHLEN, "%s/%s",
62e76326 492 fullpath, entry->d_name);
58373ff8 493 debugs(47, 3, HERE << "Opening " << fullfilename);
62e76326 494 fd = file_open(fullfilename, O_RDONLY | O_BINARY);
495
496 if (fd < 0)
58373ff8 497 debugs(47, DBG_IMPORTANT, HERE << "error opening " << fullfilename << ": " << xstrerror());
62e76326 498 else
cb4185f1 499 ++store_open_disk_fd;
62e76326 500
501 continue;
502 }
503
504 if (td != NULL)
505 closedir(td);
506
507 td = NULL;
508
509 in_dir = 0;
510
511 if (sd->validL2(++curlvl2))
512 continue;
513
514 curlvl2 = 0;
515
516 if (sd->validL1(++curlvl1))
517 continue;
518
519 curlvl1 = 0;
520
521 done = 1;
4d6d905e 522 }
62e76326 523
d3b3ab85 524 *filn_p = fn;
4d6d905e 525 return fd;
526}
528b2c61 527
c8f4eac4 528bool
58373ff8 529Fs::Ufs::RebuildState::error() const
c8f4eac4 530{
531 return false;
532}
533
534bool
58373ff8 535Fs::Ufs::RebuildState::isDone() const
c8f4eac4 536{
537 return _done;
538}
539
540StoreEntry *
58373ff8 541Fs::Ufs::RebuildState::currentItem()
c8f4eac4 542{
543 return currentEntry();
544}