2 * DEBUG: section 47 Store Directory Routines
3 * AUTHOR: Robert Collins
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
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.
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.
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.
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.
34 #define CLEAN_BUF_SZ 16384
36 #include "ConfigOption.h"
37 #include "DiskIO/DiskIOModule.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"
58 int Fs::Ufs::UFSSwapDir::NumberOfUFSDirs
= 0;
59 int *Fs::Ufs::UFSSwapDir::UFSDirToGlobalDirMapping
= NULL
;
61 class UFSCleanLog
: public SwapDir::CleanLog
65 UFSCleanLog(SwapDir
*);
66 /** Get the next entry that is a candidate for clean log writing
68 virtual const StoreEntry
*nextEntry();
69 /** "write" an entry to the clean log file.
71 virtual void write(StoreEntry
const &);
78 RemovalPolicyWalker
*walker
;
82 UFSCleanLog::UFSCleanLog(SwapDir
*aSwapDir
) :
83 cur(NULL
), newLog(NULL
), cln(NULL
), outbuf(NULL
),
84 outbuf_offset(0), fd(-1),walker(NULL
), sd(aSwapDir
)
88 UFSCleanLog::nextEntry()
90 const StoreEntry
*entry
= NULL
;
93 entry
= walker
->Next(walker
);
99 UFSCleanLog::write(StoreEntry
const &e
)
102 static size_t ss
= sizeof(StoreSwapLogData
);
103 s
.op
= (char) SWAP_LOG_ADD
;
104 s
.swap_filen
= e
.swap_filen
;
105 s
.timestamp
= e
.timestamp
;
106 s
.lastref
= e
.lastref
;
107 s
.expires
= e
.expires
;
108 s
.lastmod
= e
.lastmod
;
109 s
.swap_file_sz
= e
.swap_file_sz
;
110 s
.refcount
= e
.refcount
;
112 memcpy(&s
.key
, e
.key
, SQUID_MD5_DIGEST_LENGTH
);
114 memcpy(outbuf
+ outbuf_offset
, &s
, ss
);
118 if (outbuf_offset
+ ss
>= CLEAN_BUF_SZ
) {
119 if (FD_WRITE_METHOD(fd
, outbuf
, outbuf_offset
) < 0) {
120 /* XXX This error handling should probably move up to the caller */
121 debugs(50, DBG_CRITICAL
, HERE
<< newLog
<< ": write: " << xstrerror());
122 debugs(50, DBG_CRITICAL
, HERE
<< "Current swap logfile not replaced.");
136 Fs::Ufs::UFSSwapDir::canStore(const StoreEntry
&e
, int64_t diskSpaceNeeded
, int &load
) const
138 if (!SwapDir::canStore(e
, diskSpaceNeeded
, load
))
149 FreeObject(void *address
)
151 StoreSwapLogData
*anObject
= static_cast <StoreSwapLogData
*>(address
);
155 static QS rev_int_sort
;
157 rev_int_sort(const void *A
, const void *B
)
159 const int *i1
= (const int *)A
;
160 const int *i2
= (const int *)B
;
165 Fs::Ufs::UFSSwapDir::parseSizeL1L2()
167 int i
= GetInteger();
169 fatal("UFSSwapDir::parseSizeL1L2: invalid size value");
171 const uint64_t size
= static_cast<uint64_t>(i
) << 20; // MBytes to Bytes
173 /* just reconfigure it */
175 if (size
== maxSize())
176 debugs(3, 2, "Cache dir '" << path
<< "' size remains unchanged at " << i
<< " MB");
178 debugs(3, DBG_IMPORTANT
, "Cache dir '" << path
<< "' size changed to " << i
<< " MB");
186 fatal("UFSSwapDir::parseSizeL1L2: invalid level 1 directories value");
191 fatal("UFSSwapDir::parseSizeL1L2: invalid level 2 directories value");
195 Fs::Ufs::UFSSwapDir::reconfigure()
202 Fs::Ufs::UFSSwapDir::parse (int anIndex
, char *aPath
)
205 path
= xstrdup(aPath
);
209 /* Initialise replacement policy stuff */
210 repl
= createRemovalPolicy(Config
.replPolicy
);
216 Fs::Ufs::UFSSwapDir::changeIO(DiskIOModule
*module
)
218 DiskIOStrategy
*anIO
= module
->createStrategy();
220 ioType
= xstrdup(module
->type());
224 /* Change the IO Options */
226 if (currentIOOptions
&& currentIOOptions
->options
.size() > 2)
227 delete currentIOOptions
->options
.pop_back();
229 /* TODO: factor out these 4 lines */
230 ConfigOption
*ioOptions
= IO
->io
->getOptionTree();
233 currentIOOptions
->options
.push_back(ioOptions
);
237 Fs::Ufs::UFSSwapDir::optionIOParse(char const *option
, const char *value
, int isaReconfig
)
239 if (strcmp(option
, "IOEngine") != 0)
243 /* silently ignore this */
249 DiskIOModule
*module
= DiskIOModule::Find(value
);
260 Fs::Ufs::UFSSwapDir::optionIODump(StoreEntry
* e
) const
262 storeAppendPrintf(e
, " IOEngine=%s", ioType
);
266 Fs::Ufs::UFSSwapDir::getOptionTree() const
268 ConfigOption
*parentResult
= SwapDir::getOptionTree();
270 if (currentIOOptions
== NULL
)
271 currentIOOptions
= new ConfigOptionVector();
273 currentIOOptions
->options
.push_back(parentResult
);
275 currentIOOptions
->options
.push_back(new ConfigOptionAdapter
<UFSSwapDir
>(*const_cast<UFSSwapDir
*>(this), &UFSSwapDir::optionIOParse
, &UFSSwapDir::optionIODump
));
277 if (ConfigOption
*ioOptions
= IO
->io
->getOptionTree())
278 currentIOOptions
->options
.push_back(ioOptions
);
280 ConfigOption
* result
= currentIOOptions
;
282 currentIOOptions
= NULL
;
288 Fs::Ufs::UFSSwapDir::init()
290 debugs(47, 3, HERE
<< "Initialising UFS SwapDir engine.");
291 /* Parsing must be finished by now - force to NULL, don't delete */
292 currentIOOptions
= NULL
;
293 static int started_clean_event
= 0;
294 static const char *errmsg
=
295 "\tFailed to verify one of the swap directories, Check cache.log\n"
296 "\tfor details. Run 'squid -z' to create swap directories\n"
297 "\tif needed, or if running Squid for the first time.";
300 if (verifyCacheDirs())
307 if (!started_clean_event
) {
308 eventAdd("UFS storeDirClean", CleanEvent
, NULL
, 15.0, 1);
309 started_clean_event
= 1;
312 (void) storeDirGetBlkSize(path
, &fs
.blksize
);
316 Fs::Ufs::UFSSwapDir::create()
318 debugs(47, 3, "Creating swap space in " << path
);
319 createDirectory(path
, 0);
323 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)
325 /* modulename is only set to disk modules that are built, by configure,
326 * so the Find call should never return NULL here.
328 IO
= new Fs::Ufs::UFSStrategy(DiskIOModule::Find(anIOType
)->createStrategy());
331 Fs::Ufs::UFSSwapDir::~UFSSwapDir()
333 if (swaplog_fd
> -1) {
334 file_close(swaplog_fd
);
349 Fs::Ufs::UFSSwapDir::dumpEntry(StoreEntry
&e
) const
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
) );
357 Fs::Ufs::UFSSwapDir::doubleCheck(StoreEntry
& e
)
362 if (::stat(fullPath(e
.swap_filen
, NULL
), &sb
) < 0) {
363 debugs(47, DBG_CRITICAL
, HERE
<< "WARNING: Missing swap file");
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
);
379 Fs::Ufs::UFSSwapDir::statfs(StoreEntry
& sentry
) const
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
);
398 storeAppendPrintf(&sentry
, "Filesystem Space in use: %d/%d KB (%d%%)\n",
401 Math::intPercent(totl_kb
- free_kb
, totl_kb
));
402 storeAppendPrintf(&sentry
, "Filesystem Inodes in use: %d/%d (%d%%)\n",
405 Math::intPercent(totl_in
- free_in
, totl_in
));
408 storeAppendPrintf(&sentry
, "Flags:");
411 storeAppendPrintf(&sentry
, " SELECTED");
414 storeAppendPrintf(&sentry
, " READ-ONLY");
416 storeAppendPrintf(&sentry
, "\n");
422 Fs::Ufs::UFSSwapDir::maintain()
424 /* We can't delete objects while rebuilding swap */
426 /* XXX FIXME each store should start maintaining as it comes online. */
428 if (StoreController::store_dirs_rebuilding
)
431 StoreEntry
*e
= NULL
;
435 RemovalPurgeWalker
*walker
;
437 double f
= (double) (currentSize() - minSize()) / (maxSize() - minSize());
439 f
= f
< 0.0 ? 0.0 : f
> 1.0 ? 1.0 : f
;
441 int max_scan
= (int) (f
* 400.0 + 100.0);
443 int max_remove
= (int) (f
* 70.0 + 10.0);
446 * This is kinda cheap, but so we need this priority hack?
449 debugs(47, 3, HERE
<< "f=" << f
<< ", max_scan=" << max_scan
<< ", max_remove=" << max_remove
);
451 walker
= repl
->PurgeInit(repl
, max_scan
);
454 if (currentSize() < minSize())
457 if (removed
>= max_remove
)
460 e
= walker
->Next(walker
);
463 break; /* no more objects */
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
);
477 Fs::Ufs::UFSSwapDir::reference(StoreEntry
&e
)
479 debugs(47, 3, HERE
<< "referencing " << &e
<< " " <<
480 e
.swap_dirn
<< "/" << e
.swap_filen
);
482 if (repl
->Referenced
)
483 repl
->Referenced(repl
, &e
, &e
.repl
);
487 Fs::Ufs::UFSSwapDir::dereference(StoreEntry
& e
)
489 debugs(47, 3, HERE
<< "dereferencing " << &e
<< " " <<
490 e
.swap_dirn
<< "/" << e
.swap_filen
);
492 if (repl
->Dereferenced
)
493 repl
->Dereferenced(repl
, &e
, &e
.repl
);
495 return true; // keep e in the global store_table
498 StoreIOState::Pointer
499 Fs::Ufs::UFSSwapDir::createStoreIO(StoreEntry
&e
, StoreIOState::STFNCB
* file_callback
, StoreIOState::STIOCB
* aCallback
, void *callback_data
)
501 return IO
->create (this, &e
, file_callback
, aCallback
, callback_data
);
504 StoreIOState::Pointer
505 Fs::Ufs::UFSSwapDir::openStoreIO(StoreEntry
&e
, StoreIOState::STFNCB
* file_callback
, StoreIOState::STIOCB
* aCallback
, void *callback_data
)
507 return IO
->open (this, &e
, file_callback
, aCallback
, callback_data
);
511 Fs::Ufs::UFSSwapDir::mapBitTest(sfileno filn
)
513 return map
->testBit(filn
);
517 Fs::Ufs::UFSSwapDir::mapBitSet(sfileno filn
)
523 Fs::Ufs::UFSSwapDir::mapBitReset(sfileno filn
)
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.
533 if (map
->testBit(filn
))
538 Fs::Ufs::UFSSwapDir::mapBitAllocate()
541 fn
= map
->allocate(suggest
);
548 Fs::Ufs::UFSSwapDir::swapSubDir(int subdirn
)const
550 LOCAL_ARRAY(char, fullfilename
, MAXPATHLEN
);
551 assert(0 <= subdirn
&& subdirn
< l1
);
552 snprintf(fullfilename
, MAXPATHLEN
, "%s/%02X", path
, subdirn
);
557 Fs::Ufs::UFSSwapDir::createDirectory(const char *aPath
, int should_exist
)
564 if (0 == ::stat(aPath
, &st
)) {
565 if (S_ISDIR(st
.st_mode
)) {
566 debugs(47, (should_exist
? 3 : DBG_IMPORTANT
), aPath
<< " exists");
568 fatalf("Swap directory %s is not a directory.", aPath
);
573 } else if (0 == mkdir(aPath
)) {
576 } else if (0 == mkdir(aPath
, 0755)) {
578 debugs(47, (should_exist
? DBG_IMPORTANT
: 3), aPath
<< " created");
581 fatalf("Failed to make swap directory %s: %s",
589 Fs::Ufs::UFSSwapDir::pathIsDirectory(const char *aPath
)const
594 if (::stat(aPath
, &sb
) < 0) {
595 debugs(47, DBG_CRITICAL
, "ERROR: " << aPath
<< ": " << xstrerror());
599 if (S_ISDIR(sb
.st_mode
) == 0) {
600 debugs(47, DBG_CRITICAL
, "WARNING: " << aPath
<< " is not a directory");
608 Fs::Ufs::UFSSwapDir::verifyCacheDirs()
610 if (!pathIsDirectory(path
))
613 for (int j
= 0; j
< l1
; ++j
) {
614 char const *aPath
= swapSubDir(j
);
616 if (!pathIsDirectory(aPath
))
624 Fs::Ufs::UFSSwapDir::createSwapSubDirs()
626 LOCAL_ARRAY(char, name
, MAXPATHLEN
);
628 for (int i
= 0; i
< l1
; ++i
) {
629 snprintf(name
, MAXPATHLEN
, "%s/%02X", path
, i
);
633 if (createDirectory(name
, 0))
638 debugs(47, DBG_IMPORTANT
, "Making directories in " << name
);
640 for (int k
= 0; k
< l2
; ++k
) {
641 snprintf(name
, MAXPATHLEN
, "%s/%02X/%02X", path
, i
, k
);
642 createDirectory(name
, should_exist
);
648 Fs::Ufs::UFSSwapDir::logFile(char const *ext
) const
650 LOCAL_ARRAY(char, lpath
, MAXPATHLEN
);
651 LOCAL_ARRAY(char, pathtmp
, MAXPATHLEN
);
652 LOCAL_ARRAY(char, digit
, 32);
655 if (Config
.Log
.swap
) {
656 xstrncpy(pathtmp
, path
, MAXPATHLEN
- 64);
659 while ((pathtmp2
= strchr(pathtmp2
, '/')) != NULL
)
662 while (strlen(pathtmp
) && pathtmp
[strlen(pathtmp
) - 1] == '.')
663 pathtmp
[strlen(pathtmp
) - 1] = '\0';
665 for (pathtmp2
= pathtmp
; *pathtmp2
== '.'; ++pathtmp2
);
666 snprintf(lpath
, MAXPATHLEN
- 64, Config
.Log
.swap
, pathtmp2
);
668 if (strncmp(lpath
, Config
.Log
.swap
, MAXPATHLEN
- 64) == 0) {
670 snprintf(digit
, 32, "%02d", index
);
671 strncat(lpath
, digit
, 3);
674 xstrncpy(lpath
, path
, MAXPATHLEN
- 64);
675 strcat(lpath
, "/swap.state");
679 strncat(lpath
, ext
, 16);
685 Fs::Ufs::UFSSwapDir::openLog()
689 swaplog_fd
= file_open(logPath
, O_WRONLY
| O_CREAT
| O_BINARY
);
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.");
696 debugs(50, 3, HERE
<< "Cache Dir #" << index
<< " log opened on FD " << swaplog_fd
);
698 if (0 == NumberOfUFSDirs
)
699 assert(NULL
== UFSDirToGlobalDirMapping
);
703 assert(NumberOfUFSDirs
<= Config
.cacheSwap
.n_configured
);
707 Fs::Ufs::UFSSwapDir::closeLog()
709 if (swaplog_fd
< 0) /* not open */
712 file_close(swaplog_fd
);
714 debugs(47, 3, "Cache Dir #" << index
<< " log closed on FD " << swaplog_fd
);
720 assert(NumberOfUFSDirs
>= 0);
722 if (0 == NumberOfUFSDirs
)
723 safe_free(UFSDirToGlobalDirMapping
);
727 Fs::Ufs::UFSSwapDir::validL1(int anInt
) const
733 Fs::Ufs::UFSSwapDir::validL2(int anInt
) const
739 Fs::Ufs::UFSSwapDir::addDiskRestore(const cache_key
* key
,
741 uint64_t swap_file_sz
,
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
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
;
763 e
->lastref
= lastref
;
764 e
->timestamp
= timestamp
;
765 e
->expires
= expires
;
766 e
->lastmod
= lastmod
;
767 e
->refcount
= refcount
;
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
);
777 e
->hashInsert(key
); /* do it after we clear KEY_PRIVATE */
783 Fs::Ufs::UFSSwapDir::undoAddDiskRestore(StoreEntry
*e
)
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
);
791 cur_size
-= fs
.blksize
* sizeInBlocks(e
->swap_file_sz
);
796 Fs::Ufs::UFSSwapDir::rebuild()
798 ++StoreController::store_dirs_rebuilding
;
799 eventAdd("storeRebuild", Fs::Ufs::RebuildState::RebuildStep
, new Fs::Ufs::RebuildState(this), 0.0, 1);
803 Fs::Ufs::UFSSwapDir::closeTmpSwapLog()
805 char *swaplog_path
= xstrdup(logFile(NULL
));
806 char *new_path
= xstrdup(logFile(".new"));
808 file_close(swaplog_fd
);
810 if (xrename(new_path
, swaplog_path
) < 0) {
811 debugs(50, DBG_IMPORTANT
, HERE
<< "ERROR: " << swaplog_path
<< ": " << xstrerror());
812 fatalf("Failed to rename log file %s to %s.new", swaplog_path
, swaplog_path
);
815 fd
= file_open(swaplog_path
, O_WRONLY
| O_CREAT
| O_BINARY
);
818 debugs(50, DBG_IMPORTANT
, HERE
<< "ERROR: " << swaplog_path
<< ": " << xstrerror());
819 fatalf("Failed to open swap log %s", swaplog_path
);
822 safe_free(swaplog_path
);
825 debugs(47, 3, HERE
<< "Cache Dir #" << index
<< " log opened on FD " << fd
);
829 Fs::Ufs::UFSSwapDir::openTmpSwapLog(int *clean_flag
, int *zero_flag
)
831 char *swaplog_path
= xstrdup(logFile(NULL
));
832 char *clean_path
= xstrdup(logFile(".last-clean"));
833 char *new_path
= xstrdup(logFile(".new"));
837 struct stat clean_sb
;
841 if (::stat(swaplog_path
, &log_sb
) < 0) {
842 debugs(47, DBG_IMPORTANT
, HERE
<< "Cache Dir #" << index
<< ": No log file");
843 safe_free(swaplog_path
);
844 safe_free(clean_path
);
849 *zero_flag
= log_sb
.st_size
== 0 ? 1 : 0;
850 /* close the existing write-only FD */
853 file_close(swaplog_fd
);
855 /* open a write-only FD for the new log */
856 fd
= file_open(new_path
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
);
859 debugs(50, DBG_IMPORTANT
, "ERROR: while opening swap log" << new_path
<< ": " << xstrerror());
860 fatal("UFSSwapDir::openTmpSwapLog: Failed to open swap log.");
866 const StoreSwapLogHeader header
;
868 buf
.init(header
.record_size
, header
.record_size
);
869 buf
.append(reinterpret_cast<const char*>(&header
), sizeof(header
));
870 // Pad to keep in sync with UFSSwapDir::writeCleanStart().
871 memset(buf
.space(), 0, header
.gapSize());
872 buf
.appended(header
.gapSize());
873 file_write(swaplog_fd
, -1, buf
.content(), buf
.contentSize(),
874 NULL
, NULL
, buf
.freeFunc());
877 /* open a read-only stream of the old log */
878 fp
= fopen(swaplog_path
, "rb");
881 debugs(50, DBG_CRITICAL
, "ERROR: while opening " << swaplog_path
<< ": " << xstrerror());
882 fatal("Failed to open swap log for reading");
885 memset(&clean_sb
, '\0', sizeof(struct stat
));
887 if (::stat(clean_path
, &clean_sb
) < 0)
889 else if (clean_sb
.st_mtime
< log_sb
.st_mtime
)
894 safeunlink(clean_path
, 1);
896 safe_free(swaplog_path
);
898 safe_free(clean_path
);
906 * Begin the process to write clean cache state. For AUFS this means
907 * opening some log files and allocating write buffers. Return 0 if
908 * we succeed, and assign the 'func' and 'data' return pointers.
911 Fs::Ufs::UFSSwapDir::writeCleanStart()
913 UFSCleanLog
*state
= new UFSCleanLog(this);
914 StoreSwapLogHeader header
;
921 state
->newLog
= xstrdup(logFile(".clean"));
922 state
->fd
= file_open(state
->newLog
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
);
925 xfree(state
->newLog
);
930 state
->cur
= xstrdup(logFile(NULL
));
931 state
->cln
= xstrdup(logFile(".last-clean"));
932 state
->outbuf
= (char *)xcalloc(CLEAN_BUF_SZ
, 1);
933 state
->outbuf_offset
= 0;
935 memcpy(state
->outbuf
, &header
, sizeof(StoreSwapLogHeader
));
936 // Leave a gap to keep in sync with UFSSwapDir::openTmpSwapLog().
937 memset(state
->outbuf
+ sizeof(StoreSwapLogHeader
), 0, header
.gapSize());
938 state
->outbuf_offset
+= header
.record_size
;
940 state
->walker
= repl
->WalkInit(repl
);
941 ::unlink(state
->cln
);
942 debugs(47, 3, HERE
<< "opened " << state
->newLog
<< ", FD " << state
->fd
);
945 if (::stat(state
->cur
, &sb
) == 0)
946 fchmod(state
->fd
, sb
.st_mode
);
956 Fs::Ufs::UFSSwapDir::writeCleanDone()
958 UFSCleanLog
*state
= (UFSCleanLog
*)cleanLog
;
967 state
->walker
->Done(state
->walker
);
969 if (FD_WRITE_METHOD(state
->fd
, state
->outbuf
, state
->outbuf_offset
) < 0) {
970 debugs(50, DBG_CRITICAL
, HERE
<< state
->newLog
<< ": write: " << xstrerror());
971 debugs(50, DBG_CRITICAL
, HERE
<< "Current swap logfile not replaced.");
972 file_close(state
->fd
);
974 ::unlink(state
->newLog
);
977 safe_free(state
->outbuf
);
979 * You can't rename open files on Microsoft "operating systems"
980 * so we have to close before renaming.
983 /* save the fd value for a later test */
987 if (state
->fd
>= 0) {
988 #if _SQUID_OS2_ || _SQUID_WINDOWS_
989 file_close(state
->fd
);
993 xrename(state
->newLog
, state
->cur
);
996 /* touch a timestamp file if we're not still validating */
997 if (StoreController::store_dirs_rebuilding
)
1002 file_close(file_open(state
->cln
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_BINARY
));
1005 safe_free(state
->cur
);
1007 safe_free(state
->newLog
);
1009 safe_free(state
->cln
);
1012 file_close(state
->fd
);
1022 Fs::Ufs::UFSSwapDir::CleanEvent(void *unused
)
1024 static int swap_index
= 0;
1029 * Assert that there are UFS cache_dirs configured, otherwise
1030 * we should never be called.
1032 assert(NumberOfUFSDirs
);
1034 if (NULL
== UFSDirToGlobalDirMapping
) {
1037 * Initialize the little array that translates UFS cache_dir
1038 * number into the Config.cacheSwap.swapDirs array index.
1040 UFSDirToGlobalDirMapping
= (int *)xcalloc(NumberOfUFSDirs
, sizeof(*UFSDirToGlobalDirMapping
));
1042 for (i
= 0, n
= 0; i
< Config
.cacheSwap
.n_configured
; ++i
) {
1043 /* This is bogus, the controller should just clean each instance once */
1044 sd
= dynamic_cast <SwapDir
*>(INDEXSD(i
));
1046 if (!UFSSwapDir::IsUFSDir(sd
))
1049 UFSSwapDir
*usd
= dynamic_cast<UFSSwapDir
*>(sd
);
1053 UFSDirToGlobalDirMapping
[n
] = i
;
1056 j
+= (usd
->l1
* usd
->l2
);
1059 assert(n
== NumberOfUFSDirs
);
1061 * Start the commonUfsDirClean() swap_index with a random
1062 * value. j equals the total number of UFS level 2
1065 swap_index
= (int) (squid_random() % j
);
1068 /* if the rebuild is finished, start cleaning directories. */
1069 if (0 == StoreController::store_dirs_rebuilding
) {
1070 n
= DirClean(swap_index
);
1074 eventAdd("storeDirClean", CleanEvent
, NULL
,
1075 15.0 * exp(-0.25 * n
), 1);
1079 Fs::Ufs::UFSSwapDir::IsUFSDir(SwapDir
* sd
)
1081 UFSSwapDir
*mySD
= dynamic_cast<UFSSwapDir
*>(sd
);
1082 return (mySD
!= 0) ;
1086 * XXX: this is broken - it assumes all cache dirs use the same
1087 * l1 and l2 scheme. -RBC 20021215. Partial fix is in place -
1088 * if not UFSSwapDir return 0;
1091 Fs::Ufs::UFSSwapDir::FilenoBelongsHere(int fn
, int F0
, int F1
, int F2
)
1096 assert(F0
< Config
.cacheSwap
.n_configured
);
1097 assert (UFSSwapDir::IsUFSDir (dynamic_cast<SwapDir
*>(INDEXSD(F0
))));
1098 UFSSwapDir
*sd
= dynamic_cast<UFSSwapDir
*>(INDEXSD(F0
));
1107 D1
= ((filn
/ L2
) / L2
) % L1
;
1112 D2
= (filn
/ L2
) % L2
;
1121 Fs::Ufs::UFSSwapDir::validFileno(sfileno filn
, int flag
) const
1127 * If flag is set it means out-of-range file number should
1128 * be considered invalid.
1131 if (filn
> map
->capacity())
1138 Fs::Ufs::UFSSwapDir::unlinkFile(sfileno f
)
1140 debugs(79, 3, HERE
<< "unlinking fileno " << std::setfill('0') <<
1141 std::hex
<< std::uppercase
<< std::setw(8) << f
<< " '" <<
1142 fullPath(f
,NULL
) << "'");
1143 /* commonUfsDirMapBitReset(this, f); */
1144 IO
->unlinkFile(fullPath(f
,NULL
));
1148 Fs::Ufs::UFSSwapDir::unlinkdUseful() const
1150 // unlinkd may be useful only in workers
1151 return IamWorkerProcess() && IO
->io
->unlinkdUseful();
1155 Fs::Ufs::UFSSwapDir::unlink(StoreEntry
& e
)
1157 debugs(79, 3, HERE
<< "dirno " << index
<< ", fileno "<<
1158 std::setfill('0') << std::hex
<< std::uppercase
<< std::setw(8) << e
.swap_filen
);
1159 if (e
.swap_status
== SWAPOUT_DONE
) {
1160 cur_size
-= fs
.blksize
* sizeInBlocks(e
.swap_file_sz
);
1163 replacementRemove(&e
);
1164 mapBitReset(e
.swap_filen
);
1165 UFSSwapDir::unlinkFile(e
.swap_filen
);
1169 Fs::Ufs::UFSSwapDir::replacementAdd(StoreEntry
* e
)
1171 debugs(47, 4, HERE
<< "added node " << e
<< " to dir " << index
);
1172 repl
->Add(repl
, e
, &e
->repl
);
1177 Fs::Ufs::UFSSwapDir::replacementRemove(StoreEntry
* e
)
1181 if (e
->swap_dirn
< 0)
1184 SD
= INDEXSD(e
->swap_dirn
);
1186 assert (dynamic_cast<UFSSwapDir
*>(SD
.getRaw()) == this);
1188 debugs(47, 4, HERE
<< "remove node " << e
<< " from dir " << index
);
1190 repl
->Remove(repl
, e
, &e
->repl
);
1194 Fs::Ufs::UFSSwapDir::dump(StoreEntry
& entry
) const
1196 storeAppendPrintf(&entry
, " %" PRIu64
" %d %d", maxSize() >> 20, l1
, l2
);
1197 dumpOptions(&entry
);
1201 Fs::Ufs::UFSSwapDir::fullPath(sfileno filn
, char *fullpath
) const
1203 LOCAL_ARRAY(char, fullfilename
, MAXPATHLEN
);
1208 fullpath
= fullfilename
;
1212 snprintf(fullpath
, MAXPATHLEN
, "%s/%02X/%02X/%08X",
1214 ((filn
/ L2
) / L2
) % L1
,
1222 Fs::Ufs::UFSSwapDir::callback()
1224 return IO
->callback();
1228 Fs::Ufs::UFSSwapDir::sync()
1234 Fs::Ufs::UFSSwapDir::swappedOut(const StoreEntry
&e
)
1236 cur_size
+= fs
.blksize
* sizeInBlocks(e
.swap_file_sz
);
1241 Fs::Ufs::UFSSwapDir::search(String
const url
, HttpRequest
*request
)
1244 fatal ("Cannot search by url yet\n");
1246 return new Fs::Ufs::StoreSearchUFS (this);
1250 Fs::Ufs::UFSSwapDir::logEntry(const StoreEntry
& e
, int op
) const
1252 StoreSwapLogData
*s
= new StoreSwapLogData
;
1254 s
->swap_filen
= e
.swap_filen
;
1255 s
->timestamp
= e
.timestamp
;
1256 s
->lastref
= e
.lastref
;
1257 s
->expires
= e
.expires
;
1258 s
->lastmod
= e
.lastmod
;
1259 s
->swap_file_sz
= e
.swap_file_sz
;
1260 s
->refcount
= e
.refcount
;
1262 memcpy(s
->key
, e
.key
, SQUID_MD5_DIGEST_LENGTH
);
1264 file_write(swaplog_fd
,
1267 sizeof(StoreSwapLogData
),
1274 Fs::Ufs::UFSSwapDir::DirClean(int swap_index
)
1276 DIR *dir_pointer
= NULL
;
1278 LOCAL_ARRAY(char, p1
, MAXPATHLEN
+ 1);
1279 LOCAL_ARRAY(char, p2
, MAXPATHLEN
+ 1);
1283 int fn
; /* same as swapfileno, but with dirn bits set */
1289 N0
= NumberOfUFSDirs
;
1290 D0
= UFSDirToGlobalDirMapping
[swap_index
% N0
];
1291 SD
= dynamic_cast<UFSSwapDir
*>(INDEXSD(D0
));
1294 D1
= (swap_index
/ N0
) % N1
;
1296 D2
= ((swap_index
/ N0
) / N1
) % N2
;
1297 snprintf(p1
, MAXPATHLEN
, "%s/%02X/%02X",
1299 debugs(36, 3, HERE
<< "Cleaning directory " << p1
);
1300 dir_pointer
= opendir(p1
);
1302 if (dir_pointer
== NULL
) {
1303 if (errno
== ENOENT
) {
1304 debugs(36, DBG_CRITICAL
, HERE
<< "WARNING: Creating " << p1
);
1310 if (mkdir(p1
, 0777) == 0)
1316 debugs(50, DBG_CRITICAL
, HERE
<< p1
<< ": " << xstrerror());
1322 while ((de
= readdir(dir_pointer
)) != NULL
&& k
< 20) {
1323 if (sscanf(de
->d_name
, "%X", &swapfileno
) != 1)
1326 fn
= swapfileno
; /* XXX should remove this cruft ! */
1328 if (SD
->validFileno(fn
, 1))
1329 if (SD
->mapBitTest(fn
))
1330 if (UFSSwapDir::FilenoBelongsHere(fn
, D0
, D1
, D2
))
1333 files
[k
] = swapfileno
;
1337 closedir(dir_pointer
);
1342 qsort(files
, k
, sizeof(int), rev_int_sort
);
1347 for (n
= 0; n
< k
; ++n
) {
1348 debugs(36, 3, HERE
<< "Cleaning file "<< std::setfill('0') << std::hex
<< std::uppercase
<< std::setw(8) << files
[n
]);
1349 snprintf(p2
, MAXPATHLEN
+ 1, "%s/%08X", p1
, files
[n
]);
1351 ++statCounter
.swap
.files_cleaned
;
1354 debugs(36, 3, HERE
<< "Cleaned " << k
<< " unused files from " << p1
);