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