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