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