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