]> git.ipfire.org Git - thirdparty/squid.git/blame - src/fs/ufs/UFSSwapDir.cc
Docs: Copyright updates for 2018 (#114)
[thirdparty/squid.git] / src / fs / ufs / UFSSwapDir.cc
CommitLineData
cd748f27 1/*
5b74111a 2 * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
cd748f27 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.
cd748f27 7 */
8
bbc27441
AJ
9/* DEBUG: section 47 Store Directory Routines */
10
58373ff8
FC
11#define CLEAN_BUF_SZ 16384
12
8a01b99e
FC
13#include "squid.h"
14#include "cache_cf.h"
b9ae18aa 15#include "ConfigOption.h"
b9ae18aa 16#include "DiskIO/DiskIOModule.h"
602d9612 17#include "DiskIO/DiskIOStrategy.h"
58373ff8 18#include "fde.h"
602d9612 19#include "FileMap.h"
b3f7fd88 20#include "fs_io.h"
67679543 21#include "globals.h"
c8f4eac4 22#include "Parsing.h"
58373ff8 23#include "RebuildState.h"
602d9612 24#include "SquidConfig.h"
a98bcbee 25#include "SquidMath.h"
602d9612
A
26#include "SquidTime.h"
27#include "StatCounters.h"
fb548aaf 28#include "store_key_md5.h"
58373ff8
FC
29#include "StoreSearchUFS.h"
30#include "StoreSwapLogData.h"
5bed43d6 31#include "tools.h"
58373ff8 32#include "UFSSwapDir.h"
27e059d4 33
074d6a40
AJ
34#include <cerrno>
35#include <cmath>
c03adf02 36#include <random>
582c2af2
FC
37#if HAVE_SYS_STAT_H
38#include <sys/stat.h>
39#endif
cd748f27 40
58373ff8
FC
41int Fs::Ufs::UFSSwapDir::NumberOfUFSDirs = 0;
42int *Fs::Ufs::UFSSwapDir::UFSDirToGlobalDirMapping = NULL;
43
44class UFSCleanLog : public SwapDir::CleanLog
45{
46
47public:
48 UFSCleanLog(SwapDir *);
49 /** Get the next entry that is a candidate for clean log writing
50 */
51 virtual const StoreEntry *nextEntry();
52 /** "write" an entry to the clean log file.
53 */
54 virtual void write(StoreEntry const &);
55 char *cur;
56 char *newLog;
57 char *cln;
58 char *outbuf;
59 off_t outbuf_offset;
60 int fd;
61 RemovalPolicyWalker *walker;
62 SwapDir *sd;
63};
64
65UFSCleanLog::UFSCleanLog(SwapDir *aSwapDir) :
f53969cc
SM
66 cur(NULL), newLog(NULL), cln(NULL), outbuf(NULL),
67 outbuf_offset(0), fd(-1),walker(NULL), sd(aSwapDir)
58373ff8
FC
68{}
69
70const StoreEntry *
71UFSCleanLog::nextEntry()
72{
73 const StoreEntry *entry = NULL;
74
75 if (walker)
76 entry = walker->Next(walker);
77
78 return entry;
79}
80
81void
82UFSCleanLog::write(StoreEntry const &e)
83{
84 StoreSwapLogData s;
85 static size_t ss = sizeof(StoreSwapLogData);
86 s.op = (char) SWAP_LOG_ADD;
87 s.swap_filen = e.swap_filen;
88 s.timestamp = e.timestamp;
89 s.lastref = e.lastref;
90 s.expires = e.expires;
438b41ba 91 s.lastmod = e.lastModified();
58373ff8
FC
92 s.swap_file_sz = e.swap_file_sz;
93 s.refcount = e.refcount;
94 s.flags = e.flags;
95 memcpy(&s.key, e.key, SQUID_MD5_DIGEST_LENGTH);
96 s.finalize();
97 memcpy(outbuf + outbuf_offset, &s, ss);
98 outbuf_offset += ss;
99 /* buffered write */
100
101 if (outbuf_offset + ss >= CLEAN_BUF_SZ) {
102 if (FD_WRITE_METHOD(fd, outbuf, outbuf_offset) < 0) {
b69e9ffa 103 int xerrno = errno;
58373ff8 104 /* XXX This error handling should probably move up to the caller */
b69e9ffa
AJ
105 debugs(50, DBG_CRITICAL, MYNAME << newLog << ": write: " << xstrerr(xerrno));
106 debugs(50, DBG_CRITICAL, MYNAME << "Current swap logfile not replaced.");
58373ff8
FC
107 file_close(fd);
108 fd = -1;
109 unlink(newLog);
110 sd->cleanLog = NULL;
111 delete this;
112 return;
113 }
114
115 outbuf_offset = 0;
116 }
117}
118
aa1a691e 119bool
58373ff8 120Fs::Ufs::UFSSwapDir::canStore(const StoreEntry &e, int64_t diskSpaceNeeded, int &load) const
cd748f27 121{
aa1a691e
AR
122 if (!SwapDir::canStore(e, diskSpaceNeeded, load))
123 return false;
124
59b2d47f 125 if (IO->shedLoad())
aa1a691e 126 return false;
62e76326 127
aa1a691e
AR
128 load = IO->load();
129 return true;
cd748f27 130}
131
58373ff8
FC
132static void
133FreeObject(void *address)
134{
135 StoreSwapLogData *anObject = static_cast <StoreSwapLogData *>(address);
136 delete anObject;
137}
8e8d4f30 138
58373ff8
FC
139static int
140rev_int_sort(const void *A, const void *B)
141{
142 const int *i1 = (const int *)A;
143 const int *i2 = (const int *)B;
144 return *i2 - *i1;
145}
d3b3ab85 146
147void
58373ff8 148Fs::Ufs::UFSSwapDir::parseSizeL1L2()
cd748f27 149{
6da430a4
AJ
150 int i = GetInteger();
151 if (i <= 0)
59b2d47f 152 fatal("UFSSwapDir::parseSizeL1L2: invalid size value");
cd748f27 153
a0a5b161 154 const uint64_t size = static_cast<uint64_t>(i) << 20; // MBytes to Bytes
6da430a4 155
cd748f27 156 /* just reconfigure it */
0931cb03 157 if (reconfiguring) {
cc34568d
DK
158 if (size == maxSize())
159 debugs(3, 2, "Cache dir '" << path << "' size remains unchanged at " << i << " MB");
0931cb03 160 else
58373ff8 161 debugs(3, DBG_IMPORTANT, "Cache dir '" << path << "' size changed to " << i << " MB");
0931cb03 162 }
62e76326 163
d3b3ab85 164 max_size = size;
59b2d47f 165
166 l1 = GetInteger();
167
168 if (l1 <= 0)
169 fatal("UFSSwapDir::parseSizeL1L2: invalid level 1 directories value");
170
171 l2 = GetInteger();
172
173 if (l2 <= 0)
174 fatal("UFSSwapDir::parseSizeL1L2: invalid level 2 directories value");
cd748f27 175}
176
177void
58373ff8 178Fs::Ufs::UFSSwapDir::reconfigure()
cd748f27 179{
59b2d47f 180 parseSizeL1L2();
181 parseOptions(1);
cd748f27 182}
183
d3b3ab85 184void
58373ff8 185Fs::Ufs::UFSSwapDir::parse (int anIndex, char *aPath)
cd748f27 186{
59b2d47f 187 index = anIndex;
59b2d47f 188 path = xstrdup(aPath);
62e76326 189
1cd79a58 190 parseSizeL1L2();
191
59b2d47f 192 /* Initialise replacement policy stuff */
193 repl = createRemovalPolicy(Config.replPolicy);
62e76326 194
59b2d47f 195 parseOptions(0);
196}
62e76326 197
b9ae18aa 198void
58373ff8 199Fs::Ufs::UFSSwapDir::changeIO(DiskIOModule *module)
b9ae18aa 200{
201 DiskIOStrategy *anIO = module->createStrategy();
202 safe_free(ioType);
203 ioType = xstrdup(module->type());
204
205 delete IO->io;
206 IO->io = anIO;
207 /* Change the IO Options */
208
38c22baf
FC
209 if (currentIOOptions && currentIOOptions->options.size() > 2) {
210 delete currentIOOptions->options.back();
211 currentIOOptions->options.pop_back();
212 }
b9ae18aa 213
214 /* TODO: factor out these 4 lines */
215 ConfigOption *ioOptions = IO->io->getOptionTree();
216
ea25a575 217 if (currentIOOptions && ioOptions)
b9ae18aa 218 currentIOOptions->options.push_back(ioOptions);
219}
220
221bool
58373ff8 222Fs::Ufs::UFSSwapDir::optionIOParse(char const *option, const char *value, int isaReconfig)
59b2d47f 223{
b9ae18aa 224 if (strcmp(option, "IOEngine") != 0)
225 return false;
62e76326 226
18ec8500 227 if (isaReconfig)
b9ae18aa 228 /* silently ignore this */
229 return true;
62e76326 230
9c79325e 231 if (!value) {
b9ae18aa 232 self_destruct();
9c79325e
AJ
233 return false;
234 }
d3b3ab85 235
b9ae18aa 236 DiskIOModule *module = DiskIOModule::Find(value);
62e76326 237
9c79325e 238 if (!module) {
b9ae18aa 239 self_destruct();
9c79325e
AJ
240 return false;
241 }
8e8d4f30 242
b9ae18aa 243 changeIO(module);
244
245 return true;
246}
247
248void
58373ff8 249Fs::Ufs::UFSSwapDir::optionIODump(StoreEntry * e) const
b9ae18aa 250{
251 storeAppendPrintf(e, " IOEngine=%s", ioType);
252}
253
254ConfigOption *
58373ff8 255Fs::Ufs::UFSSwapDir::getOptionTree() const
b9ae18aa 256{
257 ConfigOption *parentResult = SwapDir::getOptionTree();
258
e2a8733e 259 if (currentIOOptions == NULL)
260 currentIOOptions = new ConfigOptionVector();
261
b9ae18aa 262 currentIOOptions->options.push_back(parentResult);
e2a8733e 263
b9ae18aa 264 currentIOOptions->options.push_back(new ConfigOptionAdapter<UFSSwapDir>(*const_cast<UFSSwapDir *>(this), &UFSSwapDir::optionIOParse, &UFSSwapDir::optionIODump));
265
5f2d056f 266 if (ConfigOption *ioOptions = IO->io->getOptionTree())
b9ae18aa 267 currentIOOptions->options.push_back(ioOptions);
268
e2a8733e 269 ConfigOption* result = currentIOOptions;
270
271 currentIOOptions = NULL;
272
273 return result;
cd748f27 274}
275
d3b3ab85 276void
58373ff8 277Fs::Ufs::UFSSwapDir::init()
d3b3ab85 278{
58373ff8 279 debugs(47, 3, HERE << "Initialising UFS SwapDir engine.");
d295d770 280 /* Parsing must be finished by now - force to NULL, don't delete */
b9ae18aa 281 currentIOOptions = NULL;
d3b3ab85 282 static int started_clean_event = 0;
283 static const char *errmsg =
62e76326 284 "\tFailed to verify one of the swap directories, Check cache.log\n"
285 "\tfor details. Run 'squid -z' to create swap directories\n"
286 "\tif needed, or if running Squid for the first time.";
59b2d47f 287 IO->init();
62e76326 288
d3b3ab85 289 if (verifyCacheDirs())
62e76326 290 fatal(errmsg);
291
d3b3ab85 292 openLog();
62e76326 293
d3b3ab85 294 rebuild();
62e76326 295
d3b3ab85 296 if (!started_clean_event) {
62e76326 297 eventAdd("UFS storeDirClean", CleanEvent, NULL, 15.0, 1);
298 started_clean_event = 1;
d3b3ab85 299 }
62e76326 300
2745fea5 301 (void) fsBlockSize(path, &fs.blksize);
d3b3ab85 302}
303
304void
58373ff8 305Fs::Ufs::UFSSwapDir::create()
d3b3ab85 306{
d295d770 307 debugs(47, 3, "Creating swap space in " << path);
d3b3ab85 308 createDirectory(path, 0);
309 createSwapSubDirs();
310}
311
23541b3e 312Fs::Ufs::UFSSwapDir::UFSSwapDir(char const *aType, const char *anIOType) :
f53969cc
SM
313 SwapDir(aType),
314 IO(NULL),
315 fsdata(NULL),
316 map(new FileMap()),
317 suggest(0),
318 l1(16),
319 l2(256),
320 swaplog_fd(-1),
321 currentIOOptions(new ConfigOptionVector()),
322 ioType(xstrdup(anIOType)),
323 cur_size(0),
7c44eb08
AR
324 n_disk_objects(0),
325 rebuilding_(false)
b7717b61 326{
327 /* modulename is only set to disk modules that are built, by configure,
328 * so the Find call should never return NULL here.
329 */
58373ff8 330 IO = new Fs::Ufs::UFSStrategy(DiskIOModule::Find(anIOType)->createStrategy());
b7717b61 331}
d3b3ab85 332
58373ff8 333Fs::Ufs::UFSSwapDir::~UFSSwapDir()
d3b3ab85 334{
335 if (swaplog_fd > -1) {
62e76326 336 file_close(swaplog_fd);
337 swaplog_fd = -1;
d3b3ab85 338 }
23541b3e 339 xfree(ioType);
39861955
FC
340 delete map;
341 delete IO;
342 delete currentIOOptions;
d3b3ab85 343}
344
d3b3ab85 345void
58373ff8 346Fs::Ufs::UFSSwapDir::dumpEntry(StoreEntry &e) const
d3b3ab85 347{
58373ff8
FC
348 debugs(47, DBG_CRITICAL, HERE << "FILENO "<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << e.swap_filen);
349 debugs(47, DBG_CRITICAL, HERE << "PATH " << fullPath(e.swap_filen, NULL) );
3900307b 350 e.dump(0);
d3b3ab85 351}
352
d3b3ab85 353bool
58373ff8 354Fs::Ufs::UFSSwapDir::doubleCheck(StoreEntry & e)
d3b3ab85 355{
62e76326 356
d3b3ab85 357 struct stat sb;
62e76326 358
c8f4eac4 359 if (::stat(fullPath(e.swap_filen, NULL), &sb) < 0) {
58373ff8 360 debugs(47, DBG_CRITICAL, HERE << "WARNING: Missing swap file");
62e76326 361 dumpEntry(e);
362 return true;
d3b3ab85 363 }
62e76326 364
d3b3ab85 365 if ((off_t)e.swap_file_sz != sb.st_size) {
58373ff8 366 debugs(47, DBG_CRITICAL, HERE << "WARNING: Size Mismatch. Entry size: "
187642d0 367 << e.swap_file_sz << ", file size: " << sb.st_size);
62e76326 368 dumpEntry(e);
369 return true;
d3b3ab85 370 }
62e76326 371
d3b3ab85 372 return false;
373}
374
375void
58373ff8 376Fs::Ufs::UFSSwapDir::statfs(StoreEntry & sentry) const
d3b3ab85 377{
378 int totl_kb = 0;
379 int free_kb = 0;
380 int totl_in = 0;
381 int free_in = 0;
382 int x;
383 storeAppendPrintf(&sentry, "First level subdirectories: %d\n", l1);
384 storeAppendPrintf(&sentry, "Second level subdirectories: %d\n", l2);
c91ca3ce 385 storeAppendPrintf(&sentry, "Maximum Size: %" PRIu64 " KB\n", maxSize() >> 10);
cc34568d 386 storeAppendPrintf(&sentry, "Current Size: %.2f KB\n", currentSize() / 1024.0);
d3b3ab85 387 storeAppendPrintf(&sentry, "Percent Used: %0.2f%%\n",
cc34568d 388 Math::doublePercent(currentSize(), maxSize()));
d3b3ab85 389 storeAppendPrintf(&sentry, "Filemap bits in use: %d of %d (%d%%)\n",
75f8f9a2
FC
390 map->numFilesInMap(), map->capacity(),
391 Math::intPercent(map->numFilesInMap(), map->capacity()));
2745fea5 392 x = fsStats(path, &totl_kb, &free_kb, &totl_in, &free_in);
62e76326 393
d3b3ab85 394 if (0 == x) {
62e76326 395 storeAppendPrintf(&sentry, "Filesystem Space in use: %d/%d KB (%d%%)\n",
396 totl_kb - free_kb,
397 totl_kb,
a98bcbee 398 Math::intPercent(totl_kb - free_kb, totl_kb));
62e76326 399 storeAppendPrintf(&sentry, "Filesystem Inodes in use: %d/%d (%d%%)\n",
400 totl_in - free_in,
401 totl_in,
a98bcbee 402 Math::intPercent(totl_in - free_in, totl_in));
d3b3ab85 403 }
62e76326 404
d3b3ab85 405 storeAppendPrintf(&sentry, "Flags:");
62e76326 406
d3b3ab85 407 if (flags.selected)
62e76326 408 storeAppendPrintf(&sentry, " SELECTED");
409
d3b3ab85 410 if (flags.read_only)
62e76326 411 storeAppendPrintf(&sentry, " READ-ONLY");
412
d3b3ab85 413 storeAppendPrintf(&sentry, "\n");
59b2d47f 414
415 IO->statfs(sentry);
d3b3ab85 416}
417
418void
58373ff8 419Fs::Ufs::UFSSwapDir::maintain()
d3b3ab85 420{
5f662601
AJ
421 /* TODO: possible options for improvement;
422 *
423 * Note that too much aggression here is not good. It means that disk
424 * controller is getting a long queue of removals to act on, along
425 * with its regular I/O queue, and that client traffic is 'paused'
426 * and growing the network I/O queue as well while the scan happens.
427 * Possibly bad knock-on effects as Squid catches up on all that.
428 *
429 * Bug 2448 may have been a sign of what can wrong. At the least it
430 * provides a test case for aggression effects in overflow conditions.
431 *
432 * - base removal limit on space saved, instead of count ?
433 *
434 * - base removal rate on a traffic speed counter ?
435 * as the purge took up more time out of the second it would grow to
436 * a graceful full pause
437 *
438 * - pass out a value to cause another event to be scheduled immediately
439 * instead of waiting a whole second more ?
440 * knock on; schedule less if all caches are under low-water
441 *
442 * - admin configurable removal rate or count ?
443 * the current numbers are arbitrary, config helps with experimental
444 * trials and future-proofing the install base.
445 * we also have this indirectly by shifting the relative positions
446 * of low-, high- water and the total capacity limit.
447 */
650c4b88 448
5f662601
AJ
449 // minSize() is swap_low_watermark in bytes
450 const uint64_t lowWaterSz = minSize();
62e76326 451
5f662601 452 if (currentSize() < lowWaterSz) {
d842c91c
AJ
453 debugs(47, 5, "space still available in " << path);
454 return;
455 }
456
457 /* We can't delete objects while rebuilding swap */
458 /* XXX each store should start maintaining as it comes online. */
459 if (StoreController::store_dirs_rebuilding) {
460 // suppress the warnings, except once each minute
461 static int64_t lastWarn = 0;
462 int warnLevel = 3;
463 if (lastWarn+60 < squid_curtime) {
464 lastWarn = squid_curtime;
465 warnLevel = DBG_IMPORTANT;
466 }
467 debugs(47, warnLevel, StoreController::store_dirs_rebuilding << " cache_dir still rebuilding. Skip GC for " << path);
5f662601
AJ
468 return;
469 }
650c4b88 470
5f662601
AJ
471 // maxSize() is cache_dir total size in bytes
472 const uint64_t highWaterSz = ((maxSize() * Config.Swap.highWaterMark) / 100);
473
474 // f is percentage of 'gap' filled between low- and high-water.
475 // Used to reduced purge rate when between water markers, and
476 // to multiply it more agressively the further above high-water
477 // it reaches. But in a graceful linear growth curve.
478 double f = 1.0;
479 if (highWaterSz > lowWaterSz) {
480 // might be equal. n/0 is bad.
481 f = (double) (currentSize() - lowWaterSz) / (highWaterSz - lowWaterSz);
482 }
650c4b88 483
5f662601 484 // how deep to look for a single object that can be removed
650c4b88 485 int max_scan = (int) (f * 400.0 + 100.0);
486
5f662601 487 // try to purge only this many objects this cycle.
7bcaf76f 488 int max_remove = (int) (f * 300.0 + 20.0);
650c4b88 489
490 /*
491 * This is kinda cheap, but so we need this priority hack?
492 */
5f662601 493 debugs(47, 3, "f=" << f << ", max_scan=" << max_scan << ", max_remove=" << max_remove);
62e76326 494
5f662601 495 RemovalPurgeWalker *walker = repl->PurgeInit(repl, max_scan);
62e76326 496
5f662601
AJ
497 int removed = 0;
498 // only purge while above low-water
499 while (currentSize() >= lowWaterSz) {
62e76326 500
5f662601
AJ
501 // stop if we reached max removals for this cycle,
502 // Bug 2448 may be from this not clearing enough,
503 // but it predates the current algorithm so not sure
62e76326 504 if (removed >= max_remove)
505 break;
506
5f662601 507 StoreEntry *e = walker->Next(walker);
62e76326 508
5f662601
AJ
509 // stop if all objects are locked / in-use,
510 // or the cache is empty
62e76326 511 if (!e)
58373ff8 512 break; /* no more objects */
62e76326 513
cb4185f1 514 ++removed;
62e76326 515
5f33b71d 516 e->release();
d3b3ab85 517 }
62e76326 518
d3b3ab85 519 walker->Done(walker);
5f662601 520 debugs(47, (removed ? 2 : 3), path <<
26ac0430 521 " removed " << removed << "/" << max_remove << " f=" <<
bf8fe701 522 std::setprecision(4) << f << " max_scan=" << max_scan);
5f662601
AJ
523
524 // what if cache is still over the high watermark ?
525 // Store::Maintain() schedules another purge in 1 second.
d3b3ab85 526}
527
d3b3ab85 528void
58373ff8 529Fs::Ufs::UFSSwapDir::reference(StoreEntry &e)
d3b3ab85 530{
58373ff8 531 debugs(47, 3, HERE << "referencing " << &e << " " <<
187642d0 532 e.swap_dirn << "/" << e.swap_filen);
62e76326 533
d3b3ab85 534 if (repl->Referenced)
62e76326 535 repl->Referenced(repl, &e, &e.repl);
536}
d3b3ab85 537
4c973beb 538bool
2745fea5 539Fs::Ufs::UFSSwapDir::dereference(StoreEntry & e)
d3b3ab85 540{
58373ff8 541 debugs(47, 3, HERE << "dereferencing " << &e << " " <<
187642d0 542 e.swap_dirn << "/" << e.swap_filen);
62e76326 543
d3b3ab85 544 if (repl->Dereferenced)
62e76326 545 repl->Dereferenced(repl, &e, &e.repl);
4c973beb
AR
546
547 return true; // keep e in the global store_table
d3b3ab85 548}
549
550StoreIOState::Pointer
58373ff8 551Fs::Ufs::UFSSwapDir::createStoreIO(StoreEntry &e, StoreIOState::STFNCB * file_callback, StoreIOState::STIOCB * aCallback, void *callback_data)
d3b3ab85 552{
18ec8500 553 return IO->create (this, &e, file_callback, aCallback, callback_data);
d3b3ab85 554}
555
556StoreIOState::Pointer
58373ff8 557Fs::Ufs::UFSSwapDir::openStoreIO(StoreEntry &e, StoreIOState::STFNCB * file_callback, StoreIOState::STIOCB * aCallback, void *callback_data)
d3b3ab85 558{
18ec8500 559 return IO->open (this, &e, file_callback, aCallback, callback_data);
d3b3ab85 560}
561
562int
58373ff8 563Fs::Ufs::UFSSwapDir::mapBitTest(sfileno filn)
d3b3ab85 564{
75f8f9a2 565 return map->testBit(filn);
d3b3ab85 566}
567
568void
58373ff8 569Fs::Ufs::UFSSwapDir::mapBitSet(sfileno filn)
d3b3ab85 570{
75f8f9a2 571 map->setBit(filn);
d3b3ab85 572}
573
574void
58373ff8 575Fs::Ufs::UFSSwapDir::mapBitReset(sfileno filn)
d3b3ab85 576{
577 /*
75f8f9a2
FC
578 * We have to test the bit before calling clearBit as
579 * it doesn't do bounds checking and blindly assumes
d3b3ab85 580 * filn is a valid file number, but it might not be because
581 * the map is dynamic in size. Also clearing an already clear
582 * bit puts the map counter of-of-whack.
583 */
62e76326 584
75f8f9a2
FC
585 if (map->testBit(filn))
586 map->clearBit(filn);
d3b3ab85 587}
588
589int
58373ff8 590Fs::Ufs::UFSSwapDir::mapBitAllocate()
d3b3ab85 591{
592 int fn;
75f8f9a2
FC
593 fn = map->allocate(suggest);
594 map->setBit(fn);
d3b3ab85 595 suggest = fn + 1;
596 return fn;
597}
598
d3b3ab85 599char *
58373ff8 600Fs::Ufs::UFSSwapDir::swapSubDir(int subdirn)const
d3b3ab85 601{
ef364f64 602 LOCAL_ARRAY(char, fullfilename, MAXPATHLEN);
d3b3ab85 603 assert(0 <= subdirn && subdirn < l1);
ef364f64 604 snprintf(fullfilename, MAXPATHLEN, "%s/%02X", path, subdirn);
d3b3ab85 605 return fullfilename;
606}
607
608int
58373ff8 609Fs::Ufs::UFSSwapDir::createDirectory(const char *aPath, int should_exist)
d3b3ab85 610{
611 int created = 0;
62e76326 612
d3b3ab85 613 struct stat st;
614 getCurrentTime();
62e76326 615
e4ae841b 616 if (0 == ::stat(aPath, &st)) {
62e76326 617 if (S_ISDIR(st.st_mode)) {
58373ff8 618 debugs(47, (should_exist ? 3 : DBG_IMPORTANT), aPath << " exists");
62e76326 619 } else {
e4ae841b 620 fatalf("Swap directory %s is not a directory.", aPath);
62e76326 621 }
e4ae841b 622 } else if (0 == mkdir(aPath, 0755)) {
58373ff8 623 debugs(47, (should_exist ? DBG_IMPORTANT : 3), aPath << " created");
62e76326 624 created = 1;
d3b3ab85 625 } else {
b69e9ffa
AJ
626 int xerrno = errno;
627 fatalf("Failed to make swap directory %s: %s", aPath, xstrerr(xerrno));
d3b3ab85 628 }
62e76326 629
d3b3ab85 630 return created;
631}
632
633bool
58373ff8 634Fs::Ufs::UFSSwapDir::pathIsDirectory(const char *aPath)const
d3b3ab85 635{
62e76326 636
d3b3ab85 637 struct stat sb;
62e76326 638
e4ae841b 639 if (::stat(aPath, &sb) < 0) {
b69e9ffa
AJ
640 int xerrno = errno;
641 debugs(47, DBG_CRITICAL, "ERROR: " << aPath << ": " << xstrerr(xerrno));
62e76326 642 return false;
d3b3ab85 643 }
62e76326 644
d3b3ab85 645 if (S_ISDIR(sb.st_mode) == 0) {
58373ff8 646 debugs(47, DBG_CRITICAL, "WARNING: " << aPath << " is not a directory");
62e76326 647 return false;
d3b3ab85 648 }
62e76326 649
d3b3ab85 650 return true;
651}
652
d3b3ab85 653bool
58373ff8 654Fs::Ufs::UFSSwapDir::verifyCacheDirs()
d3b3ab85 655{
656 if (!pathIsDirectory(path))
62e76326 657 return true;
658
cb4185f1 659 for (int j = 0; j < l1; ++j) {
62e76326 660 char const *aPath = swapSubDir(j);
661
662 if (!pathIsDirectory(aPath))
663 return true;
d3b3ab85 664 }
62e76326 665
d3b3ab85 666 return false;
667}
668
669void
58373ff8 670Fs::Ufs::UFSSwapDir::createSwapSubDirs()
d3b3ab85 671{
d3b3ab85 672 LOCAL_ARRAY(char, name, MAXPATHLEN);
62e76326 673
cb4185f1 674 for (int i = 0; i < l1; ++i) {
62e76326 675 snprintf(name, MAXPATHLEN, "%s/%02X", path, i);
676
59b2d47f 677 int should_exist;
678
62e76326 679 if (createDirectory(name, 0))
680 should_exist = 0;
681 else
682 should_exist = 1;
683
58373ff8 684 debugs(47, DBG_IMPORTANT, "Making directories in " << name);
62e76326 685
cb4185f1 686 for (int k = 0; k < l2; ++k) {
62e76326 687 snprintf(name, MAXPATHLEN, "%s/%02X/%02X", path, i, k);
688 createDirectory(name, should_exist);
689 }
d3b3ab85 690 }
691}
692
693char *
58373ff8 694Fs::Ufs::UFSSwapDir::logFile(char const *ext) const
d3b3ab85 695{
ef364f64
AJ
696 LOCAL_ARRAY(char, lpath, MAXPATHLEN);
697 LOCAL_ARRAY(char, pathtmp, MAXPATHLEN);
d3b3ab85 698 LOCAL_ARRAY(char, digit, 32);
699 char *pathtmp2;
62e76326 700
d3b3ab85 701 if (Config.Log.swap) {
ef364f64 702 xstrncpy(pathtmp, path, MAXPATHLEN - 64);
62e76326 703 pathtmp2 = pathtmp;
704
705 while ((pathtmp2 = strchr(pathtmp2, '/')) != NULL)
706 *pathtmp2 = '.';
707
708 while (strlen(pathtmp) && pathtmp[strlen(pathtmp) - 1] == '.')
709 pathtmp[strlen(pathtmp) - 1] = '\0';
710
cb4185f1 711 for (pathtmp2 = pathtmp; *pathtmp2 == '.'; ++pathtmp2);
ef364f64 712 snprintf(lpath, MAXPATHLEN - 64, Config.Log.swap, pathtmp2);
62e76326 713
ef364f64 714 if (strncmp(lpath, Config.Log.swap, MAXPATHLEN - 64) == 0) {
62e76326 715 strcat(lpath, ".");
716 snprintf(digit, 32, "%02d", index);
717 strncat(lpath, digit, 3);
718 }
d3b3ab85 719 } else {
ef364f64 720 xstrncpy(lpath, path, MAXPATHLEN - 64);
62e76326 721 strcat(lpath, "/swap.state");
d3b3ab85 722 }
62e76326 723
d3b3ab85 724 if (ext)
62e76326 725 strncat(lpath, ext, 16);
726
d3b3ab85 727 return lpath;
728}
729
730void
58373ff8 731Fs::Ufs::UFSSwapDir::openLog()
d3b3ab85 732{
7c44eb08
AR
733 assert(NumberOfUFSDirs || !UFSDirToGlobalDirMapping);
734 ++NumberOfUFSDirs;
735 assert(NumberOfUFSDirs <= Config.cacheSwap.n_configured);
736
737 if (rebuilding_) { // we did not close the temporary log used for rebuilding
738 assert(swaplog_fd >= 0);
739 return;
740 }
741
d3b3ab85 742 char *logPath;
743 logPath = logFile();
744 swaplog_fd = file_open(logPath, O_WRONLY | O_CREAT | O_BINARY);
62e76326 745
d3b3ab85 746 if (swaplog_fd < 0) {
b69e9ffa
AJ
747 int xerrno = errno;
748 debugs(50, DBG_IMPORTANT, "ERROR opening swap log " << logPath << ": " << xstrerr(xerrno));
58373ff8 749 fatal("UFSSwapDir::openLog: Failed to open swap log.");
d3b3ab85 750 }
62e76326 751
58373ff8 752 debugs(50, 3, HERE << "Cache Dir #" << index << " log opened on FD " << swaplog_fd);
d3b3ab85 753}
754
755void
58373ff8 756Fs::Ufs::UFSSwapDir::closeLog()
d3b3ab85 757{
58373ff8 758 if (swaplog_fd < 0) /* not open */
62e76326 759 return;
760
7c44eb08
AR
761 --NumberOfUFSDirs;
762 assert(NumberOfUFSDirs >= 0);
763 if (!NumberOfUFSDirs)
764 safe_free(UFSDirToGlobalDirMapping);
765
766 if (rebuilding_) // we cannot close the temporary log used for rebuilding
767 return;
768
d3b3ab85 769 file_close(swaplog_fd);
62e76326 770
bf8fe701 771 debugs(47, 3, "Cache Dir #" << index << " log closed on FD " << swaplog_fd);
62e76326 772
d3b3ab85 773 swaplog_fd = -1;
d3b3ab85 774}
775
776bool
58373ff8 777Fs::Ufs::UFSSwapDir::validL1(int anInt) const
d3b3ab85 778{
779 return anInt < l1;
780}
781
782bool
58373ff8 783Fs::Ufs::UFSSwapDir::validL2(int anInt) const
d3b3ab85 784{
785 return anInt < l2;
786}
787
d3b3ab85 788StoreEntry *
58373ff8 789Fs::Ufs::UFSSwapDir::addDiskRestore(const cache_key * key,
187642d0
A
790 sfileno file_number,
791 uint64_t swap_file_sz,
792 time_t expires,
793 time_t timestamp,
794 time_t lastref,
795 time_t lastmod,
796 uint32_t refcount,
797 uint16_t newFlags,
ced8def3 798 int)
d3b3ab85 799{
800 StoreEntry *e = NULL;
58373ff8 801 debugs(47, 5, HERE << storeKeyText(key) <<
bf8fe701 802 ", fileno="<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << file_number);
62e76326 803 /* if you call this you'd better be sure file_number is not
d3b3ab85 804 * already in use! */
c8f4eac4 805 e = new StoreEntry();
d3b3ab85 806 e->store_status = STORE_OK;
3900307b 807 e->setMemStatus(NOT_IN_MEMORY);
d3b3ab85 808 e->swap_status = SWAPOUT_DONE;
809 e->swap_filen = file_number;
810 e->swap_dirn = index;
811 e->swap_file_sz = swap_file_sz;
d3b3ab85 812 e->lastref = lastref;
813 e->timestamp = timestamp;
814 e->expires = expires;
438b41ba 815 e->lastModified(lastmod);
d3b3ab85 816 e->refcount = refcount;
e4ae841b 817 e->flags = newFlags;
d3b3ab85 818 EBIT_CLR(e->flags, RELEASE_REQUEST);
39fe14b2 819 e->clearPrivate();
d3b3ab85 820 e->ping_status = PING_NONE;
821 EBIT_CLR(e->flags, ENTRY_VALIDATED);
822 mapBitSet(e->swap_filen);
a86ff82a
DK
823 cur_size += fs.blksize * sizeInBlocks(e->swap_file_sz);
824 ++n_disk_objects;
58373ff8 825 e->hashInsert(key); /* do it after we clear KEY_PRIVATE */
d3b3ab85 826 replacementAdd (e);
827 return e;
828}
829
c6e51aff 830void
58373ff8 831Fs::Ufs::UFSSwapDir::undoAddDiskRestore(StoreEntry *e)
c6e51aff
AR
832{
833 debugs(47, 5, HERE << *e);
834 replacementRemove(e); // checks swap_dirn so do it before we invalidate it
835 // Do not unlink the file as it might be used by a subsequent entry.
836 mapBitReset(e->swap_filen);
837 e->swap_filen = -1;
838 e->swap_dirn = -1;
839 cur_size -= fs.blksize * sizeInBlocks(e->swap_file_sz);
840 --n_disk_objects;
841}
842
d3b3ab85 843void
58373ff8 844Fs::Ufs::UFSSwapDir::rebuild()
d3b3ab85 845{
bef81ea5 846 ++StoreController::store_dirs_rebuilding;
58373ff8 847 eventAdd("storeRebuild", Fs::Ufs::RebuildState::RebuildStep, new Fs::Ufs::RebuildState(this), 0.0, 1);
d3b3ab85 848}
849
850void
58373ff8 851Fs::Ufs::UFSSwapDir::closeTmpSwapLog()
d3b3ab85 852{
7c44eb08
AR
853 assert(rebuilding_);
854 rebuilding_ = false;
855
f087640f
AJ
856 char *swaplog_path = xstrdup(logFile(NULL)); // where the swaplog should be
857 char *tmp_path = xstrdup(logFile(".new")); // the temporary file we have generated
d3b3ab85 858 int fd;
859 file_close(swaplog_fd);
62e76326 860
f087640f
AJ
861 if (xrename(tmp_path, swaplog_path) < 0) {
862 fatalf("Failed to rename log file %s to %s", tmp_path, swaplog_path);
d3b3ab85 863 }
62e76326 864
d3b3ab85 865 fd = file_open(swaplog_path, O_WRONLY | O_CREAT | O_BINARY);
62e76326 866
d3b3ab85 867 if (fd < 0) {
b69e9ffa
AJ
868 int xerrno = errno;
869 debugs(50, DBG_IMPORTANT, "ERROR: " << swaplog_path << ": " << xstrerr(xerrno));
c031952f 870 fatalf("Failed to open swap log %s", swaplog_path);
d3b3ab85 871 }
62e76326 872
f087640f
AJ
873 xfree(swaplog_path);
874 xfree(tmp_path);
d3b3ab85 875 swaplog_fd = fd;
f087640f 876 debugs(47, 3, "Cache Dir #" << index << " log opened on FD " << fd);
d3b3ab85 877}
878
879FILE *
58373ff8 880Fs::Ufs::UFSSwapDir::openTmpSwapLog(int *clean_flag, int *zero_flag)
d3b3ab85 881{
7c44eb08
AR
882 assert(!rebuilding_);
883
d3b3ab85 884 char *swaplog_path = xstrdup(logFile(NULL));
885 char *clean_path = xstrdup(logFile(".last-clean"));
886 char *new_path = xstrdup(logFile(".new"));
62e76326 887
d3b3ab85 888 struct stat log_sb;
62e76326 889
d3b3ab85 890 struct stat clean_sb;
891 FILE *fp;
892 int fd;
62e76326 893
c8f4eac4 894 if (::stat(swaplog_path, &log_sb) < 0) {
f087640f 895 debugs(47, DBG_IMPORTANT, "Cache Dir #" << index << ": No log file");
62e76326 896 safe_free(swaplog_path);
897 safe_free(clean_path);
898 safe_free(new_path);
899 return NULL;
d3b3ab85 900 }
62e76326 901
d3b3ab85 902 *zero_flag = log_sb.st_size == 0 ? 1 : 0;
903 /* close the existing write-only FD */
62e76326 904
d3b3ab85 905 if (swaplog_fd >= 0)
62e76326 906 file_close(swaplog_fd);
907
d3b3ab85 908 /* open a write-only FD for the new log */
909 fd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
26ac0430 910
d3b3ab85 911 if (fd < 0) {
b69e9ffa
AJ
912 int xerrno = errno;
913 debugs(50, DBG_IMPORTANT, "ERROR: while opening swap log" << new_path << ": " << xstrerr(xerrno));
f087640f 914 fatalf("Failed to open swap log %s", new_path);
d3b3ab85 915 }
26ac0430 916
d3b3ab85 917 swaplog_fd = fd;
7c44eb08 918 rebuilding_ = true;
47f6e231 919
95ec9c9d 920 {
155d0ed6
AR
921 const StoreSwapLogHeader header;
922 MemBuf buf;
923 buf.init(header.record_size, header.record_size);
924 buf.append(reinterpret_cast<const char*>(&header), sizeof(header));
925 // Pad to keep in sync with UFSSwapDir::writeCleanStart().
786f6516
AR
926 memset(buf.space(), 0, header.gapSize());
927 buf.appended(header.gapSize());
155d0ed6
AR
928 file_write(swaplog_fd, -1, buf.content(), buf.contentSize(),
929 NULL, NULL, buf.freeFunc());
95ec9c9d 930 }
47f6e231 931
d3b3ab85 932 /* open a read-only stream of the old log */
933 fp = fopen(swaplog_path, "rb");
62e76326 934
b69e9ffa
AJ
935 if (!fp) {
936 int xerrno = errno;
937 debugs(50, DBG_CRITICAL, "ERROR: while opening " << swaplog_path << ": " << xstrerr(xerrno));
f087640f 938 fatalf("Failed to open swap log for reading %s", swaplog_path);
d3b3ab85 939 }
62e76326 940
d3b3ab85 941 memset(&clean_sb, '\0', sizeof(struct stat));
62e76326 942
c8f4eac4 943 if (::stat(clean_path, &clean_sb) < 0)
62e76326 944 *clean_flag = 0;
d3b3ab85 945 else if (clean_sb.st_mtime < log_sb.st_mtime)
62e76326 946 *clean_flag = 0;
d3b3ab85 947 else
62e76326 948 *clean_flag = 1;
949
d3b3ab85 950 safeunlink(clean_path, 1);
62e76326 951
d3b3ab85 952 safe_free(swaplog_path);
62e76326 953
d3b3ab85 954 safe_free(clean_path);
62e76326 955
d3b3ab85 956 safe_free(new_path);
62e76326 957
d3b3ab85 958 return fp;
959}
960
59b2d47f 961/*
962 * Begin the process to write clean cache state. For AUFS this means
963 * opening some log files and allocating write buffers. Return 0 if
964 * we succeed, and assign the 'func' and 'data' return pointers.
965 */
d3b3ab85 966int
58373ff8 967Fs::Ufs::UFSSwapDir::writeCleanStart()
d3b3ab85 968{
969 UFSCleanLog *state = new UFSCleanLog(this);
47f6e231 970 StoreSwapLogHeader header;
d3b3ab85 971#if HAVE_FCHMOD
62e76326 972
d3b3ab85 973 struct stat sb;
974#endif
62e76326 975
d3b3ab85 976 cleanLog = NULL;
977 state->newLog = xstrdup(logFile(".clean"));
978 state->fd = file_open(state->newLog, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY);
62e76326 979
d3b3ab85 980 if (state->fd < 0) {
62e76326 981 xfree(state->newLog);
982 delete state;
983 return -1;
d3b3ab85 984 }
62e76326 985
d3b3ab85 986 state->cur = xstrdup(logFile(NULL));
987 state->cln = xstrdup(logFile(".last-clean"));
988 state->outbuf = (char *)xcalloc(CLEAN_BUF_SZ, 1);
989 state->outbuf_offset = 0;
47f6e231 990 /*copy the header */
41d00cd3 991 memcpy(state->outbuf, &header, sizeof(StoreSwapLogHeader));
155d0ed6 992 // Leave a gap to keep in sync with UFSSwapDir::openTmpSwapLog().
786f6516 993 memset(state->outbuf + sizeof(StoreSwapLogHeader), 0, header.gapSize());
47f6e231 994 state->outbuf_offset += header.record_size;
995
d3b3ab85 996 state->walker = repl->WalkInit(repl);
997 ::unlink(state->cln);
58373ff8 998 debugs(47, 3, HERE << "opened " << state->newLog << ", FD " << state->fd);
d3b3ab85 999#if HAVE_FCHMOD
62e76326 1000
c8f4eac4 1001 if (::stat(state->cur, &sb) == 0)
62e76326 1002 fchmod(state->fd, sb.st_mode);
1003
d3b3ab85 1004#endif
26ac0430 1005
d3b3ab85 1006 cleanLog = state;
1007 return 0;
1008}
1009
d3b3ab85 1010void
58373ff8 1011Fs::Ufs::UFSSwapDir::writeCleanDone()
d3b3ab85 1012{
1013 UFSCleanLog *state = (UFSCleanLog *)cleanLog;
1014 int fd;
62e76326 1015
d3b3ab85 1016 if (NULL == state)
62e76326 1017 return;
1018
d3b3ab85 1019 if (state->fd < 0)
62e76326 1020 return;
1021
d3b3ab85 1022 state->walker->Done(state->walker);
62e76326 1023
d3b3ab85 1024 if (FD_WRITE_METHOD(state->fd, state->outbuf, state->outbuf_offset) < 0) {
b69e9ffa
AJ
1025 int xerrno = errno;
1026 debugs(50, DBG_CRITICAL, MYNAME << state->newLog << ": write: " << xstrerr(xerrno));
1027 debugs(50, DBG_CRITICAL, MYNAME << "Current swap logfile not replaced.");
62e76326 1028 file_close(state->fd);
1029 state->fd = -1;
1030 ::unlink(state->newLog);
d3b3ab85 1031 }
62e76326 1032
d3b3ab85 1033 safe_free(state->outbuf);
1034 /*
1035 * You can't rename open files on Microsoft "operating systems"
1036 * so we have to close before renaming.
1037 */
1038 closeLog();
1039 /* save the fd value for a later test */
1040 fd = state->fd;
1041 /* rename */
62e76326 1042
d3b3ab85 1043 if (state->fd >= 0) {
be266cb2 1044#if _SQUID_OS2_ || _SQUID_WINDOWS_
62e76326 1045 file_close(state->fd);
1046 state->fd = -1;
d3b3ab85 1047#endif
62e76326 1048
1049 xrename(state->newLog, state->cur);
d3b3ab85 1050 }
62e76326 1051
d3b3ab85 1052 /* touch a timestamp file if we're not still validating */
bef81ea5 1053 if (StoreController::store_dirs_rebuilding)
62e76326 1054 (void) 0;
d3b3ab85 1055 else if (fd < 0)
62e76326 1056 (void) 0;
d3b3ab85 1057 else
62e76326 1058 file_close(file_open(state->cln, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY));
1059
d3b3ab85 1060 /* close */
1061 safe_free(state->cur);
62e76326 1062
d3b3ab85 1063 safe_free(state->newLog);
62e76326 1064
d3b3ab85 1065 safe_free(state->cln);
62e76326 1066
d3b3ab85 1067 if (state->fd >= 0)
62e76326 1068 file_close(state->fd);
1069
d3b3ab85 1070 state->fd = -1;
62e76326 1071
d3b3ab85 1072 delete state;
62e76326 1073
d3b3ab85 1074 cleanLog = NULL;
1075}
1076
7c44eb08
AR
1077/// safely cleans a few unused files if possible
1078int
1079Fs::Ufs::UFSSwapDir::HandleCleanEvent()
d3b3ab85 1080{
1081 static int swap_index = 0;
1082 int i;
1083 int j = 0;
1084 int n = 0;
7c44eb08
AR
1085
1086 if (!NumberOfUFSDirs)
1087 return 0; // probably in the middle of reconfiguration
62e76326 1088
d3b3ab85 1089 if (NULL == UFSDirToGlobalDirMapping) {
62e76326 1090 SwapDir *sd;
1091 /*
1092 * Initialize the little array that translates UFS cache_dir
1093 * number into the Config.cacheSwap.swapDirs array index.
1094 */
1095 UFSDirToGlobalDirMapping = (int *)xcalloc(NumberOfUFSDirs, sizeof(*UFSDirToGlobalDirMapping));
1096
cb4185f1 1097 for (i = 0, n = 0; i < Config.cacheSwap.n_configured; ++i) {
c8f4eac4 1098 /* This is bogus, the controller should just clean each instance once */
1099 sd = dynamic_cast <SwapDir *>(INDEXSD(i));
62e76326 1100
1101 if (!UFSSwapDir::IsUFSDir(sd))
1102 continue;
1103
1104 UFSSwapDir *usd = dynamic_cast<UFSSwapDir *>(sd);
1105
1106 assert (usd);
1107
a38ec4b1
FC
1108 UFSDirToGlobalDirMapping[n] = i;
1109 ++n;
62e76326 1110
1111 j += (usd->l1 * usd->l2);
1112 }
1113
1114 assert(n == NumberOfUFSDirs);
1115 /*
1116 * Start the commonUfsDirClean() swap_index with a random
1117 * value. j equals the total number of UFS level 2
1118 * swap directories
1119 */
c03adf02 1120 std::mt19937 mt(static_cast<uint32_t>(getCurrentTime() & 0xFFFFFFFF));
8ed8fa40 1121 xuniform_int_distribution<> dist(0, j);
c03adf02 1122 swap_index = dist(mt);
d3b3ab85 1123 }
62e76326 1124
bef81ea5 1125 /* if the rebuild is finished, start cleaning directories. */
1126 if (0 == StoreController::store_dirs_rebuilding) {
62e76326 1127 n = DirClean(swap_index);
cb4185f1 1128 ++swap_index;
d3b3ab85 1129 }
62e76326 1130
7c44eb08
AR
1131 return n;
1132}
1133
1134void
1135Fs::Ufs::UFSSwapDir::CleanEvent(void *)
1136{
1137 const int n = HandleCleanEvent();
d3b3ab85 1138 eventAdd("storeDirClean", CleanEvent, NULL,
62e76326 1139 15.0 * exp(-0.25 * n), 1);
d3b3ab85 1140}
1141
58373ff8
FC
1142bool
1143Fs::Ufs::UFSSwapDir::IsUFSDir(SwapDir * sd)
d3b3ab85 1144{
1145 UFSSwapDir *mySD = dynamic_cast<UFSSwapDir *>(sd);
58373ff8 1146 return (mySD != 0) ;
d3b3ab85 1147}
1148
1149/*
d3b3ab85 1150 * XXX: this is broken - it assumes all cache dirs use the same
26ac0430 1151 * l1 and l2 scheme. -RBC 20021215. Partial fix is in place -
d3b3ab85 1152 * if not UFSSwapDir return 0;
1153 */
58373ff8
FC
1154bool
1155Fs::Ufs::UFSSwapDir::FilenoBelongsHere(int fn, int F0, int F1, int F2)
d3b3ab85 1156{
1157 int D1, D2;
1158 int L1, L2;
1159 int filn = fn;
1160 assert(F0 < Config.cacheSwap.n_configured);
c8f4eac4 1161 assert (UFSSwapDir::IsUFSDir (dynamic_cast<SwapDir *>(INDEXSD(F0))));
d3b3ab85 1162 UFSSwapDir *sd = dynamic_cast<UFSSwapDir *>(INDEXSD(F0));
62e76326 1163
d3b3ab85 1164 if (!sd)
62e76326 1165 return 0;
1166
d3b3ab85 1167 L1 = sd->l1;
62e76326 1168
d3b3ab85 1169 L2 = sd->l2;
62e76326 1170
d3b3ab85 1171 D1 = ((filn / L2) / L2) % L1;
62e76326 1172
d3b3ab85 1173 if (F1 != D1)
62e76326 1174 return 0;
1175
d3b3ab85 1176 D2 = (filn / L2) % L2;
62e76326 1177
d3b3ab85 1178 if (F2 != D2)
62e76326 1179 return 0;
1180
d3b3ab85 1181 return 1;
1182}
1183
1184int
58373ff8 1185Fs::Ufs::UFSSwapDir::validFileno(sfileno filn, int flag) const
d3b3ab85 1186{
1187 if (filn < 0)
62e76326 1188 return 0;
1189
d3b3ab85 1190 /*
1191 * If flag is set it means out-of-range file number should
1192 * be considered invalid.
1193 */
1194 if (flag)
75f8f9a2 1195 if (filn > map->capacity())
62e76326 1196 return 0;
1197
d3b3ab85 1198 return 1;
1199}
1200
d3b3ab85 1201void
58373ff8 1202Fs::Ufs::UFSSwapDir::unlinkFile(sfileno f)
d3b3ab85 1203{
58373ff8 1204 debugs(79, 3, HERE << "unlinking fileno " << std::setfill('0') <<
26ac0430
AJ
1205 std::hex << std::uppercase << std::setw(8) << f << " '" <<
1206 fullPath(f,NULL) << "'");
d3b3ab85 1207 /* commonUfsDirMapBitReset(this, f); */
59b2d47f 1208 IO->unlinkFile(fullPath(f,NULL));
1209}
1210
c521ad17 1211bool
58373ff8 1212Fs::Ufs::UFSSwapDir::unlinkdUseful() const
c521ad17
DK
1213{
1214 // unlinkd may be useful only in workers
1215 return IamWorkerProcess() && IO->io->unlinkdUseful();
1216}
1217
59b2d47f 1218void
58373ff8 1219Fs::Ufs::UFSSwapDir::unlink(StoreEntry & e)
59b2d47f 1220{
58373ff8 1221 debugs(79, 3, HERE << "dirno " << index << ", fileno "<<
bf8fe701 1222 std::setfill('0') << std::hex << std::uppercase << std::setw(8) << e.swap_filen);
c6e51aff 1223 if (e.swap_status == SWAPOUT_DONE) {
a86ff82a
DK
1224 cur_size -= fs.blksize * sizeInBlocks(e.swap_file_sz);
1225 --n_disk_objects;
1226 }
59b2d47f 1227 replacementRemove(&e);
1228 mapBitReset(e.swap_filen);
1229 UFSSwapDir::unlinkFile(e.swap_filen);
2745fea5
AR
1230 e.swap_filen = -1;
1231 e.swap_dirn = -1;
1232 e.swap_status = SWAPOUT_NONE;
d3b3ab85 1233}
1234
d3b3ab85 1235void
58373ff8 1236Fs::Ufs::UFSSwapDir::replacementAdd(StoreEntry * e)
d3b3ab85 1237{
58373ff8 1238 debugs(47, 4, HERE << "added node " << e << " to dir " << index);
d3b3ab85 1239 repl->Add(repl, e, &e->repl);
1240}
1241
d3b3ab85 1242void
58373ff8 1243Fs::Ufs::UFSSwapDir::replacementRemove(StoreEntry * e)
d3b3ab85 1244{
d3b3ab85 1245 if (e->swap_dirn < 0)
62e76326 1246 return;
1247
2745fea5 1248 SwapDirPointer SD = INDEXSD(e->swap_dirn);
62e76326 1249
c8f4eac4 1250 assert (dynamic_cast<UFSSwapDir *>(SD.getRaw()) == this);
62e76326 1251
58373ff8 1252 debugs(47, 4, HERE << "remove node " << e << " from dir " << index);
62e76326 1253
d3b3ab85 1254 repl->Remove(repl, e, &e->repl);
1255}
1256
1257void
58373ff8 1258Fs::Ufs::UFSSwapDir::dump(StoreEntry & entry) const
d3b3ab85 1259{
c91ca3ce 1260 storeAppendPrintf(&entry, " %" PRIu64 " %d %d", maxSize() >> 20, l1, l2);
59b2d47f 1261 dumpOptions(&entry);
d3b3ab85 1262}
1263
1264char *
58373ff8 1265Fs::Ufs::UFSSwapDir::fullPath(sfileno filn, char *fullpath) const
d3b3ab85 1266{
ef364f64 1267 LOCAL_ARRAY(char, fullfilename, MAXPATHLEN);
d3b3ab85 1268 int L1 = l1;
1269 int L2 = l2;
62e76326 1270
d3b3ab85 1271 if (!fullpath)
62e76326 1272 fullpath = fullfilename;
1273
d3b3ab85 1274 fullpath[0] = '\0';
62e76326 1275
ef364f64 1276 snprintf(fullpath, MAXPATHLEN, "%s/%02X/%02X/%08X",
62e76326 1277 path,
1278 ((filn / L2) / L2) % L1,
1279 (filn / L2) % L2,
1280 filn);
1281
d3b3ab85 1282 return fullpath;
1283}
59b2d47f 1284
1285int
58373ff8 1286Fs::Ufs::UFSSwapDir::callback()
59b2d47f 1287{
1288 return IO->callback();
1289}
1290
1291void
58373ff8 1292Fs::Ufs::UFSSwapDir::sync()
59b2d47f 1293{
1294 IO->sync();
1295}
c8f4eac4 1296
da9d3191 1297void
58373ff8 1298Fs::Ufs::UFSSwapDir::swappedOut(const StoreEntry &e)
da9d3191 1299{
a86ff82a
DK
1300 cur_size += fs.blksize * sizeInBlocks(e.swap_file_sz);
1301 ++n_disk_objects;
da9d3191
DK
1302}
1303
c8f4eac4 1304void
58373ff8 1305Fs::Ufs::UFSSwapDir::logEntry(const StoreEntry & e, int op) const
c8f4eac4 1306{
7c44eb08
AR
1307 if (swaplog_fd < 0) {
1308 debugs(36, 5, "cannot log " << e << " in the middle of reconfiguration");
1309 return;
1310 }
1311
58373ff8
FC
1312 StoreSwapLogData *s = new StoreSwapLogData;
1313 s->op = (char) op;
1314 s->swap_filen = e.swap_filen;
1315 s->timestamp = e.timestamp;
1316 s->lastref = e.lastref;
1317 s->expires = e.expires;
438b41ba 1318 s->lastmod = e.lastModified();
58373ff8
FC
1319 s->swap_file_sz = e.swap_file_sz;
1320 s->refcount = e.refcount;
1321 s->flags = e.flags;
1322 memcpy(s->key, e.key, SQUID_MD5_DIGEST_LENGTH);
1323 s->finalize();
1324 file_write(swaplog_fd,
1325 -1,
1326 s,
1327 sizeof(StoreSwapLogData),
1328 NULL,
1329 NULL,
1330 FreeObject);
c8f4eac4 1331}
1332
58373ff8
FC
1333int
1334Fs::Ufs::UFSSwapDir::DirClean(int swap_index)
c8f4eac4 1335{
58373ff8 1336 DIR *dir_pointer = NULL;
c8f4eac4 1337
58373ff8
FC
1338 LOCAL_ARRAY(char, p1, MAXPATHLEN + 1);
1339 LOCAL_ARRAY(char, p2, MAXPATHLEN + 1);
c8f4eac4 1340
58373ff8
FC
1341 int files[20];
1342 int swapfileno;
1343 int fn; /* same as swapfileno, but with dirn bits set */
1344 int n = 0;
1345 int k = 0;
1346 int N0, N1, N2;
1347 int D0, D1, D2;
1348 UFSSwapDir *SD;
1349 N0 = NumberOfUFSDirs;
1350 D0 = UFSDirToGlobalDirMapping[swap_index % N0];
1351 SD = dynamic_cast<UFSSwapDir *>(INDEXSD(D0));
1352 assert (SD);
1353 N1 = SD->l1;
1354 D1 = (swap_index / N0) % N1;
1355 N2 = SD->l2;
1356 D2 = ((swap_index / N0) / N1) % N2;
1357 snprintf(p1, MAXPATHLEN, "%s/%02X/%02X",
1358 SD->path, D1, D2);
1359 debugs(36, 3, HERE << "Cleaning directory " << p1);
1360 dir_pointer = opendir(p1);
c8f4eac4 1361
b69e9ffa
AJ
1362 if (!dir_pointer) {
1363 int xerrno = errno;
1364 if (xerrno == ENOENT) {
1365 debugs(36, DBG_CRITICAL, MYNAME << "WARNING: Creating " << p1);
58373ff8 1366 if (mkdir(p1, 0777) == 0)
58373ff8
FC
1367 return 0;
1368 }
1369
b69e9ffa 1370 debugs(50, DBG_CRITICAL, MYNAME << p1 << ": " << xstrerr(xerrno));
58373ff8
FC
1371 safeunlink(p1, 1);
1372 return 0;
1373 }
1374
1375 dirent_t *de;
1376 while ((de = readdir(dir_pointer)) != NULL && k < 20) {
1377 if (sscanf(de->d_name, "%X", &swapfileno) != 1)
1378 continue;
1379
1380 fn = swapfileno; /* XXX should remove this cruft ! */
1381
1382 if (SD->validFileno(fn, 1))
1383 if (SD->mapBitTest(fn))
1384 if (UFSSwapDir::FilenoBelongsHere(fn, D0, D1, D2))
1385 continue;
1386
1387 files[k] = swapfileno;
1388 ++k;
1389 }
1390
1391 closedir(dir_pointer);
1392
1393 if (k == 0)
1394 return 0;
1395
1396 qsort(files, k, sizeof(int), rev_int_sort);
1397
1398 if (k > 10)
1399 k = 10;
1400
1401 for (n = 0; n < k; ++n) {
1402 debugs(36, 3, HERE << "Cleaning file "<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << files[n]);
1403 snprintf(p2, MAXPATHLEN + 1, "%s/%08X", p1, files[n]);
1404 safeunlink(p2, 0);
1405 ++statCounter.swap.files_cleaned;
1406 }
1407
1408 debugs(36, 3, HERE << "Cleaned " << k << " unused files from " << p1);
1409 return k;
c8f4eac4 1410}
f53969cc 1411