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