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