# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: kinkie@squid-cache.org-20120807151508-3wmhi2dp77epzgzp # target_branch: ../trunk/ # testament_sha1: 0fe5e8154cb83b03105c9006ae9eaf04176d8637 # timestamp: 2012-08-07 18:31:35 +0200 # base_revision_id: kinkie@squid-cache.org-20120806174108-\ # v2z18lf2n0sooe64 # # Begin patch === modified file 'src/fs/Makefile.am' --- src/fs/Makefile.am 2011-09-10 16:38:36 +0000 +++ src/fs/Makefile.am 2012-08-05 18:43:04 +0000 @@ -22,11 +22,18 @@ libufs_la_SOURCES = \ ufs/StoreFSufs.h \ ufs/StoreFSufs.cc \ - ufs/store_dir_ufs.cc \ - ufs/store_io_ufs.cc \ - ufs/ufscommon.cci \ - ufs/ufscommon.cc \ - ufs/ufscommon.h + ufs/UFSStoreState.cc \ + ufs/UFSSwapDir.cc \ + ufs/UFSSwapDir.h \ + ufs/UFSStrategy.cc \ + ufs/UFSStrategy.h \ + ufs/UFSStoreState.h \ + ufs/StoreSearchUFS.h \ + ufs/StoreSearchUFS.cc \ + ufs/UFSSwapLogParser.h \ + ufs/UFSSwapLogParser.cc \ + ufs/RebuildState.h \ + ufs/RebuildState.cc librock_la_SOURCES = \ rock/RockDbCell.h \ === modified file 'src/fs/Module.cc' --- src/fs/Module.cc 2012-01-20 18:55:04 +0000 +++ src/fs/Module.cc 2012-08-05 20:47:56 +0000 @@ -2,7 +2,7 @@ #include "Module.h" #if defined(HAVE_FS_UFS) || defined(HAVE_FS_AUFS) || defined(HAVE_FS_DISKD) #include "fs/ufs/StoreFSufs.h" -#include "fs/ufs/ufscommon.h" +#include "fs/ufs/UFSSwapDir.h" #endif #if HAVE_FS_COSS @@ -10,16 +10,16 @@ #endif #if HAVE_FS_UFS -static StoreFSufs *UfsInstance; +static Fs::Ufs::StoreFSufs *UfsInstance; #endif #if HAVE_FS_AUFS -static StoreFSufs *AufsInstance; +static Fs::Ufs::StoreFSufs *AufsInstance; #endif #if HAVE_FS_DISKD -static StoreFSufs *DiskdInstance; +static Fs::Ufs::StoreFSufs *DiskdInstance; #endif #if HAVE_FS_ROCK @@ -42,16 +42,16 @@ { #if HAVE_FS_UFS - UfsInstance = new StoreFSufs("Blocking", "ufs"); + UfsInstance = new Fs::Ufs::StoreFSufs("Blocking", "ufs"); #endif #if HAVE_FS_AUFS - AufsInstance = new StoreFSufs("DiskThreads", "aufs");; + AufsInstance = new Fs::Ufs::StoreFSufs("DiskThreads", "aufs");; #endif #if HAVE_FS_DISKD - DiskdInstance = new StoreFSufs("DiskDaemon", "diskd");; + DiskdInstance = new Fs::Ufs::StoreFSufs("DiskDaemon", "diskd");; #endif #if HAVE_FS_ROCK === modified file 'src/fs/aufs/StoreFSaufs.cc' --- src/fs/aufs/StoreFSaufs.cc 2012-01-20 18:55:04 +0000 +++ src/fs/aufs/StoreFSaufs.cc 2012-08-05 20:47:56 +0000 @@ -45,8 +45,7 @@ #include "fs/ufs/StoreFSufs.h" -/** \todo FIXME: break UFSSwapDir out so we don't need all the guff */ -#include "fs/ufs/ufscommon.h" +#include "fs/ufs/UFSSwapDir.h" /** \defgroup AUFS AUFS Storage Filesystem (UFS Based) @@ -54,4 +53,3 @@ */ /* Unused variable: */ -StoreFSufs *AufsInstance_foo = NULL; === modified file 'src/fs/diskd/StoreFSdiskd.cc' --- src/fs/diskd/StoreFSdiskd.cc 2012-01-20 18:55:04 +0000 +++ src/fs/diskd/StoreFSdiskd.cc 2012-08-05 20:47:56 +0000 @@ -42,9 +42,7 @@ #endif #include "fs/ufs/StoreFSufs.h" - -/** \todo FIXME: break UFSSwapDir out so we don;t need all the extras */ -#include "fs/ufs/ufscommon.h" +#include "fs/ufs/UFSSwapDir.h" /** \defgroup diskd diskd Storage Filesystem (UFS Based) @@ -52,4 +50,4 @@ */ /* Unused variable: */ -StoreFSufs *DiskdInstance_foo = NULL; +Fs::Ufs::StoreFSufs *DiskdInstance_foo = NULL; === modified file 'src/fs/rock/RockSwapDir.cc' --- src/fs/rock/RockSwapDir.cc 2012-06-22 03:49:38 +0000 +++ src/fs/rock/RockSwapDir.cc 2012-08-02 06:19:48 +0000 @@ -18,6 +18,7 @@ #include "MemObject.h" #include "Parsing.h" #include "SquidMath.h" +#include #include const int64_t Rock::SwapDir::HeaderSize = 16*1024; === added file 'src/fs/ufs/RebuildState.cc' --- src/fs/ufs/RebuildState.cc 1970-01-01 00:00:00 +0000 +++ src/fs/ufs/RebuildState.cc 2012-08-05 20:47:56 +0000 @@ -0,0 +1,540 @@ +/* + * + * DEBUG: section 47 Store Directory Routines + * AUTHOR: Robert Collins + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" +#include "RebuildState.h" +#include "SquidTime.h" +#include "StoreSwapLogData.h" +#include "UFSSwapLogParser.h" + +CBDATA_NAMESPACED_CLASS_INIT(Fs::Ufs,RebuildState); + +Fs::Ufs::RebuildState::RebuildState(RefCount aSwapDir) : + sd (aSwapDir), LogParser(NULL), e(NULL), fromLog(true), _done (false) +{ + /* + * If the swap.state file exists in the cache_dir, then + * we'll use commonUfsDirRebuildFromSwapLog(), otherwise we'll + * use commonUfsDirRebuildFromDirectory() to open up each file + * and suck in the meta data. + */ + int clean = 0; + int zeroLengthLog = 0; + FILE *fp = sd->openTmpSwapLog(&clean, &zeroLengthLog); + + if (fp && !zeroLengthLog) + LogParser = Fs::Ufs::UFSSwapLogParser::GetUFSSwapLogParser(fp); + + if (LogParser == NULL ) { + fromLog = false; + + if (fp != NULL) + fclose(fp); + + } else { + fromLog = true; + flags.clean = (unsigned int) clean; + } + + if (!clean) + flags.need_to_validate = 1; + + debugs(47, DBG_IMPORTANT, "Rebuilding storage in " << sd->path << " (" << + (clean ? "clean log" : (LogParser ? "dirty log" : "no log")) << ")"); +} + +Fs::Ufs::RebuildState::~RebuildState() +{ + sd->closeTmpSwapLog(); + + if (LogParser) + delete LogParser; +} + +void +Fs::Ufs::RebuildState::RebuildStep(void *data) +{ + RebuildState *rb = (RebuildState *)data; + rb->rebuildStep(); + + if (!rb->isDone()) + eventAdd("storeRebuild", RebuildStep, rb, 0.01, 1); + else { + -- StoreController::store_dirs_rebuilding; + storeRebuildComplete(&rb->counts); + delete rb; + } +} + +/// load entries from swap.state or files until we run out of entries or time +void +Fs::Ufs::RebuildState::rebuildStep() +{ + currentEntry(NULL); + + // Balance our desire to maximize the number of entries processed at once + // (and, hence, minimize overheads and total rebuild time) with a + // requirement to also process Coordinator events, disk I/Os, etc. + const int maxSpentMsec = 50; // keep small: most RAM I/Os are under 1ms + const timeval loopStart = current_time; + + const int totalEntries = LogParser ? LogParser->SwapLogEntries() : -1; + + while (!isDone()) { + if (fromLog) + rebuildFromSwapLog(); + else + rebuildFromDirectory(); + + // TODO: teach storeRebuildProgress to handle totalEntries <= 0 + if (totalEntries > 0 && (n_read % 4000 == 0)) + storeRebuildProgress(sd->index, totalEntries, n_read); + + if (opt_foreground_rebuild) + continue; // skip "few entries at a time" check below + + getCurrentTime(); + const double elapsedMsec = tvSubMsec(loopStart, current_time); + if (elapsedMsec > maxSpentMsec || elapsedMsec < 0) { + debugs(47, 5, HERE << "pausing after " << n_read << " entries in " << + elapsedMsec << "ms; " << (elapsedMsec/n_read) << "ms per entry"); + break; + } + } +} + +/// process one cache file +void +Fs::Ufs::RebuildState::rebuildFromDirectory() +{ + cache_key key[SQUID_MD5_DIGEST_LENGTH]; + + struct stat sb; + int fd = -1; + assert(this != NULL); + debugs(47, 3, HERE << "DIR #" << sd->index); + + assert(fd == -1); + sfileno filn = 0; + int size; + fd = getNextFile(&filn, &size); + + if (fd == -2) { + debugs(47, DBG_IMPORTANT, "Done scanning " << sd->path << " dir (" << + n_read << " entries)"); + _done = true; + return; + } else if (fd < 0) { + return; + } + + assert(fd > -1); + /* lets get file stats here */ + + ++n_read; + + if (fstat(fd, &sb) < 0) { + debugs(47, DBG_IMPORTANT, HERE << "fstat(FD " << fd << "): " << xstrerror()); + file_close(fd); + --store_open_disk_fd; + fd = -1; + return; + } + + MemBuf buf; + buf.init(SM_PAGE_SIZE, SM_PAGE_SIZE); + if (!storeRebuildLoadEntry(fd, sd->index, buf, counts)) + return; + + StoreEntry tmpe; + const bool loaded = storeRebuildParseEntry(buf, tmpe, key, counts, + (int64_t)sb.st_size); + + file_close(fd); + --store_open_disk_fd; + fd = -1; + + if (!loaded) { + // XXX: shouldn't this be a call to commonUfsUnlink? + sd->unlinkFile(filn); // should we unlink in all failure cases? + return; + } + + if (!storeRebuildKeepEntry(tmpe, key, counts)) + return; + + ++counts.objcount; + // tmpe.dump(5); + currentEntry(sd->addDiskRestore(key, + filn, + tmpe.swap_file_sz, + tmpe.expires, + tmpe.timestamp, + tmpe.lastref, + tmpe.lastmod, + tmpe.refcount, /* refcount */ + tmpe.flags, /* flags */ + (int) flags.clean)); + storeDirSwapLog(currentEntry(), SWAP_LOG_ADD); +} + +StoreEntry * +Fs::Ufs::RebuildState::currentEntry() const +{ + return e; +} + +void +Fs::Ufs::RebuildState::currentEntry(StoreEntry *newValue) +{ + e = newValue; +} + +/// process one swap log entry +void +Fs::Ufs::RebuildState::rebuildFromSwapLog() +{ + StoreSwapLogData swapData; + + if (LogParser->ReadRecord(swapData) != 1) { + debugs(47, DBG_IMPORTANT, "Done reading " << sd->path << " swaplog (" << n_read << " entries)"); + LogParser->Close(); + delete LogParser; + LogParser = NULL; + _done = true; + return; + } + + ++n_read; + + if (!swapData.sane()) { + ++counts.invalid; + return; + } + + /* + * BC: during 2.4 development, we changed the way swap file + * numbers are assigned and stored. The high 16 bits used + * to encode the SD index number. There used to be a call + * to storeDirProperFileno here that re-assigned the index + * bits. Now, for backwards compatibility, we just need + * to mask it off. + */ + swapData.swap_filen &= 0x00FFFFFF; + + debugs(47, 3, HERE << swap_log_op_str[(int) swapData.op] << " " << + storeKeyText(swapData.key) << " "<< std::setfill('0') << + std::hex << std::uppercase << std::setw(8) << + swapData.swap_filen); + + if (swapData.op == SWAP_LOG_ADD) { + (void) 0; + } else if (swapData.op == SWAP_LOG_DEL) { + /* Delete unless we already have a newer copy anywhere in any store */ + /* this needs to become + * 1) unpack url + * 2) make synthetic request with headers ?? or otherwise search + * for a matching object in the store + * TODO FIXME change to new async api + */ + currentEntry (Store::Root().get(swapData.key)); + + if (currentEntry() != NULL && swapData.lastref >= e->lastref) { + undoAdd(); + --counts.objcount; + ++counts.cancelcount; + } + return; + } else { + const double + x = ::log(static_cast(++counts.bad_log_op)) / ::log(10.0); + + if (0.0 == x - (double) (int) x) + debugs(47, DBG_IMPORTANT, "WARNING: " << counts.bad_log_op << " invalid swap log entries found"); + + ++counts.invalid; + + return; + } + + ++counts.scancount; // XXX: should not this be incremented earlier? + + if (!sd->validFileno(swapData.swap_filen, 0)) { + ++counts.invalid; + return; + } + + if (EBIT_TEST(swapData.flags, KEY_PRIVATE)) { + ++counts.badflags; + return; + } + + /* this needs to become + * 1) unpack url + * 2) make synthetic request with headers ?? or otherwise search + * for a matching object in the store + * TODO FIXME change to new async api + */ + currentEntry (Store::Root().get(swapData.key)); + + int used; /* is swapfile already in use? */ + + used = sd->mapBitTest(swapData.swap_filen); + + /* If this URL already exists in the cache, does the swap log + * appear to have a newer entry? Compare 'lastref' from the + * swap log to e->lastref. */ + /* is the log entry newer than current entry? */ + int disk_entry_newer = currentEntry() ? (swapData.lastref > currentEntry()->lastref ? 1 : 0) : 0; + + if (used && !disk_entry_newer) { + /* log entry is old, ignore it */ + ++counts.clashcount; + return; + } else if (used && currentEntry() && currentEntry()->swap_filen == swapData.swap_filen && currentEntry()->swap_dirn == sd->index) { + /* swapfile taken, same URL, newer, update meta */ + + if (currentEntry()->store_status == STORE_OK) { + currentEntry()->lastref = swapData.timestamp; + currentEntry()->timestamp = swapData.timestamp; + currentEntry()->expires = swapData.expires; + currentEntry()->lastmod = swapData.lastmod; + currentEntry()->flags = swapData.flags; + currentEntry()->refcount += swapData.refcount; + sd->dereference(*currentEntry()); + } else { + debug_trap("commonUfsDirRebuildFromSwapLog: bad condition"); + debugs(47, DBG_IMPORTANT, HERE << "bad condition"); + } + return; + } else if (used) { + /* swapfile in use, not by this URL, log entry is newer */ + /* This is sorta bad: the log entry should NOT be newer at this + * point. If the log is dirty, the filesize check should have + * caught this. If the log is clean, there should never be a + * newer entry. */ + debugs(47, DBG_IMPORTANT, "WARNING: newer swaplog entry for dirno " << + sd->index << ", fileno "<< std::setfill('0') << std::hex << + std::uppercase << std::setw(8) << swapData.swap_filen); + + /* I'm tempted to remove the swapfile here just to be safe, + * but there is a bad race condition in the NOVM version if + * the swapfile has recently been opened for writing, but + * not yet opened for reading. Because we can't map + * swapfiles back to StoreEntrys, we don't know the state + * of the entry using that file. */ + /* We'll assume the existing entry is valid, probably because + * were in a slow rebuild and the the swap file number got taken + * and the validation procedure hasn't run. */ + assert(flags.need_to_validate); + ++counts.clashcount; + return; + } else if (currentEntry() && !disk_entry_newer) { + /* key already exists, current entry is newer */ + /* keep old, ignore new */ + ++counts.dupcount; + return; + } else if (currentEntry()) { + /* key already exists, this swapfile not being used */ + /* junk old, load new */ + undoAdd(); + --counts.objcount; + ++counts.dupcount; + } else { + /* URL doesnt exist, swapfile not in use */ + /* load new */ + (void) 0; + } + + ++counts.objcount; + + currentEntry(sd->addDiskRestore(swapData.key, + swapData.swap_filen, + swapData.swap_file_sz, + swapData.expires, + swapData.timestamp, + swapData.lastref, + swapData.lastmod, + swapData.refcount, + swapData.flags, + (int) flags.clean)); + + storeDirSwapLog(currentEntry(), SWAP_LOG_ADD); +} + +/// undo the effects of adding an entry in rebuildFromSwapLog() +void +Fs::Ufs::RebuildState::undoAdd() +{ + StoreEntry *added = currentEntry(); + assert(added); + currentEntry(NULL); + + // TODO: Why bother with these two if we are going to release?! + added->expireNow(); + added->releaseRequest(); + + if (added->swap_filen > -1) { + UFSSwapDir *sde = dynamic_cast(INDEXSD(added->swap_dirn)); + assert(sde); + sde->undoAddDiskRestore(added); + } + + added->release(); +} + +int +Fs::Ufs::RebuildState::getNextFile(sfileno * filn_p, int *size) +{ + int fd = -1; + int dirs_opened = 0; + debugs(47, 3, HERE << "flag=" << flags.init << ", " << + sd->index << ": /"<< std::setfill('0') << std::hex << + std::uppercase << std::setw(2) << curlvl1 << "/" << std::setw(2) << + curlvl2); + + if (done) + return -2; + + while (fd < 0 && done == 0) { + fd = -1; + + if (0 == flags.init) { /* initialize, open first file */ + done = 0; + curlvl1 = 0; + curlvl2 = 0; + in_dir = 0; + flags.init = 1; + assert(Config.cacheSwap.n_configured > 0); + } + + if (0 == in_dir) { /* we need to read in a new directory */ + snprintf(fullpath, MAXPATHLEN, "%s/%02X/%02X", + sd->path, + curlvl1, curlvl2); + + if (dirs_opened) + return -1; + + td = opendir(fullpath); + + ++dirs_opened; + + if (td == NULL) { + debugs(47, DBG_IMPORTANT, HERE << "error in opendir (" << fullpath << "): " << xstrerror()); + } else { + entry = readdir(td); /* skip . and .. */ + entry = readdir(td); + + if (entry == NULL && errno == ENOENT) + debugs(47, DBG_IMPORTANT, HERE << "WARNING: directory does not exist!"); + debugs(47, 3, HERE << "Directory " << fullpath); + } + } + + if (td != NULL && (entry = readdir(td)) != NULL) { + ++in_dir; + + if (sscanf(entry->d_name, "%x", &fn) != 1) { + debugs(47, 3, HERE << "invalid entry " << entry->d_name); + continue; + } + + if (!UFSSwapDir::FilenoBelongsHere(fn, sd->index, curlvl1, curlvl2)) { + debugs(47, 3, HERE << std::setfill('0') << + std::hex << std::uppercase << std::setw(8) << fn << + " does not belong in " << std::dec << sd->index << "/" << + curlvl1 << "/" << curlvl2); + + continue; + } + + if (sd->mapBitTest(fn)) { + debugs(47, 3, HERE << "Locked, continuing with next."); + continue; + } + + snprintf(fullfilename, MAXPATHLEN, "%s/%s", + fullpath, entry->d_name); + debugs(47, 3, HERE << "Opening " << fullfilename); + fd = file_open(fullfilename, O_RDONLY | O_BINARY); + + if (fd < 0) + debugs(47, DBG_IMPORTANT, HERE << "error opening " << fullfilename << ": " << xstrerror()); + else + ++store_open_disk_fd; + + continue; + } + + if (td != NULL) + closedir(td); + + td = NULL; + + in_dir = 0; + + if (sd->validL2(++curlvl2)) + continue; + + curlvl2 = 0; + + if (sd->validL1(++curlvl1)) + continue; + + curlvl1 = 0; + + done = 1; + } + + *filn_p = fn; + return fd; +} + +bool +Fs::Ufs::RebuildState::error() const +{ + return false; +} + +bool +Fs::Ufs::RebuildState::isDone() const +{ + return _done; +} + +StoreEntry * +Fs::Ufs::RebuildState::currentItem() +{ + return currentEntry(); +} === added file 'src/fs/ufs/RebuildState.h' --- src/fs/ufs/RebuildState.h 1970-01-01 00:00:00 +0000 +++ src/fs/ufs/RebuildState.h 2012-08-07 15:15:08 +0000 @@ -0,0 +1,102 @@ +/* + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_FS_UFS_REBUILDSTATE_H +#define SQUID_FS_UFS_REBUILDSTATE_H + +#include "RefCount.h" +#include "UFSSwapDir.h" +#include "structs.h" +#include "UFSSwapLogParser.h" + +class StoreEntry; + +namespace Fs +{ +namespace Ufs +{ + +/// \ingroup UFS +class RebuildState : public RefCountable +{ +public: + static EVH RebuildStep; + + RebuildState(RefCount sd); + ~RebuildState(); + + virtual bool error() const; + virtual bool isDone() const; + virtual StoreEntry *currentItem(); + + RefCount sd; + int n_read; + /* FILE *log;*/ + Fs::Ufs::UFSSwapLogParser *LogParser; + int curlvl1; + int curlvl2; + + struct { + unsigned int need_to_validate:1; + unsigned int clean:1; + unsigned int init:1; + } flags; + int in_dir; + int done; + int fn; + + dirent_t *entry; + DIR *td; + char fullpath[MAXPATHLEN]; + char fullfilename[MAXPATHLEN]; + + struct _store_rebuild_data counts; + +private: + CBDATA_CLASS2(RebuildState); + void rebuildFromDirectory(); + void rebuildFromSwapLog(); + void rebuildStep(); + void undoAdd(); + int getNextFile(sfileno *, int *size); + StoreEntry *currentEntry() const; + void currentEntry(StoreEntry *); + StoreEntry *e; + bool fromLog; + bool _done; + /// \bug (callback) should be hidden behind a proper human readable name + void (callback)(void *cbdata); + void *cbdata; +}; + + +} /* namespace Ufs */ +} /* namespace Fs */ + +#endif /* SQUID_FS_UFS_REBUILDSTATE_H */ === modified file 'src/fs/ufs/StoreFSufs.cc' --- src/fs/ufs/StoreFSufs.cc 2012-01-20 18:55:04 +0000 +++ src/fs/ufs/StoreFSufs.cc 2012-08-05 20:47:56 +0000 @@ -1,6 +1,4 @@ /* - * $Id$ - * * DEBUG: section 47 Store Directory Routines * AUTHOR: Robert Collins * @@ -41,13 +39,11 @@ #endif #include "fs/ufs/StoreFSufs.h" +#include "fs/ufs/UFSSwapDir.h" #if 0 #include "DiskIO/DiskIOModule.h" #endif -/** \todo FIXME: break UFSSwapDir out so we don't build all the extras */ -#include "fs/ufs/ufscommon.h" - /* Unused variable: */ -StoreFSufs *UfsInstance_foo = NULL; +Fs::Ufs::StoreFSufs *UfsInstance_foo = NULL; === modified file 'src/fs/ufs/StoreFSufs.h' --- src/fs/ufs/StoreFSufs.h 2009-01-21 03:47:47 +0000 +++ src/fs/ufs/StoreFSufs.h 2012-08-05 18:16:05 +0000 @@ -37,10 +37,14 @@ \ingroup FileSystems */ +#include "StoreFileSystem.h" + class DiskIOModule; -#include "StoreFileSystem.h" - +namespace Fs +{ +namespace Ufs +{ /** \ingroup UFS, FileSystems * @@ -52,7 +56,6 @@ template class StoreFSufs : public StoreFileSystem { - public: static StoreFileSystem &GetInstance(); StoreFSufs(char const *DefaultModuleType, char const *label); @@ -108,4 +111,7 @@ initialised = true; } +} /* namespace Ufs */ +} /* namespace Fs */ + #endif /* SQUID_STOREFSUFS_H */ === added file 'src/fs/ufs/StoreSearchUFS.cc' --- src/fs/ufs/StoreSearchUFS.cc 1970-01-01 00:00:00 +0000 +++ src/fs/ufs/StoreSearchUFS.cc 2012-08-05 18:28:30 +0000 @@ -0,0 +1,90 @@ +/* + * DEBUG: section 47 Store Directory Routines + * AUTHOR: Robert Collins + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" +#include "cbdata.h" +#include "StoreSearchUFS.h" +#include "UFSSwapDir.h" + +CBDATA_NAMESPACED_CLASS_INIT(Fs::Ufs,StoreSearchUFS); + +Fs::Ufs::StoreSearchUFS::StoreSearchUFS(RefCount aSwapDir) : + sd(aSwapDir), walker (sd->repl->WalkInit(sd->repl)), + current (NULL), _done (false) +{} + +Fs::Ufs::StoreSearchUFS::~StoreSearchUFS() +{ + walker->Done(walker); + walker = NULL; +} + +void +Fs::Ufs::StoreSearchUFS::next(void (aCallback)(void *cbdata), void *aCallbackArgs) +{ + next(); + aCallback(aCallbackArgs); +} + +bool +Fs::Ufs::StoreSearchUFS::next() +{ + /* the walker API doesn't make sense. the store entries referred to are already readwrite + * from their hash table entries + */ + + if (walker) + current = const_cast(walker->Next(walker)); + + if (current == NULL) + _done = true; + + return current != NULL; +} + +bool +Fs::Ufs::StoreSearchUFS::error() const +{ + return false; +} + +bool +Fs::Ufs::StoreSearchUFS::isDone() const +{ + return _done; +} + +StoreEntry * +Fs::Ufs::StoreSearchUFS::currentItem() +{ + return current; +} === added file 'src/fs/ufs/StoreSearchUFS.h' --- src/fs/ufs/StoreSearchUFS.h 1970-01-01 00:00:00 +0000 +++ src/fs/ufs/StoreSearchUFS.h 2012-08-07 15:15:08 +0000 @@ -0,0 +1,83 @@ +/* + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_FS_UFS_STORESEARCHUFS_H +#define SQUID_FS_UFS_STORESEARCHUFS_H + +#include "StoreSearch.h" +#include "UFSSwapDir.h" + +namespace Fs +{ +namespace Ufs +{ + +/// \ingroup UFS +class StoreSearchUFS : public StoreSearch +{ +public: + StoreSearchUFS(RefCount sd); + virtual ~StoreSearchUFS(); + + /** \todo Iterator API - garh, wrong place */ + /** + * callback the client when a new StoreEntry is available + * or an error occurs + */ + virtual void next(void (callback)(void *cbdata), void *cbdata); + + /** + \retval true if a new StoreEntry is immediately available + \retval false if a new StoreEntry is NOT immediately available + */ + virtual bool next(); + + virtual bool error() const; + virtual bool isDone() const; + virtual StoreEntry *currentItem(); + + RefCount sd; + RemovalPolicyWalker *walker; + +private: + CBDATA_CLASS2(StoreSearchUFS); + /// \bug (callback) should be hidden behind a proper human readable name + void (callback)(void *cbdata); + void *cbdata; + StoreEntry * current; + bool _done; + + StoreSearchUFS(StoreSearchUFS const &); //disabled + StoreSearchUFS& operator=(StoreSearchUFS const &); //disabled + StoreSearchUFS(); //disabled +}; + +} //namespace Ufs +} //namespace Fs +#endif /* SQUID_FS_UFS_STORESEARCHUFS_H */ === renamed file 'src/fs/ufs/store_io_ufs.cc' => 'src/fs/ufs/UFSStoreState.cc' --- src/fs/ufs/store_io_ufs.cc 2012-08-06 17:41:08 +0000 +++ src/fs/ufs/UFSStoreState.cc 2012-08-06 17:56:03 +0000 @@ -35,7 +35,6 @@ #include "squid-old.h" #include "Store.h" -#include "ufscommon.h" #include "Generic.h" #include "DiskIO/DiskFile.h" #include "DiskIO/DiskIOStrategy.h" @@ -43,63 +42,27 @@ #include "DiskIO/WriteRequest.h" #include "SwapDir.h" - -bool -UFSStrategy::shedLoad() -{ - return io->shedLoad(); -} - -int -UFSStrategy::load() -{ - return io->load(); -} - -UFSStrategy::UFSStrategy (DiskIOStrategy *anIO) : io(anIO) -{} - -UFSStrategy::~UFSStrategy () -{ - delete io; -} - -StoreIOState::Pointer -UFSStrategy::createState(SwapDir *SD, StoreEntry *e, StoreIOState::STIOCB * aCallback, void *callback_data) const -{ - return new UFSStoreState (SD, e, aCallback, callback_data); -} - -DiskFile::Pointer -UFSStrategy::newFile (char const *path) -{ - return io->newFile(path); -} - - -void -UFSStrategy::unlinkFile(char const *path) -{ - io->unlinkFile(path); -} - -CBDATA_CLASS_INIT(UFSStoreState); +#include "UFSStrategy.h" +#include "UFSStoreState.h" + + +CBDATA_NAMESPACED_CLASS_INIT(Fs::Ufs,UFSStoreState); void * -UFSStoreState::operator new (size_t) +Fs::Ufs::UFSStoreState::operator new (size_t) { CBDATA_INIT_TYPE(UFSStoreState); return cbdataAlloc(UFSStoreState); } void -UFSStoreState::operator delete (void *address) +Fs::Ufs::UFSStoreState::operator delete (void *address) { cbdataFree(address); } void -UFSStoreState::ioCompletedNotification() +Fs::Ufs::UFSStoreState::ioCompletedNotification() { if (opening) { opening = false; @@ -137,7 +100,7 @@ } void -UFSStoreState::openDone() +Fs::Ufs::UFSStoreState::openDone() { if (closing) debugs(0, DBG_CRITICAL, HERE << "already closing in openDone()!?"); @@ -162,7 +125,7 @@ } void -UFSStoreState::closeCompleted() +Fs::Ufs::UFSStoreState::closeCompleted() { assert (closing); debugs(79, 3, "UFSStoreState::closeCompleted: dirno " << swap_dirn << @@ -190,7 +153,7 @@ * when it is safe to actually signal the lower layer for closing. */ void -UFSStoreState::close(int) +Fs::Ufs::UFSStoreState::close(int) { debugs(79, 3, "UFSStoreState::close: dirno " << swap_dirn << ", fileno "<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << swap_filen); @@ -198,7 +161,7 @@ } void -UFSStoreState::read_(char *buf, size_t size, off_t aOffset, STRCB * aCallback, void *aCallbackData) +Fs::Ufs::UFSStoreState::read_(char *buf, size_t size, off_t aOffset, STRCB * aCallback, void *aCallbackData) { assert(read.callback == NULL); assert(read.callback_data == NULL); @@ -232,7 +195,7 @@ * code simpler and always go through the pending_writes queue. */ void -UFSStoreState::write(char const *buf, size_t size, off_t aOffset, FREE * free_func) +Fs::Ufs::UFSStoreState::write(char const *buf, size_t size, off_t aOffset, FREE * free_func) { debugs(79, 3, "UFSStoreState::write: dirn " << swap_dirn << ", fileno "<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << swap_filen); @@ -256,7 +219,7 @@ * called by drainWriteQueue(). */ void -UFSStoreState::doWrite() +Fs::Ufs::UFSStoreState::doWrite() { debugs(79, 3, HERE << this << " UFSStoreState::doWrite"); @@ -300,7 +263,7 @@ } void -UFSStoreState::readCompleted(const char *buf, int len, int errflag, RefCount result) +Fs::Ufs::UFSStoreState::readCompleted(const char *buf, int len, int errflag, RefCount result) { assert (result.getRaw()); reading = false; @@ -343,7 +306,7 @@ } void -UFSStoreState::writeCompleted(int errflag, size_t len, RefCount writeRequest) +Fs::Ufs::UFSStoreState::writeCompleted(int errflag, size_t len, RefCount writeRequest) { debugs(79, 3, HERE << "dirno " << swap_dirn << ", fileno " << std::setfill('0') << std::hex << std::uppercase << std::setw(8) << swap_filen << @@ -369,7 +332,7 @@ } void -UFSStoreState::doCloseCallback(int errflag) +Fs::Ufs::UFSStoreState::doCloseCallback(int errflag) { debugs(79, 3, "storeUfsIOCallback: errflag=" << errflag); /* @@ -398,7 +361,7 @@ /* ============= THE REAL UFS CODE ================ */ -UFSStoreState::UFSStoreState(SwapDir * SD, StoreEntry * anEntry, STIOCB * callback_, void *callback_data_) : opening (false), creating (false), closing (false), reading(false), writing(false), pending_reads(NULL), pending_writes (NULL) +Fs::Ufs::UFSStoreState::UFSStoreState(SwapDir * SD, StoreEntry * anEntry, STIOCB * callback_, void *callback_data_) : opening (false), creating (false), closing (false), reading(false), writing(false), pending_reads(NULL), pending_writes (NULL) { swap_filen = anEntry->swap_filen; swap_dirn = SD->index; @@ -410,14 +373,14 @@ flags.try_closing = false; } -UFSStoreState::~UFSStoreState() +Fs::Ufs::UFSStoreState::~UFSStoreState() { assert(pending_reads == NULL); assert(pending_writes == NULL); } void -UFSStoreState::freePending() +Fs::Ufs::UFSStoreState::freePending() { _queued_read *qr; @@ -440,7 +403,7 @@ } bool -UFSStoreState::kickReadQueue() +Fs::Ufs::UFSStoreState::kickReadQueue() { _queued_read *q = (_queued_read *)linklistShift(&pending_reads); @@ -465,7 +428,7 @@ } void -UFSStoreState::queueRead(char *buf, size_t size, off_t aOffset, STRCB *callback_, void *callback_data_) +Fs::Ufs::UFSStoreState::queueRead(char *buf, size_t size, off_t aOffset, STRCB *callback_, void *callback_data_) { debugs(79, 3, "UFSStoreState::queueRead: queueing read"); assert(opening); @@ -484,7 +447,7 @@ * drainWriteQueue() is a loop around doWrite(). */ void -UFSStoreState::drainWriteQueue() +Fs::Ufs::UFSStoreState::drainWriteQueue() { /* * DPW 2007-04-12 @@ -521,7 +484,7 @@ * or will remember to do the close for us. */ void -UFSStoreState::tryClosing() +Fs::Ufs::UFSStoreState::tryClosing() { debugs(79,3,HERE << this << " tryClosing()" << " closing = " << closing << @@ -541,7 +504,7 @@ } void -UFSStoreState::queueWrite(char const *buf, size_t size, off_t aOffset, FREE * free_func) +Fs::Ufs::UFSStoreState::queueWrite(char const *buf, size_t size, off_t aOffset, FREE * free_func) { debugs(79, 3, HERE << this << " UFSStoreState::queueWrite: queueing write of size " << size); @@ -554,109 +517,3 @@ linklistPush(&pending_writes, q); } -StoreIOState::Pointer -UFSStrategy::open(SwapDir * SD, StoreEntry * e, StoreIOState::STFNCB * file_callback, - StoreIOState::STIOCB * aCallback, void *callback_data) -{ - assert (((UFSSwapDir *)SD)->IO == this); - debugs(79, 3, "UFSStrategy::open: fileno "<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << e->swap_filen); - - /* to consider: make createstate a private UFSStrategy call */ - StoreIOState::Pointer sio = createState (SD, e, aCallback, callback_data); - - sio->mode |= O_RDONLY; - - UFSStoreState *state = dynamic_cast (sio.getRaw()); - - assert (state); - - char *path = ((UFSSwapDir *)SD)->fullPath(e->swap_filen, NULL); - - DiskFile::Pointer myFile = newFile (path); - - if (myFile.getRaw() == NULL) - return NULL; - - state->theFile = myFile; - - state->opening = true; - - myFile->open (sio->mode, 0644, state); - - if (myFile->error()) - return NULL; - - return sio; -} - -StoreIOState::Pointer -UFSStrategy::create(SwapDir * SD, StoreEntry * e, StoreIOState::STFNCB * file_callback, - StoreIOState::STIOCB * aCallback, void *callback_data) -{ - assert (((UFSSwapDir *)SD)->IO == this); - /* Allocate a number */ - sfileno filn = ((UFSSwapDir *)SD)->mapBitAllocate(); - debugs(79, 3, "UFSStrategy::create: fileno "<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << filn); - - /* Shouldn't we handle a 'bitmap full' error here? */ - - StoreIOState::Pointer sio = createState (SD, e, aCallback, callback_data); - - sio->mode |= O_WRONLY | O_CREAT | O_TRUNC; - - sio->swap_filen = filn; - - UFSStoreState *state = dynamic_cast (sio.getRaw()); - - assert (state); - - char *path = ((UFSSwapDir *)SD)->fullPath(filn, NULL); - - DiskFile::Pointer myFile = newFile (path); - - if (myFile.getRaw() == NULL) { - ((UFSSwapDir *)SD)->mapBitReset (filn); - return NULL; - } - - state->theFile = myFile; - - state->creating = true; - - myFile->create (state->mode, 0644, state); - - if (myFile->error()) { - ((UFSSwapDir *)SD)->mapBitReset (filn); - return NULL; - } - - /* now insert into the replacement policy */ - ((UFSSwapDir *)SD)->replacementAdd(e); - - return sio; -} - -int -UFSStrategy::callback() -{ - return io->callback(); -} - -void -UFSStrategy::init() -{ - io->init(); -} - -void -UFSStrategy::sync() -{ - io->sync(); -} - -void -UFSStrategy::statfs(StoreEntry & sentry)const -{ - io->statfs(sentry); -} - === added file 'src/fs/ufs/UFSStoreState.h' --- src/fs/ufs/UFSStoreState.h 1970-01-01 00:00:00 +0000 +++ src/fs/ufs/UFSStoreState.h 2012-08-07 15:15:08 +0000 @@ -0,0 +1,129 @@ +/* + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_FS_UFS_UFSSTORESTATE_H +#define SQUID_FS_UFS_UFSSTORESTATE_H + +#include "DiskIO/IORequestor.h" +#include "StoreIOState.h" + +namespace Fs +{ +namespace Ufs +{ +/// \ingroup UFS +class UFSStoreState : public StoreIOState, public IORequestor +{ +public: + void * operator new (size_t); + void operator delete (void *); + UFSStoreState(SwapDir * SD, StoreEntry * anEntry, STIOCB * callback_, void *callback_data_); + ~UFSStoreState(); + virtual void close(int how); + virtual void closeCompleted(); + // protected: + virtual void ioCompletedNotification(); + virtual void readCompleted(const char *buf, int len, int errflag, RefCount); + virtual void writeCompleted(int errflag, size_t len, RefCount); + RefCount theFile; + bool opening; + bool creating; + bool closing; + bool reading; + bool writing; + void read_(char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data); + void write(char const *buf, size_t size, off_t offset, FREE * free_func); + +protected: + virtual void doCloseCallback (int errflag); + + class _queued_read + { + public: + MEMPROXY_CLASS(UFSStoreState::_queued_read); + char *buf; + size_t size; + off_t offset; + STRCB *callback; + void *callback_data; + + }; + + class _queued_write + { + public: + MEMPROXY_CLASS(UFSStoreState::_queued_write); + char const *buf; + size_t size; + off_t offset; + FREE *free_func; + + }; + + /** \todo These should be in the IO strategy */ + + struct { + /** + * DPW 2006-05-24 + * the write_draining flag is used to avoid recursion inside + * the UFSStoreState::drainWriteQueue() method. + */ + bool write_draining; + /** + * DPW 2006-05-24 + * The try_closing flag is set by UFSStoreState::tryClosing() + * when UFSStoreState wants to close the file, but cannot + * because of pending I/Os. If set, UFSStoreState will + * try to close again in the I/O callbacks. + */ + bool try_closing; + } flags; + link_list *pending_reads; + link_list *pending_writes; + void queueRead(char *, size_t, off_t, STRCB *, void *); + void queueWrite(char const *, size_t, off_t, FREE *); + bool kickReadQueue(); + void drainWriteQueue(); + void tryClosing(); + char *read_buf; + +private: + CBDATA_CLASS(UFSStoreState); + void openDone(); + void freePending(); + void doWrite(); +}; + +MEMPROXY_CLASS_INLINE(UFSStoreState::_queued_read); +MEMPROXY_CLASS_INLINE(UFSStoreState::_queued_write); + +} //namespace Ufs +} //namespace Fs + +#endif /* SQUID_FS_UFS_UFSSTORESTATE_H */ === added file 'src/fs/ufs/UFSStrategy.cc' --- src/fs/ufs/UFSStrategy.cc 1970-01-01 00:00:00 +0000 +++ src/fs/ufs/UFSStrategy.cc 2012-08-05 20:47:56 +0000 @@ -0,0 +1,183 @@ +/* + * DEBUG: section 47 Store Directory Routines + * AUTHOR: Robert Collins + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + */ + +#include "squid.h" + +#include "DiskIO/DiskIOStrategy.h" +#include "UFSStrategy.h" +#include "UFSStoreState.h" +#include "UFSSwapDir.h" + +bool +Fs::Ufs::UFSStrategy::shedLoad() +{ + return io->shedLoad(); +} + +int +Fs::Ufs::UFSStrategy::load() +{ + return io->load(); +} + +Fs::Ufs::UFSStrategy::UFSStrategy (DiskIOStrategy *anIO) : io(anIO) +{} + +Fs::Ufs::UFSStrategy::~UFSStrategy () +{ + delete io; +} + +StoreIOState::Pointer +Fs::Ufs::UFSStrategy::createState(SwapDir *SD, StoreEntry *e, StoreIOState::STIOCB * aCallback, void *callback_data) const +{ + return new Fs::Ufs::UFSStoreState (SD, e, aCallback, callback_data); +} + +DiskFile::Pointer +Fs::Ufs::UFSStrategy::newFile (char const *path) +{ + return io->newFile(path); +} + +void +Fs::Ufs::UFSStrategy::unlinkFile(char const *path) +{ + io->unlinkFile(path); +} + +StoreIOState::Pointer +Fs::Ufs::UFSStrategy::open(SwapDir * SD, StoreEntry * e, StoreIOState::STFNCB * file_callback, + StoreIOState::STIOCB * aCallback, void *callback_data) +{ + assert (((UFSSwapDir *)SD)->IO == this); + debugs(79, 3, HERE << "fileno "<< std::setfill('0') << std::hex + << std::uppercase << std::setw(8) << e->swap_filen); + + /* to consider: make createstate a private UFSStrategy call */ + StoreIOState::Pointer sio = createState (SD, e, aCallback, callback_data); + + sio->mode |= O_RDONLY; + + Fs::Ufs::UFSStoreState *state = dynamic_cast (sio.getRaw()); + + assert (state); + + char *path = ((UFSSwapDir *)SD)->fullPath(e->swap_filen, NULL); + + DiskFile::Pointer myFile = newFile (path); + + if (myFile.getRaw() == NULL) + return NULL; + + state->theFile = myFile; + + state->opening = true; + + myFile->open (sio->mode, 0644, state); + + if (myFile->error()) + return NULL; + + return sio; +} + +StoreIOState::Pointer +Fs::Ufs::UFSStrategy::create(SwapDir * SD, StoreEntry * e, StoreIOState::STFNCB * file_callback, + StoreIOState::STIOCB * aCallback, void *callback_data) +{ + assert (((UFSSwapDir *)SD)->IO == this); + /* Allocate a number */ + sfileno filn = ((UFSSwapDir *)SD)->mapBitAllocate(); + debugs(79, 3, HERE << "fileno "<< std::setfill('0') << + std::hex << std::uppercase << std::setw(8) << filn); + + /* Shouldn't we handle a 'bitmap full' error here? */ + + StoreIOState::Pointer sio = createState (SD, e, aCallback, callback_data); + + sio->mode |= O_WRONLY | O_CREAT | O_TRUNC; + + sio->swap_filen = filn; + + Fs::Ufs::UFSStoreState *state = dynamic_cast (sio.getRaw()); + + assert (state); + + char *path = ((UFSSwapDir *)SD)->fullPath(filn, NULL); + + DiskFile::Pointer myFile = newFile (path); + + if (myFile.getRaw() == NULL) { + ((UFSSwapDir *)SD)->mapBitReset (filn); + return NULL; + } + + state->theFile = myFile; + + state->creating = true; + + myFile->create (state->mode, 0644, state); + + if (myFile->error()) { + ((UFSSwapDir *)SD)->mapBitReset (filn); + return NULL; + } + + /* now insert into the replacement policy */ + ((UFSSwapDir *)SD)->replacementAdd(e); + + return sio; +} + +int +Fs::Ufs::UFSStrategy::callback() +{ + return io->callback(); +} + +void +Fs::Ufs::UFSStrategy::init() +{ + io->init(); +} + +void +Fs::Ufs::UFSStrategy::sync() +{ + io->sync(); +} + +void +Fs::Ufs::UFSStrategy::statfs(StoreEntry & sentry)const +{ + io->statfs(sentry); +} === added file 'src/fs/ufs/UFSStrategy.h' --- src/fs/ufs/UFSStrategy.h 1970-01-01 00:00:00 +0000 +++ src/fs/ufs/UFSStrategy.h 2012-08-05 20:47:56 +0000 @@ -0,0 +1,90 @@ +/* + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_FS_UFS_UFSSTRATEGY_H +#define SQUID_FS_UFS_UFSSTRATEGY_H + +#include "DiskIO/DiskFile.h" +#include "StoreIOState.h" + +class Swapdir; +class StoreEntry; +class DiskIOStrategy; + +namespace Fs +{ +namespace Ufs +{ +/// \ingroup UFS +class UFSStrategy +{ +public: + UFSStrategy (DiskIOStrategy *); + virtual ~UFSStrategy (); + virtual bool shedLoad(); + + virtual int load(); + + StoreIOState::Pointer createState(SwapDir *SD, StoreEntry *e, StoreIOState::STIOCB * callback, void *callback_data) const; + /* UFS specific */ + virtual RefCount newFile (char const *path); + StoreIOState::Pointer open(SwapDir *, StoreEntry *, StoreIOState::STFNCB *, + StoreIOState::STIOCB *, void *); + StoreIOState::Pointer create(SwapDir *, StoreEntry *, StoreIOState::STFNCB *, + StoreIOState::STIOCB *, void *); + + virtual void unlinkFile (char const *); + virtual void sync(); + + virtual int callback(); + + /** Init per-instance logic */ + virtual void init(); + + /** cachemgr output on the IO instance stats */ + virtual void statfs(StoreEntry & sentry)const; + + /** The io strategy in use */ + DiskIOStrategy *io; + +protected: + + friend class UFSSwapDir; + +private: + UFSStrategy(); //disabled + UFSStrategy(UFSStrategy const &); //disabled + UFSStrategy &operator=(UFSStrategy const &); //disabled + +}; + +} //namespace Ufs +} //namespace Fs + +#endif /* SQUID_FS_UFS_UFSSTRATEGY_H */ === added file 'src/fs/ufs/UFSSwapDir.cc' --- src/fs/ufs/UFSSwapDir.cc 1970-01-01 00:00:00 +0000 +++ src/fs/ufs/UFSSwapDir.cc 2012-08-05 20:47:56 +0000 @@ -0,0 +1,1350 @@ +/* + * DEBUG: section 47 Store Directory Routines + * AUTHOR: Robert Collins + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + */ + +#include "squid-old.h" + +#define CLEAN_BUF_SZ 16384 + +#include "ConfigOption.h" +#include "DiskIO/DiskIOModule.h" +#include "FileMap.h" +#include "fde.h" +#include "Parsing.h" +#include "protos.h" +#include "RebuildState.h" +#include "SquidMath.h" +#include "DiskIO/DiskIOStrategy.h" +#include "StoreSearchUFS.h" +#include "StoreSwapLogData.h" +#include "SquidTime.h" +#include "StatCounters.h" +#include "UFSSwapDir.h" + + +int Fs::Ufs::UFSSwapDir::NumberOfUFSDirs = 0; +int *Fs::Ufs::UFSSwapDir::UFSDirToGlobalDirMapping = NULL; + +class UFSCleanLog : public SwapDir::CleanLog +{ + +public: + UFSCleanLog(SwapDir *); + /** Get the next entry that is a candidate for clean log writing + */ + virtual const StoreEntry *nextEntry(); + /** "write" an entry to the clean log file. + */ + virtual void write(StoreEntry const &); + char *cur; + char *newLog; + char *cln; + char *outbuf; + off_t outbuf_offset; + int fd; + RemovalPolicyWalker *walker; + SwapDir *sd; +}; + +UFSCleanLog::UFSCleanLog(SwapDir *aSwapDir) : + cur(NULL), newLog(NULL), cln(NULL), outbuf(NULL), + outbuf_offset(0), fd(-1),walker(NULL), sd(aSwapDir) +{} + +const StoreEntry * +UFSCleanLog::nextEntry() +{ + const StoreEntry *entry = NULL; + + if (walker) + entry = walker->Next(walker); + + return entry; +} + +void +UFSCleanLog::write(StoreEntry const &e) +{ + StoreSwapLogData s; + static size_t ss = sizeof(StoreSwapLogData); + s.op = (char) SWAP_LOG_ADD; + s.swap_filen = e.swap_filen; + s.timestamp = e.timestamp; + s.lastref = e.lastref; + s.expires = e.expires; + s.lastmod = e.lastmod; + s.swap_file_sz = e.swap_file_sz; + s.refcount = e.refcount; + s.flags = e.flags; + memcpy(&s.key, e.key, SQUID_MD5_DIGEST_LENGTH); + s.finalize(); + memcpy(outbuf + outbuf_offset, &s, ss); + outbuf_offset += ss; + /* buffered write */ + + if (outbuf_offset + ss >= CLEAN_BUF_SZ) { + if (FD_WRITE_METHOD(fd, outbuf, outbuf_offset) < 0) { + /* XXX This error handling should probably move up to the caller */ + debugs(50, DBG_CRITICAL, HERE << newLog << ": write: " << xstrerror()); + debugs(50, DBG_CRITICAL, HERE << "Current swap logfile not replaced."); + file_close(fd); + fd = -1; + unlink(newLog); + sd->cleanLog = NULL; + delete this; + return; + } + + outbuf_offset = 0; + } +} + +bool +Fs::Ufs::UFSSwapDir::canStore(const StoreEntry &e, int64_t diskSpaceNeeded, int &load) const +{ + if (!SwapDir::canStore(e, diskSpaceNeeded, load)) + return false; + + if (IO->shedLoad()) + return false; + + load = IO->load(); + return true; +} + +static void +FreeObject(void *address) +{ + StoreSwapLogData *anObject = static_cast (address); + delete anObject; +} + +static QS rev_int_sort; +static int +rev_int_sort(const void *A, const void *B) +{ + const int *i1 = (const int *)A; + const int *i2 = (const int *)B; + return *i2 - *i1; +} + +void +Fs::Ufs::UFSSwapDir::parseSizeL1L2() +{ + int i = GetInteger(); + if (i <= 0) + fatal("UFSSwapDir::parseSizeL1L2: invalid size value"); + + const uint64_t size = static_cast(i) << 20; // MBytes to Bytes + + /* just reconfigure it */ + if (reconfiguring) { + if (size == maxSize()) + debugs(3, 2, "Cache dir '" << path << "' size remains unchanged at " << i << " MB"); + else + debugs(3, DBG_IMPORTANT, "Cache dir '" << path << "' size changed to " << i << " MB"); + } + + max_size = size; + + l1 = GetInteger(); + + if (l1 <= 0) + fatal("UFSSwapDir::parseSizeL1L2: invalid level 1 directories value"); + + l2 = GetInteger(); + + if (l2 <= 0) + fatal("UFSSwapDir::parseSizeL1L2: invalid level 2 directories value"); +} + +void +Fs::Ufs::UFSSwapDir::reconfigure() +{ + parseSizeL1L2(); + parseOptions(1); +} + +void +Fs::Ufs::UFSSwapDir::parse (int anIndex, char *aPath) +{ + index = anIndex; + path = xstrdup(aPath); + + parseSizeL1L2(); + + /* Initialise replacement policy stuff */ + repl = createRemovalPolicy(Config.replPolicy); + + parseOptions(0); +} + +void +Fs::Ufs::UFSSwapDir::changeIO(DiskIOModule *module) +{ + DiskIOStrategy *anIO = module->createStrategy(); + safe_free(ioType); + ioType = xstrdup(module->type()); + + delete IO->io; + IO->io = anIO; + /* Change the IO Options */ + + if (currentIOOptions && currentIOOptions->options.size() > 2) + delete currentIOOptions->options.pop_back(); + + /* TODO: factor out these 4 lines */ + ConfigOption *ioOptions = IO->io->getOptionTree(); + + if (ioOptions) + currentIOOptions->options.push_back(ioOptions); +} + +bool +Fs::Ufs::UFSSwapDir::optionIOParse(char const *option, const char *value, int isaReconfig) +{ + if (strcmp(option, "IOEngine") != 0) + return false; + + if (isaReconfig) + /* silently ignore this */ + return true; + + if (!value) + self_destruct(); + + DiskIOModule *module = DiskIOModule::Find(value); + + if (!module) + self_destruct(); + + changeIO(module); + + return true; +} + +void +Fs::Ufs::UFSSwapDir::optionIODump(StoreEntry * e) const +{ + storeAppendPrintf(e, " IOEngine=%s", ioType); +} + +ConfigOption * +Fs::Ufs::UFSSwapDir::getOptionTree() const +{ + ConfigOption *parentResult = SwapDir::getOptionTree(); + + if (currentIOOptions == NULL) + currentIOOptions = new ConfigOptionVector(); + + currentIOOptions->options.push_back(parentResult); + + currentIOOptions->options.push_back(new ConfigOptionAdapter(*const_cast(this), &UFSSwapDir::optionIOParse, &UFSSwapDir::optionIODump)); + + if (ConfigOption *ioOptions = IO->io->getOptionTree()) + currentIOOptions->options.push_back(ioOptions); + + ConfigOption* result = currentIOOptions; + + currentIOOptions = NULL; + + return result; +} + +void +Fs::Ufs::UFSSwapDir::init() +{ + debugs(47, 3, HERE << "Initialising UFS SwapDir engine."); + /* Parsing must be finished by now - force to NULL, don't delete */ + currentIOOptions = NULL; + static int started_clean_event = 0; + static const char *errmsg = + "\tFailed to verify one of the swap directories, Check cache.log\n" + "\tfor details. Run 'squid -z' to create swap directories\n" + "\tif needed, or if running Squid for the first time."; + IO->init(); + + if (verifyCacheDirs()) + fatal(errmsg); + + openLog(); + + rebuild(); + + if (!started_clean_event) { + eventAdd("UFS storeDirClean", CleanEvent, NULL, 15.0, 1); + started_clean_event = 1; + } + + (void) storeDirGetBlkSize(path, &fs.blksize); +} + +void +Fs::Ufs::UFSSwapDir::create() +{ + debugs(47, 3, "Creating swap space in " << path); + createDirectory(path, 0); + createSwapSubDirs(); +} + +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) +{ + /* modulename is only set to disk modules that are built, by configure, + * so the Find call should never return NULL here. + */ + IO = new Fs::Ufs::UFSStrategy(DiskIOModule::Find(anIOType)->createStrategy()); +} + +Fs::Ufs::UFSSwapDir::~UFSSwapDir() +{ + if (swaplog_fd > -1) { + file_close(swaplog_fd); + swaplog_fd = -1; + } + + delete map; + + if (IO) + delete IO; + + IO = NULL; + + safe_free(ioType); +} + +void +Fs::Ufs::UFSSwapDir::dumpEntry(StoreEntry &e) const +{ + debugs(47, DBG_CRITICAL, HERE << "FILENO "<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << e.swap_filen); + debugs(47, DBG_CRITICAL, HERE << "PATH " << fullPath(e.swap_filen, NULL) ); + e.dump(0); +} + +bool +Fs::Ufs::UFSSwapDir::doubleCheck(StoreEntry & e) +{ + + struct stat sb; + + if (::stat(fullPath(e.swap_filen, NULL), &sb) < 0) { + debugs(47, DBG_CRITICAL, HERE << "WARNING: Missing swap file"); + dumpEntry(e); + return true; + } + + if ((off_t)e.swap_file_sz != sb.st_size) { + debugs(47, DBG_CRITICAL, HERE << "WARNING: Size Mismatch. Entry size: " + << e.swap_file_sz << ", file size: " << sb.st_size); + dumpEntry(e); + return true; + } + + return false; +} + +void +Fs::Ufs::UFSSwapDir::statfs(StoreEntry & sentry) const +{ + int totl_kb = 0; + int free_kb = 0; + int totl_in = 0; + int free_in = 0; + int x; + storeAppendPrintf(&sentry, "First level subdirectories: %d\n", l1); + storeAppendPrintf(&sentry, "Second level subdirectories: %d\n", l2); + storeAppendPrintf(&sentry, "Maximum Size: %" PRIu64 " KB\n", maxSize() >> 10); + storeAppendPrintf(&sentry, "Current Size: %.2f KB\n", currentSize() / 1024.0); + storeAppendPrintf(&sentry, "Percent Used: %0.2f%%\n", + Math::doublePercent(currentSize(), maxSize())); + storeAppendPrintf(&sentry, "Filemap bits in use: %d of %d (%d%%)\n", + map->numFilesInMap(), map->capacity(), + Math::intPercent(map->numFilesInMap(), map->capacity())); + x = storeDirGetUFSStats(path, &totl_kb, &free_kb, &totl_in, &free_in); + + if (0 == x) { + storeAppendPrintf(&sentry, "Filesystem Space in use: %d/%d KB (%d%%)\n", + totl_kb - free_kb, + totl_kb, + Math::intPercent(totl_kb - free_kb, totl_kb)); + storeAppendPrintf(&sentry, "Filesystem Inodes in use: %d/%d (%d%%)\n", + totl_in - free_in, + totl_in, + Math::intPercent(totl_in - free_in, totl_in)); + } + + storeAppendPrintf(&sentry, "Flags:"); + + if (flags.selected) + storeAppendPrintf(&sentry, " SELECTED"); + + if (flags.read_only) + storeAppendPrintf(&sentry, " READ-ONLY"); + + storeAppendPrintf(&sentry, "\n"); + + IO->statfs(sentry); +} + +void +Fs::Ufs::UFSSwapDir::maintain() +{ + /* We can't delete objects while rebuilding swap */ + + /* XXX FIXME each store should start maintaining as it comes online. */ + + if (StoreController::store_dirs_rebuilding) + return; + + StoreEntry *e = NULL; + + int removed = 0; + + RemovalPurgeWalker *walker; + + double f = (double) (currentSize() - minSize()) / (maxSize() - minSize()); + + f = f < 0.0 ? 0.0 : f > 1.0 ? 1.0 : f; + + int max_scan = (int) (f * 400.0 + 100.0); + + int max_remove = (int) (f * 70.0 + 10.0); + + /* + * This is kinda cheap, but so we need this priority hack? + */ + + debugs(47, 3, HERE << "f=" << f << ", max_scan=" << max_scan << ", max_remove=" << max_remove ); + + walker = repl->PurgeInit(repl, max_scan); + + while (1) { + if (currentSize() < minSize()) + break; + + if (removed >= max_remove) + break; + + e = walker->Next(walker); + + if (!e) + break; /* no more objects */ + + ++removed; + + e->release(); + } + + walker->Done(walker); + debugs(47, (removed ? 2 : 3), HERE << path << + " removed " << removed << "/" << max_remove << " f=" << + std::setprecision(4) << f << " max_scan=" << max_scan); +} + +void +Fs::Ufs::UFSSwapDir::reference(StoreEntry &e) +{ + debugs(47, 3, HERE << "referencing " << &e << " " << + e.swap_dirn << "/" << e.swap_filen); + + if (repl->Referenced) + repl->Referenced(repl, &e, &e.repl); +} + +bool +Fs::Ufs::UFSSwapDir::dereference(StoreEntry & e) +{ + debugs(47, 3, HERE << "dereferencing " << &e << " " << + e.swap_dirn << "/" << e.swap_filen); + + if (repl->Dereferenced) + repl->Dereferenced(repl, &e, &e.repl); + + return true; // keep e in the global store_table +} + +StoreIOState::Pointer +Fs::Ufs::UFSSwapDir::createStoreIO(StoreEntry &e, StoreIOState::STFNCB * file_callback, StoreIOState::STIOCB * aCallback, void *callback_data) +{ + return IO->create (this, &e, file_callback, aCallback, callback_data); +} + +StoreIOState::Pointer +Fs::Ufs::UFSSwapDir::openStoreIO(StoreEntry &e, StoreIOState::STFNCB * file_callback, StoreIOState::STIOCB * aCallback, void *callback_data) +{ + return IO->open (this, &e, file_callback, aCallback, callback_data); +} + +int +Fs::Ufs::UFSSwapDir::mapBitTest(sfileno filn) +{ + return map->testBit(filn); +} + +void +Fs::Ufs::UFSSwapDir::mapBitSet(sfileno filn) +{ + map->setBit(filn); +} + +void +Fs::Ufs::UFSSwapDir::mapBitReset(sfileno filn) +{ + /* + * We have to test the bit before calling clearBit as + * it doesn't do bounds checking and blindly assumes + * filn is a valid file number, but it might not be because + * the map is dynamic in size. Also clearing an already clear + * bit puts the map counter of-of-whack. + */ + + if (map->testBit(filn)) + map->clearBit(filn); +} + +int +Fs::Ufs::UFSSwapDir::mapBitAllocate() +{ + int fn; + fn = map->allocate(suggest); + map->setBit(fn); + suggest = fn + 1; + return fn; +} + +char * +Fs::Ufs::UFSSwapDir::swapSubDir(int subdirn)const +{ + LOCAL_ARRAY(char, fullfilename, MAXPATHLEN); + assert(0 <= subdirn && subdirn < l1); + snprintf(fullfilename, MAXPATHLEN, "%s/%02X", path, subdirn); + return fullfilename; +} + +int +Fs::Ufs::UFSSwapDir::createDirectory(const char *aPath, int should_exist) +{ + int created = 0; + + struct stat st; + getCurrentTime(); + + if (0 == ::stat(aPath, &st)) { + if (S_ISDIR(st.st_mode)) { + debugs(47, (should_exist ? 3 : DBG_IMPORTANT), aPath << " exists"); + } else { + fatalf("Swap directory %s is not a directory.", aPath); + } + +#if _SQUID_MSWIN_ + + } else if (0 == mkdir(aPath)) { +#else + + } else if (0 == mkdir(aPath, 0755)) { +#endif + debugs(47, (should_exist ? DBG_IMPORTANT : 3), aPath << " created"); + created = 1; + } else { + fatalf("Failed to make swap directory %s: %s", + aPath, xstrerror()); + } + + return created; +} + +bool +Fs::Ufs::UFSSwapDir::pathIsDirectory(const char *aPath)const +{ + + struct stat sb; + + if (::stat(aPath, &sb) < 0) { + debugs(47, DBG_CRITICAL, "ERROR: " << aPath << ": " << xstrerror()); + return false; + } + + if (S_ISDIR(sb.st_mode) == 0) { + debugs(47, DBG_CRITICAL, "WARNING: " << aPath << " is not a directory"); + return false; + } + + return true; +} + +bool +Fs::Ufs::UFSSwapDir::verifyCacheDirs() +{ + if (!pathIsDirectory(path)) + return true; + + for (int j = 0; j < l1; ++j) { + char const *aPath = swapSubDir(j); + + if (!pathIsDirectory(aPath)) + return true; + } + + return false; +} + +void +Fs::Ufs::UFSSwapDir::createSwapSubDirs() +{ + LOCAL_ARRAY(char, name, MAXPATHLEN); + + for (int i = 0; i < l1; ++i) { + snprintf(name, MAXPATHLEN, "%s/%02X", path, i); + + int should_exist; + + if (createDirectory(name, 0)) + should_exist = 0; + else + should_exist = 1; + + debugs(47, DBG_IMPORTANT, "Making directories in " << name); + + for (int k = 0; k < l2; ++k) { + snprintf(name, MAXPATHLEN, "%s/%02X/%02X", path, i, k); + createDirectory(name, should_exist); + } + } +} + +char * +Fs::Ufs::UFSSwapDir::logFile(char const *ext) const +{ + LOCAL_ARRAY(char, lpath, MAXPATHLEN); + LOCAL_ARRAY(char, pathtmp, MAXPATHLEN); + LOCAL_ARRAY(char, digit, 32); + char *pathtmp2; + + if (Config.Log.swap) { + xstrncpy(pathtmp, path, MAXPATHLEN - 64); + pathtmp2 = pathtmp; + + while ((pathtmp2 = strchr(pathtmp2, '/')) != NULL) + *pathtmp2 = '.'; + + while (strlen(pathtmp) && pathtmp[strlen(pathtmp) - 1] == '.') + pathtmp[strlen(pathtmp) - 1] = '\0'; + + for (pathtmp2 = pathtmp; *pathtmp2 == '.'; ++pathtmp2); + snprintf(lpath, MAXPATHLEN - 64, Config.Log.swap, pathtmp2); + + if (strncmp(lpath, Config.Log.swap, MAXPATHLEN - 64) == 0) { + strcat(lpath, "."); + snprintf(digit, 32, "%02d", index); + strncat(lpath, digit, 3); + } + } else { + xstrncpy(lpath, path, MAXPATHLEN - 64); + strcat(lpath, "/swap.state"); + } + + if (ext) + strncat(lpath, ext, 16); + + return lpath; +} + +void +Fs::Ufs::UFSSwapDir::openLog() +{ + char *logPath; + logPath = logFile(); + swaplog_fd = file_open(logPath, O_WRONLY | O_CREAT | O_BINARY); + + if (swaplog_fd < 0) { + debugs(50, DBG_IMPORTANT, "ERROR opening swap log " << logPath << ": " << xstrerror()); + fatal("UFSSwapDir::openLog: Failed to open swap log."); + } + + debugs(50, 3, HERE << "Cache Dir #" << index << " log opened on FD " << swaplog_fd); + + if (0 == NumberOfUFSDirs) + assert(NULL == UFSDirToGlobalDirMapping); + + ++NumberOfUFSDirs; + + assert(NumberOfUFSDirs <= Config.cacheSwap.n_configured); +} + +void +Fs::Ufs::UFSSwapDir::closeLog() +{ + if (swaplog_fd < 0) /* not open */ + return; + + file_close(swaplog_fd); + + debugs(47, 3, "Cache Dir #" << index << " log closed on FD " << swaplog_fd); + + swaplog_fd = -1; + + --NumberOfUFSDirs; + + assert(NumberOfUFSDirs >= 0); + + if (0 == NumberOfUFSDirs) + safe_free(UFSDirToGlobalDirMapping); +} + +bool +Fs::Ufs::UFSSwapDir::validL1(int anInt) const +{ + return anInt < l1; +} + +bool +Fs::Ufs::UFSSwapDir::validL2(int anInt) const +{ + return anInt < l2; +} + +StoreEntry * +Fs::Ufs::UFSSwapDir::addDiskRestore(const cache_key * key, + sfileno file_number, + uint64_t swap_file_sz, + time_t expires, + time_t timestamp, + time_t lastref, + time_t lastmod, + uint32_t refcount, + uint16_t newFlags, + int clean) +{ + StoreEntry *e = NULL; + debugs(47, 5, HERE << storeKeyText(key) << + ", fileno="<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << file_number); + /* if you call this you'd better be sure file_number is not + * already in use! */ + e = new StoreEntry(); + e->store_status = STORE_OK; + e->setMemStatus(NOT_IN_MEMORY); + e->swap_status = SWAPOUT_DONE; + e->swap_filen = file_number; + e->swap_dirn = index; + e->swap_file_sz = swap_file_sz; + e->lock_count = 0; + e->lastref = lastref; + e->timestamp = timestamp; + e->expires = expires; + e->lastmod = lastmod; + e->refcount = refcount; + e->flags = newFlags; + EBIT_SET(e->flags, ENTRY_CACHABLE); + EBIT_CLR(e->flags, RELEASE_REQUEST); + EBIT_CLR(e->flags, KEY_PRIVATE); + e->ping_status = PING_NONE; + EBIT_CLR(e->flags, ENTRY_VALIDATED); + mapBitSet(e->swap_filen); + cur_size += fs.blksize * sizeInBlocks(e->swap_file_sz); + ++n_disk_objects; + e->hashInsert(key); /* do it after we clear KEY_PRIVATE */ + replacementAdd (e); + return e; +} + +void +Fs::Ufs::UFSSwapDir::undoAddDiskRestore(StoreEntry *e) +{ + debugs(47, 5, HERE << *e); + replacementRemove(e); // checks swap_dirn so do it before we invalidate it + // Do not unlink the file as it might be used by a subsequent entry. + mapBitReset(e->swap_filen); + e->swap_filen = -1; + e->swap_dirn = -1; + cur_size -= fs.blksize * sizeInBlocks(e->swap_file_sz); + --n_disk_objects; +} + +void +Fs::Ufs::UFSSwapDir::rebuild() +{ + ++StoreController::store_dirs_rebuilding; + eventAdd("storeRebuild", Fs::Ufs::RebuildState::RebuildStep, new Fs::Ufs::RebuildState(this), 0.0, 1); +} + +void +Fs::Ufs::UFSSwapDir::closeTmpSwapLog() +{ + char *swaplog_path = xstrdup(logFile(NULL)); + char *new_path = xstrdup(logFile(".new")); + int fd; + file_close(swaplog_fd); + + if (xrename(new_path, swaplog_path) < 0) { + debugs(50, DBG_IMPORTANT, HERE << "ERROR: " << swaplog_path << ": " << xstrerror()); + fatalf("Failed to rename log file %s to %s.new", swaplog_path, swaplog_path); + } + + fd = file_open(swaplog_path, O_WRONLY | O_CREAT | O_BINARY); + + if (fd < 0) { + debugs(50, DBG_IMPORTANT, HERE << "ERROR: " << swaplog_path << ": " << xstrerror()); + fatalf("Failed to open swap log %s", swaplog_path); + } + + safe_free(swaplog_path); + safe_free(new_path); + swaplog_fd = fd; + debugs(47, 3, HERE << "Cache Dir #" << index << " log opened on FD " << fd); +} + +FILE * +Fs::Ufs::UFSSwapDir::openTmpSwapLog(int *clean_flag, int *zero_flag) +{ + char *swaplog_path = xstrdup(logFile(NULL)); + char *clean_path = xstrdup(logFile(".last-clean")); + char *new_path = xstrdup(logFile(".new")); + + struct stat log_sb; + + struct stat clean_sb; + FILE *fp; + int fd; + + if (::stat(swaplog_path, &log_sb) < 0) { + debugs(47, DBG_IMPORTANT, HERE << "Cache Dir #" << index << ": No log file"); + safe_free(swaplog_path); + safe_free(clean_path); + safe_free(new_path); + return NULL; + } + + *zero_flag = log_sb.st_size == 0 ? 1 : 0; + /* close the existing write-only FD */ + + if (swaplog_fd >= 0) + file_close(swaplog_fd); + + /* open a write-only FD for the new log */ + fd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY); + + if (fd < 0) { + debugs(50, DBG_IMPORTANT, "ERROR: while opening swap log" << new_path << ": " << xstrerror()); + fatal("UFSSwapDir::openTmpSwapLog: Failed to open swap log."); + } + + swaplog_fd = fd; + + { + const StoreSwapLogHeader header; + MemBuf buf; + buf.init(header.record_size, header.record_size); + buf.append(reinterpret_cast(&header), sizeof(header)); + // Pad to keep in sync with UFSSwapDir::writeCleanStart(). + memset(buf.space(), 0, header.gapSize()); + buf.appended(header.gapSize()); + file_write(swaplog_fd, -1, buf.content(), buf.contentSize(), + NULL, NULL, buf.freeFunc()); + } + + /* open a read-only stream of the old log */ + fp = fopen(swaplog_path, "rb"); + + if (fp == NULL) { + debugs(50, DBG_CRITICAL, "ERROR: while opening " << swaplog_path << ": " << xstrerror()); + fatal("Failed to open swap log for reading"); + } + + memset(&clean_sb, '\0', sizeof(struct stat)); + + if (::stat(clean_path, &clean_sb) < 0) + *clean_flag = 0; + else if (clean_sb.st_mtime < log_sb.st_mtime) + *clean_flag = 0; + else + *clean_flag = 1; + + safeunlink(clean_path, 1); + + safe_free(swaplog_path); + + safe_free(clean_path); + + safe_free(new_path); + + return fp; +} + +/* + * Begin the process to write clean cache state. For AUFS this means + * opening some log files and allocating write buffers. Return 0 if + * we succeed, and assign the 'func' and 'data' return pointers. + */ +int +Fs::Ufs::UFSSwapDir::writeCleanStart() +{ + UFSCleanLog *state = new UFSCleanLog(this); + StoreSwapLogHeader header; +#if HAVE_FCHMOD + + struct stat sb; +#endif + + cleanLog = NULL; + state->newLog = xstrdup(logFile(".clean")); + state->fd = file_open(state->newLog, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY); + + if (state->fd < 0) { + xfree(state->newLog); + delete state; + return -1; + } + + state->cur = xstrdup(logFile(NULL)); + state->cln = xstrdup(logFile(".last-clean")); + state->outbuf = (char *)xcalloc(CLEAN_BUF_SZ, 1); + state->outbuf_offset = 0; + /*copy the header */ + memcpy(state->outbuf, &header, sizeof(StoreSwapLogHeader)); + // Leave a gap to keep in sync with UFSSwapDir::openTmpSwapLog(). + memset(state->outbuf + sizeof(StoreSwapLogHeader), 0, header.gapSize()); + state->outbuf_offset += header.record_size; + + state->walker = repl->WalkInit(repl); + ::unlink(state->cln); + debugs(47, 3, HERE << "opened " << state->newLog << ", FD " << state->fd); +#if HAVE_FCHMOD + + if (::stat(state->cur, &sb) == 0) + fchmod(state->fd, sb.st_mode); + +#endif + + + cleanLog = state; + return 0; +} + +void +Fs::Ufs::UFSSwapDir::writeCleanDone() +{ + UFSCleanLog *state = (UFSCleanLog *)cleanLog; + int fd; + + if (NULL == state) + return; + + if (state->fd < 0) + return; + + state->walker->Done(state->walker); + + if (FD_WRITE_METHOD(state->fd, state->outbuf, state->outbuf_offset) < 0) { + debugs(50, DBG_CRITICAL, HERE << state->newLog << ": write: " << xstrerror()); + debugs(50, DBG_CRITICAL, HERE << "Current swap logfile not replaced."); + file_close(state->fd); + state->fd = -1; + ::unlink(state->newLog); + } + + safe_free(state->outbuf); + /* + * You can't rename open files on Microsoft "operating systems" + * so we have to close before renaming. + */ + closeLog(); + /* save the fd value for a later test */ + fd = state->fd; + /* rename */ + + if (state->fd >= 0) { +#if _SQUID_OS2_ || _SQUID_WINDOWS_ + file_close(state->fd); + state->fd = -1; +#endif + + xrename(state->newLog, state->cur); + } + + /* touch a timestamp file if we're not still validating */ + if (StoreController::store_dirs_rebuilding) + (void) 0; + else if (fd < 0) + (void) 0; + else + file_close(file_open(state->cln, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY)); + + /* close */ + safe_free(state->cur); + + safe_free(state->newLog); + + safe_free(state->cln); + + if (state->fd >= 0) + file_close(state->fd); + + state->fd = -1; + + delete state; + + cleanLog = NULL; +} + +void +Fs::Ufs::UFSSwapDir::CleanEvent(void *unused) +{ + static int swap_index = 0; + int i; + int j = 0; + int n = 0; + /* + * Assert that there are UFS cache_dirs configured, otherwise + * we should never be called. + */ + assert(NumberOfUFSDirs); + + if (NULL == UFSDirToGlobalDirMapping) { + SwapDir *sd; + /* + * Initialize the little array that translates UFS cache_dir + * number into the Config.cacheSwap.swapDirs array index. + */ + UFSDirToGlobalDirMapping = (int *)xcalloc(NumberOfUFSDirs, sizeof(*UFSDirToGlobalDirMapping)); + + for (i = 0, n = 0; i < Config.cacheSwap.n_configured; ++i) { + /* This is bogus, the controller should just clean each instance once */ + sd = dynamic_cast (INDEXSD(i)); + + if (!UFSSwapDir::IsUFSDir(sd)) + continue; + + UFSSwapDir *usd = dynamic_cast(sd); + + assert (usd); + + UFSDirToGlobalDirMapping[n] = i; + ++n; + + j += (usd->l1 * usd->l2); + } + + assert(n == NumberOfUFSDirs); + /* + * Start the commonUfsDirClean() swap_index with a random + * value. j equals the total number of UFS level 2 + * swap directories + */ + swap_index = (int) (squid_random() % j); + } + + /* if the rebuild is finished, start cleaning directories. */ + if (0 == StoreController::store_dirs_rebuilding) { + n = DirClean(swap_index); + ++swap_index; + } + + eventAdd("storeDirClean", CleanEvent, NULL, + 15.0 * exp(-0.25 * n), 1); +} + +bool +Fs::Ufs::UFSSwapDir::IsUFSDir(SwapDir * sd) +{ + UFSSwapDir *mySD = dynamic_cast(sd); + return (mySD != 0) ; +} + +/* + * XXX: this is broken - it assumes all cache dirs use the same + * l1 and l2 scheme. -RBC 20021215. Partial fix is in place - + * if not UFSSwapDir return 0; + */ +bool +Fs::Ufs::UFSSwapDir::FilenoBelongsHere(int fn, int F0, int F1, int F2) +{ + int D1, D2; + int L1, L2; + int filn = fn; + assert(F0 < Config.cacheSwap.n_configured); + assert (UFSSwapDir::IsUFSDir (dynamic_cast(INDEXSD(F0)))); + UFSSwapDir *sd = dynamic_cast(INDEXSD(F0)); + + if (!sd) + return 0; + + L1 = sd->l1; + + L2 = sd->l2; + + D1 = ((filn / L2) / L2) % L1; + + if (F1 != D1) + return 0; + + D2 = (filn / L2) % L2; + + if (F2 != D2) + return 0; + + return 1; +} + +int +Fs::Ufs::UFSSwapDir::validFileno(sfileno filn, int flag) const +{ + if (filn < 0) + return 0; + + /* + * If flag is set it means out-of-range file number should + * be considered invalid. + */ + if (flag) + if (filn > map->capacity()) + return 0; + + return 1; +} + +void +Fs::Ufs::UFSSwapDir::unlinkFile(sfileno f) +{ + debugs(79, 3, HERE << "unlinking fileno " << std::setfill('0') << + std::hex << std::uppercase << std::setw(8) << f << " '" << + fullPath(f,NULL) << "'"); + /* commonUfsDirMapBitReset(this, f); */ + IO->unlinkFile(fullPath(f,NULL)); +} + +bool +Fs::Ufs::UFSSwapDir::unlinkdUseful() const +{ + // unlinkd may be useful only in workers + return IamWorkerProcess() && IO->io->unlinkdUseful(); +} + +void +Fs::Ufs::UFSSwapDir::unlink(StoreEntry & e) +{ + debugs(79, 3, HERE << "dirno " << index << ", fileno "<< + std::setfill('0') << std::hex << std::uppercase << std::setw(8) << e.swap_filen); + if (e.swap_status == SWAPOUT_DONE) { + cur_size -= fs.blksize * sizeInBlocks(e.swap_file_sz); + --n_disk_objects; + } + replacementRemove(&e); + mapBitReset(e.swap_filen); + UFSSwapDir::unlinkFile(e.swap_filen); +} + +void +Fs::Ufs::UFSSwapDir::replacementAdd(StoreEntry * e) +{ + debugs(47, 4, HERE << "added node " << e << " to dir " << index); + repl->Add(repl, e, &e->repl); +} + + +void +Fs::Ufs::UFSSwapDir::replacementRemove(StoreEntry * e) +{ + StorePointer SD; + + if (e->swap_dirn < 0) + return; + + SD = INDEXSD(e->swap_dirn); + + assert (dynamic_cast(SD.getRaw()) == this); + + debugs(47, 4, HERE << "remove node " << e << " from dir " << index); + + repl->Remove(repl, e, &e->repl); +} + +void +Fs::Ufs::UFSSwapDir::dump(StoreEntry & entry) const +{ + storeAppendPrintf(&entry, " %" PRIu64 " %d %d", maxSize() >> 20, l1, l2); + dumpOptions(&entry); +} + +char * +Fs::Ufs::UFSSwapDir::fullPath(sfileno filn, char *fullpath) const +{ + LOCAL_ARRAY(char, fullfilename, MAXPATHLEN); + int L1 = l1; + int L2 = l2; + + if (!fullpath) + fullpath = fullfilename; + + fullpath[0] = '\0'; + + snprintf(fullpath, MAXPATHLEN, "%s/%02X/%02X/%08X", + path, + ((filn / L2) / L2) % L1, + (filn / L2) % L2, + filn); + + return fullpath; +} + +int +Fs::Ufs::UFSSwapDir::callback() +{ + return IO->callback(); +} + +void +Fs::Ufs::UFSSwapDir::sync() +{ + IO->sync(); +} + +void +Fs::Ufs::UFSSwapDir::swappedOut(const StoreEntry &e) +{ + cur_size += fs.blksize * sizeInBlocks(e.swap_file_sz); + ++n_disk_objects; +} + +StoreSearch * +Fs::Ufs::UFSSwapDir::search(String const url, HttpRequest *request) +{ + if (url.size()) + fatal ("Cannot search by url yet\n"); + + return new Fs::Ufs::StoreSearchUFS (this); +} + +void +Fs::Ufs::UFSSwapDir::logEntry(const StoreEntry & e, int op) const +{ + StoreSwapLogData *s = new StoreSwapLogData; + s->op = (char) op; + s->swap_filen = e.swap_filen; + s->timestamp = e.timestamp; + s->lastref = e.lastref; + s->expires = e.expires; + s->lastmod = e.lastmod; + s->swap_file_sz = e.swap_file_sz; + s->refcount = e.refcount; + s->flags = e.flags; + memcpy(s->key, e.key, SQUID_MD5_DIGEST_LENGTH); + s->finalize(); + file_write(swaplog_fd, + -1, + s, + sizeof(StoreSwapLogData), + NULL, + NULL, + FreeObject); +} + +int +Fs::Ufs::UFSSwapDir::DirClean(int swap_index) +{ + DIR *dir_pointer = NULL; + + LOCAL_ARRAY(char, p1, MAXPATHLEN + 1); + LOCAL_ARRAY(char, p2, MAXPATHLEN + 1); + + int files[20]; + int swapfileno; + int fn; /* same as swapfileno, but with dirn bits set */ + int n = 0; + int k = 0; + int N0, N1, N2; + int D0, D1, D2; + UFSSwapDir *SD; + N0 = NumberOfUFSDirs; + D0 = UFSDirToGlobalDirMapping[swap_index % N0]; + SD = dynamic_cast(INDEXSD(D0)); + assert (SD); + N1 = SD->l1; + D1 = (swap_index / N0) % N1; + N2 = SD->l2; + D2 = ((swap_index / N0) / N1) % N2; + snprintf(p1, MAXPATHLEN, "%s/%02X/%02X", + SD->path, D1, D2); + debugs(36, 3, HERE << "Cleaning directory " << p1); + dir_pointer = opendir(p1); + + if (dir_pointer == NULL) { + if (errno == ENOENT) { + debugs(36, DBG_CRITICAL, HERE << "WARNING: Creating " << p1); +#if _SQUID_MSWIN_ + + if (mkdir(p1) == 0) +#else + + if (mkdir(p1, 0777) == 0) +#endif + + return 0; + } + + debugs(50, DBG_CRITICAL, HERE << p1 << ": " << xstrerror()); + safeunlink(p1, 1); + return 0; + } + + dirent_t *de; + while ((de = readdir(dir_pointer)) != NULL && k < 20) { + if (sscanf(de->d_name, "%X", &swapfileno) != 1) + continue; + + fn = swapfileno; /* XXX should remove this cruft ! */ + + if (SD->validFileno(fn, 1)) + if (SD->mapBitTest(fn)) + if (UFSSwapDir::FilenoBelongsHere(fn, D0, D1, D2)) + continue; + + files[k] = swapfileno; + ++k; + } + + closedir(dir_pointer); + + if (k == 0) + return 0; + + qsort(files, k, sizeof(int), rev_int_sort); + + if (k > 10) + k = 10; + + for (n = 0; n < k; ++n) { + debugs(36, 3, HERE << "Cleaning file "<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << files[n]); + snprintf(p2, MAXPATHLEN + 1, "%s/%08X", p1, files[n]); + safeunlink(p2, 0); + ++statCounter.swap.files_cleaned; + } + + debugs(36, 3, HERE << "Cleaned " << k << " unused files from " << p1); + return k; +} === added file 'src/fs/ufs/UFSSwapDir.h' --- src/fs/ufs/UFSSwapDir.h 1970-01-01 00:00:00 +0000 +++ src/fs/ufs/UFSSwapDir.h 2012-08-05 20:47:56 +0000 @@ -0,0 +1,191 @@ +/* + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + */ + +#ifndef SQUID_FS_UFS_UFSSWAPDIR_H +#define SQUID_FS_UFS_UFSSWAPDIR_H + +#include "SquidString.h" +#include "Store.h" +#include "StoreIOState.h" +#include "StoreSearch.h" +#include "SwapDir.h" +#include "swap_log_op.h" +#include "UFSStrategy.h" + +class HttpRequest; +class ConfigOptionVector; +class FileMap; +class DiskIOModule; + +namespace Fs +{ +namespace Ufs +{ +/// \ingroup UFS +class UFSSwapDir : public SwapDir +{ +public: + static bool IsUFSDir(SwapDir* sd); + static int DirClean(int swap_index); + /** check whether swapfile belongs to the specified cachedir/l1dir/l2dir + * + * \param cachedir the number of the cachedir which is being tested + * \param level1dir level-1 dir in the cachedir + * \param level2dir level-2 dir + */ + static bool FilenoBelongsHere(int fn, int cachedir, int level1dir, int level2dir); + + UFSSwapDir(char const *aType, const char *aModuleType); + /** Initial setup / end destruction */ + virtual void init(); + /** Create a new SwapDir (-z command-line option) */ + virtual void create(); + virtual void dump(StoreEntry &) const; + ~UFSSwapDir(); + virtual StoreSearch *search(String const url, HttpRequest *); + /** double-check swap during rebuild (-S command-line option) + * + * called by storeCleanup if needed + */ + virtual bool doubleCheck(StoreEntry &); + virtual bool unlinkdUseful() const; + /** unlink a file, and remove its entry from the filemap */ + virtual void unlink(StoreEntry &); + virtual void statfs(StoreEntry &)const; + virtual void maintain(); + /** check whether this filesystem can store the given object + * + * UFS filesystems will happily store anything as long as + * the LRU time isn't too small + */ + virtual bool canStore(const StoreEntry &e, int64_t diskSpaceNeeded, int &load) const; + /** reference an object + * + * This routine is called whenever an object is referenced, so we can + * maintain replacement information within the storage fs. + */ + virtual void reference(StoreEntry &); + /** de-reference an object + * + * This routine is called whenever the last reference to an object is + * removed, to maintain replacement information within the storage fs. + */ + virtual bool dereference(StoreEntry &); + virtual StoreIOState::Pointer createStoreIO(StoreEntry &, StoreIOState::STFNCB *, StoreIOState::STIOCB *, void *); + virtual StoreIOState::Pointer openStoreIO(StoreEntry &, StoreIOState::STFNCB *, StoreIOState::STIOCB *, void *); + virtual void openLog(); + virtual void closeLog(); + virtual int writeCleanStart(); + virtual void writeCleanDone(); + virtual void logEntry(const StoreEntry & e, int op) const; + virtual void parse(int index, char *path); ///parse configuration and setup new SwapDir + virtual void reconfigure(); ///reconfigure the SwapDir + virtual int callback(); + virtual void sync(); + virtual void swappedOut(const StoreEntry &e); + virtual uint64_t currentSize() const { return cur_size; } + virtual uint64_t currentCount() const { return n_disk_objects; } + + void unlinkFile(sfileno f); + // move down when unlink is a virtual method + //protected: + Fs::Ufs::UFSStrategy *IO; + char *fullPath(sfileno, char *) const; + /* temp */ + void closeTmpSwapLog(); + FILE *openTmpSwapLog(int *clean_flag, int *zero_flag); + char *swapSubDir(int subdirn) const; + int mapBitTest(sfileno filn); + void mapBitReset(sfileno filn); + void mapBitSet(sfileno filn); + /** Add a new object to the cache with empty memory copy and pointer to disk + * + * This method is used to rebuild a store from disk + */ + StoreEntry *addDiskRestore(const cache_key * key, + sfileno file_number, + uint64_t swap_file_sz, + time_t expires, + time_t timestamp, + time_t lastref, + time_t lastmod, + uint32_t refcount, + uint16_t flags, + int clean); + /// Undo the effects of UFSSwapDir::addDiskRestore(). + void undoAddDiskRestore(StoreEntry *e); + int validFileno(sfileno filn, int flag) const; + int mapBitAllocate(); + virtual ConfigOption *getOptionTree() const; + + void *fsdata; + + bool validL2(int) const; + bool validL1(int) const; + + /** Add and remove the given StoreEntry from the replacement policy in use */ + void replacementAdd(StoreEntry *e); + void replacementRemove(StoreEntry *e); + +protected: + FileMap *map; + int suggest; + int l1; + int l2; + +private: + void parseSizeL1L2(); + static int NumberOfUFSDirs; + static int * UFSDirToGlobalDirMapping; + bool pathIsDirectory(const char *path)const; + int swaplog_fd; + static EVH CleanEvent; + /** Verify that the the CacheDir exists + * + * If this returns < 0, then Squid exits, complains about swap + * directories not existing, and instructs the admin to run 'squid -z' + * Called by UFSSwapDir::init + */ + bool verifyCacheDirs(); + void rebuild(); + int createDirectory(const char *path, int); + void createSwapSubDirs(); + void dumpEntry(StoreEntry &) const; + char *logFile(char const *ext = NULL)const; + void changeIO(DiskIOModule *); + bool optionIOParse(char const *option, const char *value, int reconfiguring); + void optionIODump(StoreEntry * e) const; + mutable ConfigOptionVector *currentIOOptions; + char const *ioType; + uint64_t cur_size; ///< currently used space in the storage area + uint64_t n_disk_objects; ///< total number of objects stored +}; + +} //namespace Ufs +} //namespace Fs +#endif /* SQUID_FS_UFS_UFSSWAPDIR_H */ === added file 'src/fs/ufs/UFSSwapLogParser.cc' --- src/fs/ufs/UFSSwapLogParser.cc 1970-01-01 00:00:00 +0000 +++ src/fs/ufs/UFSSwapLogParser.cc 2012-08-05 20:47:56 +0000 @@ -0,0 +1,201 @@ +/* + * UFSSwapLogParser.cc + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#include "squid.h" +#include "md5.h" +#include "StoreSwapLogData.h" +#include "swap_log_op.h" +#include "UFSSwapLogParser.h" + +/// Parse a swap header entry created on a system with 32-bit size_t and sfileno +/// this is typical of 32-bit systems without large file support +/// NP: SQUID_MD5_DIGEST_LENGTH is very risky still. +class UFSSwapLogParser_v1_32bs:public Fs::Ufs::UFSSwapLogParser +{ +public: + /// version 1 cache swap.state entry with 32-bit size_t (swap_file_sz) + /// time_t an sfileno have no variation from the v1 baseline format + struct StoreSwapLogDataOld { + char op; + sfileno swap_filen; + time_t timestamp; + time_t lastref; + time_t expires; + time_t lastmod; + uint32_t swap_file_sz; + uint16_t refcount; + uint16_t flags; + unsigned char key[SQUID_MD5_DIGEST_LENGTH]; + }; + UFSSwapLogParser_v1_32bs(FILE *fp):Fs::Ufs::UFSSwapLogParser(fp) { + record_size = sizeof(UFSSwapLogParser_v1_32bs::StoreSwapLogDataOld); + } + /// Convert the on-disk 32-bit format to our current format while reading + bool ReadRecord(StoreSwapLogData &swapData) { + UFSSwapLogParser_v1_32bs::StoreSwapLogDataOld readData; + int bytes = sizeof(UFSSwapLogParser_v1_32bs::StoreSwapLogDataOld); + + assert(log); + + if (fread(&readData, bytes, 1, log) != 1) { + return false; + } + swapData.op = readData.op; + swapData.swap_filen = readData.swap_filen; + swapData.timestamp = readData.timestamp; + swapData.lastref = readData.lastref; + swapData.expires = readData.expires; + swapData.lastmod = readData.lastmod; + swapData.swap_file_sz = readData.swap_file_sz; + swapData.refcount = readData.refcount; + swapData.flags = readData.flags; + memcpy(swapData.key, readData.key, SQUID_MD5_DIGEST_LENGTH); + return true; + } +}; + +/// swap.state v2 log parser +class UFSSwapLogParser_v2: public Fs::Ufs::UFSSwapLogParser +{ +public: + UFSSwapLogParser_v2(FILE *fp): Fs::Ufs::UFSSwapLogParser(fp) { + record_size = sizeof(StoreSwapLogData); + } + bool ReadRecord(StoreSwapLogData &swapData) { + assert(log); + return fread(&swapData, sizeof(StoreSwapLogData), 1, log) == 1; + } +}; + +Fs::Ufs::UFSSwapLogParser * +Fs::Ufs::UFSSwapLogParser::GetUFSSwapLogParser(FILE *fp) +{ + StoreSwapLogHeader header; + + assert(fp); + + if (fread(&header, sizeof(StoreSwapLogHeader), 1, fp) != 1) + return NULL; + + if (header.op != SWAP_LOG_VERSION) { + debugs(47, DBG_IMPORTANT, "Old swap file detected..."); + fseek(fp, 0, SEEK_SET); + return new UFSSwapLogParser_v1_32bs(fp); // Um. 32-bits except time_t, and can't determine that. + } + + debugs(47, 2, "Swap file version: " << header.version); + + if (header.version == 1) { + if (fseek(fp, header.record_size, SEEK_SET) != 0) + return NULL; + + debugs(47, DBG_IMPORTANT, "Rejecting swap file v1 to avoid cache " << + "index corruption. Forcing a full cache index rebuild. " << + "See Squid bug #3441."); + return NULL; + +#if UNUSED_CODE + // baseline + // 32-bit sfileno + // native time_t (hopefully 64-bit) + // 64-bit file size + if (header.record_size == sizeof(StoreSwapLogData)) { + debugs(47, DBG_IMPORTANT, "Version 1 of swap file with LFS support detected... "); + return new UFSSwapLogParser_v1(fp); + } + + // which means we have a 3-way grid of permutations to import (yuck!) + // 1) sfileno 32-bit / 64-bit (64-bit was broken) + // 2) time_t 32-bit / 64-bit + // 3) size_t 32-bit / 64-bit (32-bit was pre-LFS) + + // 32-bit systems... + // only LFS (size_t) differs from baseline + if (header.record_size == sizeof(struct UFSSwapLogParser_v1_32bs::StoreSwapLogDataOld)) { + debugs(47, DBG_IMPORTANT, "Version 1 (32-bit) swap file without LFS support detected... "); + return new UFSSwapLogParser_v1_32bs(fp); + } + // LFS (size_t) and timestamps (time_t) differs from baseline + if (header.record_size == sizeof(struct UFSSwapLogParser_v1_32bst::StoreSwapLogDataOld)) { + debugs(47, DBG_IMPORTANT, "Version 1 (32-bit) swap file with short timestamps and without LFS support detected... "); + return new UFSSwapLogParser_v1_32bst(fp); + } + // No downgrade for 64-bit timestamps to 32-bit. + + // 64-bit systems + // sfileno was 64-bit for a some builds + if (header.record_size == sizeof(struct UFSSwapLogParser_v1_64bfn::StoreSwapLogDataOld)) { + debugs(47, DBG_IMPORTANT, "Version 1 (64-bit) swap file with broken sfileno detected... "); + return new UFSSwapLogParser_v1_64bfn(fp); + } + // NP: 64-bit system with 32-bit size_t/time_t are not handled. + + debugs(47, DBG_IMPORTANT, "WARNING: The swap file has wrong format!... "); + debugs(47, DBG_IMPORTANT, "NOTE: Cannot safely downgrade caches to short (32-bit) timestamps."); + return NULL; +#endif + } + + if (header.version >= 2) { + if (!header.sane()) { + debugs(47, DBG_IMPORTANT, "ERROR: Corrupted v" << header.version << + " swap file header."); + return NULL; + } + + if (fseek(fp, header.record_size, SEEK_SET) != 0) + return NULL; + + if (header.version == 2) + return new UFSSwapLogParser_v2(fp); + } + + // TODO: v3: write to disk in network-order bytes for the larger fields? + + debugs(47, DBG_IMPORTANT, "Unknown swap file version: " << header.version); + return NULL; +} + +int +Fs::Ufs::UFSSwapLogParser::SwapLogEntries() +{ + struct stat sb; + + if (log_entries >= 0) + return log_entries; + + if (log && record_size && 0 == fstat(fileno(log), &sb)) { + log_entries = sb.st_size/record_size; + return log_entries; + } + + return 0; +} === added file 'src/fs/ufs/UFSSwapLogParser.h' --- src/fs/ufs/UFSSwapLogParser.h 1970-01-01 00:00:00 +0000 +++ src/fs/ufs/UFSSwapLogParser.h 2012-08-05 20:47:56 +0000 @@ -0,0 +1,69 @@ +/* + * UFSSwapLogParser.h + * + * SQUID Web Proxy Cache http://www.squid-cache.org/ + * ---------------------------------------------------------- + * + * Squid is the result of efforts by numerous individuals from + * the Internet community; see the CONTRIBUTORS file for full + * details. Many organizations have provided support for Squid's + * development; see the SPONSORS file for full details. Squid is + * Copyrighted (C) 2001 by the Regents of the University of + * California; see the COPYRIGHT file for full details. Squid + * incorporates software developed and/or copyrighted by other + * sources; see the CREDITS file for full details. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. + * + */ + +#ifndef SQUID_FS_UFS_UFSSWAPLOGPARSER_H +#define SQUID_FS_UFS_UFSSWAPLOGPARSER_H + +#include + +class StoreSwapLogData; + +namespace Fs +{ +namespace Ufs +{ +/// \ingroup UFS +class UFSSwapLogParser +{ +public: + FILE *log; + int log_entries; + int record_size; + + UFSSwapLogParser(FILE *fp):log(fp),log_entries(-1), record_size(0) { + } + virtual ~UFSSwapLogParser() {}; + + static UFSSwapLogParser *GetUFSSwapLogParser(FILE *fp); + + virtual bool ReadRecord(StoreSwapLogData &swapData) = 0; + int SwapLogEntries(); + void Close() { + if (log) { + fclose(log); + log = NULL; + } + } +}; + +} //namespace Ufs +} //namespace Fs +#endif /* SQUID_FS_UFS_UFSSWAPLOGPARSER_H */ === removed file 'src/fs/ufs/store_dir_ufs.cc' --- src/fs/ufs/store_dir_ufs.cc 2012-07-23 15:34:12 +0000 +++ src/fs/ufs/store_dir_ufs.cc 1970-01-01 00:00:00 +0000 @@ -1,1471 +0,0 @@ - -/* - * $Id$ - * - * DEBUG: section 47 Store Directory Routines - * AUTHOR: Duane Wessels - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - -#include "squid-old.h" -#include "Store.h" -#include "fde.h" -#include "ufscommon.h" -#include "StoreSwapLogData.h" -#include "ConfigOption.h" -#include "DiskIO/DiskIOStrategy.h" -#include "DiskIO/DiskIOModule.h" -#include "FileMap.h" -#include "Parsing.h" -#include "SquidMath.h" -#include "SquidTime.h" -#include "StatCounters.h" -#include "SwapDir.h" -#include "swap_log_op.h" - -int UFSSwapDir::NumberOfUFSDirs = 0; -int *UFSSwapDir::UFSDirToGlobalDirMapping = NULL; - -/* - * storeUfsDirCheckObj - * - * This routine is called by storeDirSelectSwapDir to see if the given - * object is able to be stored on this filesystem. UFS filesystems will - * happily store anything as long as the LRU time isn't too small. - */ -bool -UFSSwapDir::canStore(const StoreEntry &e, int64_t diskSpaceNeeded, int &load) const -{ - if (!SwapDir::canStore(e, diskSpaceNeeded, load)) - return false; - - if (IO->shedLoad()) - return false; - - load = IO->load(); - return true; -} - - -/* ========== LOCAL FUNCTIONS ABOVE, GLOBAL FUNCTIONS BELOW ========== */ - -void -UFSSwapDir::parseSizeL1L2() -{ - int i = GetInteger(); - if (i <= 0) - fatal("UFSSwapDir::parseSizeL1L2: invalid size value"); - - const uint64_t size = static_cast(i) << 20; // MBytes to Bytes - - /* just reconfigure it */ - if (reconfiguring) { - if (size == maxSize()) - debugs(3, 2, "Cache dir '" << path << "' size remains unchanged at " << i << " MB"); - else - debugs(3, 1, "Cache dir '" << path << "' size changed to " << i << " MB"); - } - - max_size = size; - - l1 = GetInteger(); - - if (l1 <= 0) - fatal("UFSSwapDir::parseSizeL1L2: invalid level 1 directories value"); - - l2 = GetInteger(); - - if (l2 <= 0) - fatal("UFSSwapDir::parseSizeL1L2: invalid level 2 directories value"); -} - -/* - * storeUfsDirReconfigure - * - * This routine is called when the given swapdir needs reconfiguring - */ - -void -UFSSwapDir::reconfigure() -{ - parseSizeL1L2(); - parseOptions(1); -} - -/* - * storeUfsDirParse - * - * Called when a *new* fs is being setup. - */ -void -UFSSwapDir::parse (int anIndex, char *aPath) -{ - index = anIndex; - path = xstrdup(aPath); - - parseSizeL1L2(); - - /* Initialise replacement policy stuff */ - repl = createRemovalPolicy(Config.replPolicy); - - parseOptions(0); -} - -void -UFSSwapDir::changeIO(DiskIOModule *module) -{ - DiskIOStrategy *anIO = module->createStrategy(); - safe_free(ioType); - ioType = xstrdup(module->type()); - - delete IO->io; - IO->io = anIO; - /* Change the IO Options */ - - if (currentIOOptions && currentIOOptions->options.size() > 2) - delete currentIOOptions->options.pop_back(); - - /* TODO: factor out these 4 lines */ - ConfigOption *ioOptions = IO->io->getOptionTree(); - - if (ioOptions) - currentIOOptions->options.push_back(ioOptions); -} - -bool -UFSSwapDir::optionIOParse(char const *option, const char *value, int isaReconfig) -{ - if (strcmp(option, "IOEngine") != 0) - return false; - - if (isaReconfig) - /* silently ignore this */ - return true; - - if (!value) - self_destruct(); - - DiskIOModule *module = DiskIOModule::Find(value); - - if (!module) - self_destruct(); - - changeIO(module); - - return true; -} - -void -UFSSwapDir::optionIODump(StoreEntry * e) const -{ - storeAppendPrintf(e, " IOEngine=%s", ioType); -} - -ConfigOption * -UFSSwapDir::getOptionTree() const -{ - ConfigOption *parentResult = SwapDir::getOptionTree(); - - if (currentIOOptions == NULL) - currentIOOptions = new ConfigOptionVector(); - - currentIOOptions->options.push_back(parentResult); - - currentIOOptions->options.push_back(new ConfigOptionAdapter(*const_cast(this), &UFSSwapDir::optionIOParse, &UFSSwapDir::optionIODump)); - - if (ConfigOption *ioOptions = IO->io->getOptionTree()) - currentIOOptions->options.push_back(ioOptions); - - ConfigOption* result = currentIOOptions; - - currentIOOptions = NULL; - - return result; -} - -/* - * Initial setup / end destruction - */ -void -UFSSwapDir::init() -{ - debugs(47, 3, "Initialising UFS SwapDir engine."); - /* Parsing must be finished by now - force to NULL, don't delete */ - currentIOOptions = NULL; - static int started_clean_event = 0; - static const char *errmsg = - "\tFailed to verify one of the swap directories, Check cache.log\n" - "\tfor details. Run 'squid -z' to create swap directories\n" - "\tif needed, or if running Squid for the first time."; - IO->init(); - - if (verifyCacheDirs()) - fatal(errmsg); - - openLog(); - - rebuild(); - - if (!started_clean_event) { - eventAdd("UFS storeDirClean", CleanEvent, NULL, 15.0, 1); - started_clean_event = 1; - } - - (void) storeDirGetBlkSize(path, &fs.blksize); -} - -void -UFSSwapDir::create() -{ - debugs(47, 3, "Creating swap space in " << path); - createDirectory(path, 0); - createSwapSubDirs(); -} - -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) -{ - /* modulename is only set to disk modules that are built, by configure, - * so the Find call should never return NULL here. - */ - IO = new UFSStrategy(DiskIOModule::Find(anIOType)->createStrategy()); -} - -UFSSwapDir::~UFSSwapDir() -{ - if (swaplog_fd > -1) { - file_close(swaplog_fd); - swaplog_fd = -1; - } - - delete map; - - if (IO) - delete IO; - - IO = NULL; - - safe_free(ioType); -} - -void -UFSSwapDir::dumpEntry(StoreEntry &e) const -{ - debugs(47, 0, "UFSSwapDir::dumpEntry: FILENO "<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << e.swap_filen); - debugs(47, 0, "UFSSwapDir::dumpEntry: PATH " << fullPath(e.swap_filen, NULL) ); - e.dump(0); -} - -/* - * UFSSwapDir::doubleCheck - * - * This is called by storeCleanup() if -S was given on the command line. - */ -bool -UFSSwapDir::doubleCheck(StoreEntry & e) -{ - - struct stat sb; - - if (::stat(fullPath(e.swap_filen, NULL), &sb) < 0) { - debugs(47, 0, "UFSSwapDir::doubleCheck: MISSING SWAP FILE"); - dumpEntry(e); - return true; - } - - if ((off_t)e.swap_file_sz != sb.st_size) { - debugs(47, 0, "UFSSwapDir::doubleCheck: SIZE MISMATCH"); - debugs(47, 0, "UFSSwapDir::doubleCheck: ENTRY SIZE: " << e.swap_file_sz << ", FILE SIZE: " << sb.st_size); - dumpEntry(e); - return true; - } - - return false; -} - -void -UFSSwapDir::statfs(StoreEntry & sentry) const -{ - int totl_kb = 0; - int free_kb = 0; - int totl_in = 0; - int free_in = 0; - int x; - storeAppendPrintf(&sentry, "First level subdirectories: %d\n", l1); - storeAppendPrintf(&sentry, "Second level subdirectories: %d\n", l2); - storeAppendPrintf(&sentry, "Maximum Size: %" PRIu64 " KB\n", maxSize() >> 10); - storeAppendPrintf(&sentry, "Current Size: %.2f KB\n", currentSize() / 1024.0); - storeAppendPrintf(&sentry, "Percent Used: %0.2f%%\n", - Math::doublePercent(currentSize(), maxSize())); - storeAppendPrintf(&sentry, "Filemap bits in use: %d of %d (%d%%)\n", - map->numFilesInMap(), map->capacity(), - Math::intPercent(map->numFilesInMap(), map->capacity())); - x = storeDirGetUFSStats(path, &totl_kb, &free_kb, &totl_in, &free_in); - - if (0 == x) { - storeAppendPrintf(&sentry, "Filesystem Space in use: %d/%d KB (%d%%)\n", - totl_kb - free_kb, - totl_kb, - Math::intPercent(totl_kb - free_kb, totl_kb)); - storeAppendPrintf(&sentry, "Filesystem Inodes in use: %d/%d (%d%%)\n", - totl_in - free_in, - totl_in, - Math::intPercent(totl_in - free_in, totl_in)); - } - - storeAppendPrintf(&sentry, "Flags:"); - - if (flags.selected) - storeAppendPrintf(&sentry, " SELECTED"); - - if (flags.read_only) - storeAppendPrintf(&sentry, " READ-ONLY"); - - storeAppendPrintf(&sentry, "\n"); - - IO->statfs(sentry); -} - -void -UFSSwapDir::maintain() -{ - /* We can't delete objects while rebuilding swap */ - - /* XXX FIXME each store should start maintaining as it comes online. */ - - if (StoreController::store_dirs_rebuilding) - return; - - StoreEntry *e = NULL; - - int removed = 0; - - RemovalPurgeWalker *walker; - - double f = (double) (currentSize() - minSize()) / (maxSize() - minSize()); - - f = f < 0.0 ? 0.0 : f > 1.0 ? 1.0 : f; - - int max_scan = (int) (f * 400.0 + 100.0); - - int max_remove = (int) (f * 70.0 + 10.0); - - /* - * This is kinda cheap, but so we need this priority hack? - */ - - debugs(47, 3, "storeMaintainSwapSpace: f=" << f << ", max_scan=" << max_scan << ", max_remove=" << max_remove ); - - walker = repl->PurgeInit(repl, max_scan); - - while (1) { - if (currentSize() < minSize()) - break; - - if (removed >= max_remove) - break; - - e = walker->Next(walker); - - if (!e) - break; /* no more objects */ - - ++removed; - - e->release(); - } - - walker->Done(walker); - debugs(47, (removed ? 2 : 3), "UFSSwapDir::maintain: " << path << - " removed " << removed << "/" << max_remove << " f=" << - std::setprecision(4) << f << " max_scan=" << max_scan); -} - -/* - * UFSSwapDir::reference - * - * This routine is called whenever an object is referenced, so we can - * maintain replacement information within the storage fs. - */ -void -UFSSwapDir::reference(StoreEntry &e) -{ - debugs(47, 3, "UFSSwapDir::reference: referencing " << &e << " " << e.swap_dirn << "/" << e.swap_filen); - - if (repl->Referenced) - repl->Referenced(repl, &e, &e.repl); -} - -/* - * UFSSwapDir::dereference - * This routine is called whenever the last reference to an object is - * removed, to maintain replacement information within the storage fs. - */ -bool -UFSSwapDir::dereference(StoreEntry & e) -{ - debugs(47, 3, "UFSSwapDir::dereference: referencing " << &e << " " << e.swap_dirn << "/" << e.swap_filen); - - if (repl->Dereferenced) - repl->Dereferenced(repl, &e, &e.repl); - - return true; // keep e in the global store_table -} - -StoreIOState::Pointer -UFSSwapDir::createStoreIO(StoreEntry &e, StoreIOState::STFNCB * file_callback, StoreIOState::STIOCB * aCallback, void *callback_data) -{ - return IO->create (this, &e, file_callback, aCallback, callback_data); -} - -StoreIOState::Pointer -UFSSwapDir::openStoreIO(StoreEntry &e, StoreIOState::STFNCB * file_callback, StoreIOState::STIOCB * aCallback, void *callback_data) -{ - return IO->open (this, &e, file_callback, aCallback, callback_data); -} - -int -UFSSwapDir::mapBitTest(sfileno filn) -{ - return map->testBit(filn); -} - -void -UFSSwapDir::mapBitSet(sfileno filn) -{ - map->setBit(filn); -} - -void -UFSSwapDir::mapBitReset(sfileno filn) -{ - /* - * We have to test the bit before calling clearBit as - * it doesn't do bounds checking and blindly assumes - * filn is a valid file number, but it might not be because - * the map is dynamic in size. Also clearing an already clear - * bit puts the map counter of-of-whack. - */ - - if (map->testBit(filn)) - map->clearBit(filn); -} - -int -UFSSwapDir::mapBitAllocate() -{ - int fn; - fn = map->allocate(suggest); - map->setBit(fn); - suggest = fn + 1; - return fn; -} - -char * -UFSSwapDir::swapSubDir(int subdirn)const -{ - LOCAL_ARRAY(char, fullfilename, MAXPATHLEN); - assert(0 <= subdirn && subdirn < l1); - snprintf(fullfilename, MAXPATHLEN, "%s/%02X", path, subdirn); - return fullfilename; -} - -int -UFSSwapDir::createDirectory(const char *aPath, int should_exist) -{ - int created = 0; - - struct stat st; - getCurrentTime(); - - if (0 == ::stat(aPath, &st)) { - if (S_ISDIR(st.st_mode)) { - debugs(47, (should_exist ? 3 : 1), aPath << " exists"); - } else { - fatalf("Swap directory %s is not a directory.", aPath); - } - -#if _SQUID_MSWIN_ - - } else if (0 == mkdir(aPath)) { -#else - - } else if (0 == mkdir(aPath, 0755)) { -#endif - debugs(47, (should_exist ? 1 : 3), aPath << " created"); - created = 1; - } else { - fatalf("Failed to make swap directory %s: %s", - aPath, xstrerror()); - } - - return created; -} - -bool -UFSSwapDir::pathIsDirectory(const char *aPath)const -{ - - struct stat sb; - - if (::stat(aPath, &sb) < 0) { - debugs(47, 0, "" << aPath << ": " << xstrerror()); - return false; - } - - if (S_ISDIR(sb.st_mode) == 0) { - debugs(47, 0, "" << aPath << " is not a directory"); - return false; - } - - return true; -} - -/* - * This function is called by commonUfsDirInit(). If this returns < 0, - * then Squid exits, complains about swap directories not - * existing, and instructs the admin to run 'squid -z' - */ -bool -UFSSwapDir::verifyCacheDirs() -{ - if (!pathIsDirectory(path)) - return true; - - for (int j = 0; j < l1; ++j) { - char const *aPath = swapSubDir(j); - - if (!pathIsDirectory(aPath)) - return true; - } - - return false; -} - -void -UFSSwapDir::createSwapSubDirs() -{ - LOCAL_ARRAY(char, name, MAXPATHLEN); - - for (int i = 0; i < l1; ++i) { - snprintf(name, MAXPATHLEN, "%s/%02X", path, i); - - int should_exist; - - if (createDirectory(name, 0)) - should_exist = 0; - else - should_exist = 1; - - debugs(47, 1, "Making directories in " << name); - - for (int k = 0; k < l2; ++k) { - snprintf(name, MAXPATHLEN, "%s/%02X/%02X", path, i, k); - createDirectory(name, should_exist); - } - } -} - -char * -UFSSwapDir::logFile(char const *ext) const -{ - LOCAL_ARRAY(char, lpath, MAXPATHLEN); - LOCAL_ARRAY(char, pathtmp, MAXPATHLEN); - LOCAL_ARRAY(char, digit, 32); - char *pathtmp2; - - if (Config.Log.swap) { - xstrncpy(pathtmp, path, MAXPATHLEN - 64); - pathtmp2 = pathtmp; - - while ((pathtmp2 = strchr(pathtmp2, '/')) != NULL) - *pathtmp2 = '.'; - - while (strlen(pathtmp) && pathtmp[strlen(pathtmp) - 1] == '.') - pathtmp[strlen(pathtmp) - 1] = '\0'; - - for (pathtmp2 = pathtmp; *pathtmp2 == '.'; ++pathtmp2); - snprintf(lpath, MAXPATHLEN - 64, Config.Log.swap, pathtmp2); - - if (strncmp(lpath, Config.Log.swap, MAXPATHLEN - 64) == 0) { - strcat(lpath, "."); - snprintf(digit, 32, "%02d", index); - strncat(lpath, digit, 3); - } - } else { - xstrncpy(lpath, path, MAXPATHLEN - 64); - strcat(lpath, "/swap.state"); - } - - if (ext) - strncat(lpath, ext, 16); - - return lpath; -} - -void -UFSSwapDir::openLog() -{ - char *logPath; - logPath = logFile(); - swaplog_fd = file_open(logPath, O_WRONLY | O_CREAT | O_BINARY); - - if (swaplog_fd < 0) { - debugs(50, 1, "" << logPath << ": " << xstrerror()); - fatal("commonUfsDirOpenSwapLog: Failed to open swap log."); - } - - debugs(50, 3, "Cache Dir #" << index << " log opened on FD " << swaplog_fd); - - if (0 == NumberOfUFSDirs) - assert(NULL == UFSDirToGlobalDirMapping); - - ++NumberOfUFSDirs; - - assert(NumberOfUFSDirs <= Config.cacheSwap.n_configured); -} - -void -UFSSwapDir::closeLog() -{ - if (swaplog_fd < 0) /* not open */ - return; - - file_close(swaplog_fd); - - debugs(47, 3, "Cache Dir #" << index << " log closed on FD " << swaplog_fd); - - swaplog_fd = -1; - - --NumberOfUFSDirs; - - assert(NumberOfUFSDirs >= 0); - - if (0 == NumberOfUFSDirs) - safe_free(UFSDirToGlobalDirMapping); -} - -bool -UFSSwapDir::validL1(int anInt) const -{ - return anInt < l1; -} - -bool -UFSSwapDir::validL2(int anInt) const -{ - return anInt < l2; -} - -/* Add a new object to the cache with empty memory copy and pointer to disk - * use to rebuild store from disk. */ -StoreEntry * -UFSSwapDir::addDiskRestore(const cache_key * key, - sfileno file_number, - uint64_t swap_file_sz, - time_t expires, - time_t timestamp, - time_t lastref, - time_t lastmod, - uint32_t refcount, - uint16_t newFlags, - int clean) -{ - StoreEntry *e = NULL; - debugs(47, 5, "commonUfsAddDiskRestore: " << storeKeyText(key) << - ", fileno="<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << file_number); - /* if you call this you'd better be sure file_number is not - * already in use! */ - e = new StoreEntry(); - e->store_status = STORE_OK; - e->setMemStatus(NOT_IN_MEMORY); - e->swap_status = SWAPOUT_DONE; - e->swap_filen = file_number; - e->swap_dirn = index; - e->swap_file_sz = swap_file_sz; - e->lock_count = 0; - e->lastref = lastref; - e->timestamp = timestamp; - e->expires = expires; - e->lastmod = lastmod; - e->refcount = refcount; - e->flags = newFlags; - EBIT_SET(e->flags, ENTRY_CACHABLE); - EBIT_CLR(e->flags, RELEASE_REQUEST); - EBIT_CLR(e->flags, KEY_PRIVATE); - e->ping_status = PING_NONE; - EBIT_CLR(e->flags, ENTRY_VALIDATED); - mapBitSet(e->swap_filen); - cur_size += fs.blksize * sizeInBlocks(e->swap_file_sz); - ++n_disk_objects; - e->hashInsert(key); /* do it after we clear KEY_PRIVATE */ - replacementAdd (e); - return e; -} - -void -UFSSwapDir::undoAddDiskRestore(StoreEntry *e) -{ - debugs(47, 5, HERE << *e); - replacementRemove(e); // checks swap_dirn so do it before we invalidate it - // Do not unlink the file as it might be used by a subsequent entry. - mapBitReset(e->swap_filen); - e->swap_filen = -1; - e->swap_dirn = -1; - cur_size -= fs.blksize * sizeInBlocks(e->swap_file_sz); - --n_disk_objects; -} - -void -UFSSwapDir::rebuild() -{ - ++StoreController::store_dirs_rebuilding; - eventAdd("storeRebuild", RebuildState::RebuildStep, new RebuildState(this), 0.0, 1); -} - -void -UFSSwapDir::closeTmpSwapLog() -{ - char *swaplog_path = xstrdup(logFile(NULL)); - char *new_path = xstrdup(logFile(".new")); - int fd; - file_close(swaplog_fd); - - if (xrename(new_path, swaplog_path) < 0) { - debugs(50, DBG_IMPORTANT, "ERROR: " << swaplog_path << ": " << xstrerror()); - fatalf("Failed to rename log file %s to %s.new", swaplog_path, swaplog_path); - } - - fd = file_open(swaplog_path, O_WRONLY | O_CREAT | O_BINARY); - - if (fd < 0) { - debugs(50, DBG_IMPORTANT, "ERROR: " << swaplog_path << ": " << xstrerror()); - fatalf("Failed to open swap log %s", swaplog_path); - } - - safe_free(swaplog_path); - safe_free(new_path); - swaplog_fd = fd; - debugs(47, 3, "Cache Dir #" << index << " log opened on FD " << fd); -} - -FILE * -UFSSwapDir::openTmpSwapLog(int *clean_flag, int *zero_flag) -{ - char *swaplog_path = xstrdup(logFile(NULL)); - char *clean_path = xstrdup(logFile(".last-clean")); - char *new_path = xstrdup(logFile(".new")); - - struct stat log_sb; - - struct stat clean_sb; - FILE *fp; - int fd; - - if (::stat(swaplog_path, &log_sb) < 0) { - debugs(47, 1, "Cache Dir #" << index << ": No log file"); - safe_free(swaplog_path); - safe_free(clean_path); - safe_free(new_path); - return NULL; - } - - *zero_flag = log_sb.st_size == 0 ? 1 : 0; - /* close the existing write-only FD */ - - if (swaplog_fd >= 0) - file_close(swaplog_fd); - - /* open a write-only FD for the new log */ - fd = file_open(new_path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY); - - if (fd < 0) { - debugs(50, 1, "" << new_path << ": " << xstrerror()); - fatal("storeDirOpenTmpSwapLog: Failed to open swap log."); - } - - swaplog_fd = fd; - - { - const StoreSwapLogHeader header; - MemBuf buf; - buf.init(header.record_size, header.record_size); - buf.append(reinterpret_cast(&header), sizeof(header)); - // Pad to keep in sync with UFSSwapDir::writeCleanStart(). - memset(buf.space(), 0, header.gapSize()); - buf.appended(header.gapSize()); - file_write(swaplog_fd, -1, buf.content(), buf.contentSize(), - NULL, NULL, buf.freeFunc()); - } - - /* open a read-only stream of the old log */ - fp = fopen(swaplog_path, "rb"); - - if (fp == NULL) { - debugs(50, 0, "" << swaplog_path << ": " << xstrerror()); - fatal("Failed to open swap log for reading"); - } - - memset(&clean_sb, '\0', sizeof(struct stat)); - - if (::stat(clean_path, &clean_sb) < 0) - *clean_flag = 0; - else if (clean_sb.st_mtime < log_sb.st_mtime) - *clean_flag = 0; - else - *clean_flag = 1; - - safeunlink(clean_path, 1); - - safe_free(swaplog_path); - - safe_free(clean_path); - - safe_free(new_path); - - return fp; -} - -class UFSCleanLog : public SwapDir::CleanLog -{ - -public: - UFSCleanLog(SwapDir *); - virtual const StoreEntry *nextEntry(); - virtual void write(StoreEntry const &); - char *cur; - char *newLog; - char *cln; - char *outbuf; - off_t outbuf_offset; - int fd; - RemovalPolicyWalker *walker; - SwapDir *sd; -}; - -#define CLEAN_BUF_SZ 16384 - - -UFSCleanLog::UFSCleanLog(SwapDir *aSwapDir) : cur(NULL),newLog(NULL),cln(NULL),outbuf(NULL), - outbuf_offset(0), fd(-1),walker(NULL), sd(aSwapDir) -{} - -/* - * Begin the process to write clean cache state. For AUFS this means - * opening some log files and allocating write buffers. Return 0 if - * we succeed, and assign the 'func' and 'data' return pointers. - */ -int -UFSSwapDir::writeCleanStart() -{ - UFSCleanLog *state = new UFSCleanLog(this); - StoreSwapLogHeader header; -#if HAVE_FCHMOD - - struct stat sb; -#endif - - cleanLog = NULL; - state->newLog = xstrdup(logFile(".clean")); - state->fd = file_open(state->newLog, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY); - - if (state->fd < 0) { - xfree(state->newLog); - delete state; - return -1; - } - - state->cur = xstrdup(logFile(NULL)); - state->cln = xstrdup(logFile(".last-clean")); - state->outbuf = (char *)xcalloc(CLEAN_BUF_SZ, 1); - state->outbuf_offset = 0; - /*copy the header */ - memcpy(state->outbuf, &header, sizeof(StoreSwapLogHeader)); - // Leave a gap to keep in sync with UFSSwapDir::openTmpSwapLog(). - memset(state->outbuf + sizeof(StoreSwapLogHeader), 0, header.gapSize()); - state->outbuf_offset += header.record_size; - - state->walker = repl->WalkInit(repl); - ::unlink(state->cln); - debugs(47, 3, "storeDirWriteCleanLogs: opened " << state->newLog << ", FD " << state->fd); -#if HAVE_FCHMOD - - if (::stat(state->cur, &sb) == 0) - fchmod(state->fd, sb.st_mode); - -#endif - - - cleanLog = state; - return 0; -} - -/* - * Get the next entry that is a candidate for clean log writing - */ -const StoreEntry * -UFSCleanLog::nextEntry() -{ - const StoreEntry *entry = NULL; - - if (walker) - entry = walker->Next(walker); - - return entry; -} - -/* - * "write" an entry to the clean log file. - */ -void -UFSCleanLog::write(StoreEntry const &e) -{ - StoreSwapLogData s; - static size_t ss = sizeof(StoreSwapLogData); - s.op = (char) SWAP_LOG_ADD; - s.swap_filen = e.swap_filen; - s.timestamp = e.timestamp; - s.lastref = e.lastref; - s.expires = e.expires; - s.lastmod = e.lastmod; - s.swap_file_sz = e.swap_file_sz; - s.refcount = e.refcount; - s.flags = e.flags; - memcpy(&s.key, e.key, SQUID_MD5_DIGEST_LENGTH); - s.finalize(); - memcpy(outbuf + outbuf_offset, &s, ss); - outbuf_offset += ss; - /* buffered write */ - - if (outbuf_offset + ss >= CLEAN_BUF_SZ) { - if (FD_WRITE_METHOD(fd, outbuf, outbuf_offset) < 0) { - /* XXX This error handling should probably move up to the caller */ - debugs(50, 0, "storeDirWriteCleanLogs: " << newLog << ": write: " << xstrerror()); - debugs(50, 0, "storeDirWriteCleanLogs: Current swap logfile not replaced."); - file_close(fd); - fd = -1; - unlink(newLog); - sd->cleanLog = NULL; - delete this; - return; - } - - outbuf_offset = 0; - } -} - -void -UFSSwapDir::writeCleanDone() -{ - UFSCleanLog *state = (UFSCleanLog *)cleanLog; - int fd; - - if (NULL == state) - return; - - if (state->fd < 0) - return; - - state->walker->Done(state->walker); - - if (FD_WRITE_METHOD(state->fd, state->outbuf, state->outbuf_offset) < 0) { - debugs(50, 0, "storeDirWriteCleanLogs: " << state->newLog << ": write: " << xstrerror()); - debugs(50, 0, "storeDirWriteCleanLogs: Current swap logfile not replaced."); - file_close(state->fd); - state->fd = -1; - ::unlink(state->newLog); - } - - safe_free(state->outbuf); - /* - * You can't rename open files on Microsoft "operating systems" - * so we have to close before renaming. - */ - closeLog(); - /* save the fd value for a later test */ - fd = state->fd; - /* rename */ - - if (state->fd >= 0) { -#if _SQUID_OS2_ || _SQUID_WINDOWS_ - file_close(state->fd); - state->fd = -1; -#endif - - xrename(state->newLog, state->cur); - } - - /* touch a timestamp file if we're not still validating */ - if (StoreController::store_dirs_rebuilding) - (void) 0; - else if (fd < 0) - (void) 0; - else - file_close(file_open(state->cln, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY)); - - /* close */ - safe_free(state->cur); - - safe_free(state->newLog); - - safe_free(state->cln); - - if (state->fd >= 0) - file_close(state->fd); - - state->fd = -1; - - delete state; - - cleanLog = NULL; -} - -static void -FreeObject(void *address) -{ - StoreSwapLogData *anObject = static_cast (address); - delete anObject; -} - -void -UFSSwapDir::logEntry(const StoreEntry & e, int op) const -{ - StoreSwapLogData *s = new StoreSwapLogData; - s->op = (char) op; - s->swap_filen = e.swap_filen; - s->timestamp = e.timestamp; - s->lastref = e.lastref; - s->expires = e.expires; - s->lastmod = e.lastmod; - s->swap_file_sz = e.swap_file_sz; - s->refcount = e.refcount; - s->flags = e.flags; - memcpy(s->key, e.key, SQUID_MD5_DIGEST_LENGTH); - s->finalize(); - file_write(swaplog_fd, - -1, - s, - sizeof(StoreSwapLogData), - NULL, - NULL, - FreeObject); -} - -static QS rev_int_sort; -static int -rev_int_sort(const void *A, const void *B) -{ - const int *i1 = (const int *)A; - const int *i2 = (const int *)B; - return *i2 - *i1; -} - -int -UFSSwapDir::DirClean(int swap_index) -{ - DIR *dir_pointer = NULL; - - LOCAL_ARRAY(char, p1, MAXPATHLEN + 1); - LOCAL_ARRAY(char, p2, MAXPATHLEN + 1); - - int files[20]; - int swapfileno; - int fn; /* same as swapfileno, but with dirn bits set */ - int n = 0; - int k = 0; - int N0, N1, N2; - int D0, D1, D2; - UFSSwapDir *SD; - N0 = NumberOfUFSDirs; - D0 = UFSDirToGlobalDirMapping[swap_index % N0]; - SD = dynamic_cast(INDEXSD(D0)); - assert (SD); - N1 = SD->l1; - D1 = (swap_index / N0) % N1; - N2 = SD->l2; - D2 = ((swap_index / N0) / N1) % N2; - snprintf(p1, MAXPATHLEN, "%s/%02X/%02X", - SD->path, D1, D2); - debugs(36, 3, "storeDirClean: Cleaning directory " << p1); - dir_pointer = opendir(p1); - - if (dir_pointer == NULL) { - if (errno == ENOENT) { - debugs(36, 0, "storeDirClean: WARNING: Creating " << p1); -#if _SQUID_MSWIN_ - - if (mkdir(p1) == 0) -#else - - if (mkdir(p1, 0777) == 0) -#endif - - return 0; - } - - debugs(50, 0, "storeDirClean: " << p1 << ": " << xstrerror()); - safeunlink(p1, 1); - return 0; - } - - dirent_t *de; - while ((de = readdir(dir_pointer)) != NULL && k < 20) { - if (sscanf(de->d_name, "%X", &swapfileno) != 1) - continue; - - fn = swapfileno; /* XXX should remove this cruft ! */ - - if (SD->validFileno(fn, 1)) - if (SD->mapBitTest(fn)) - if (UFSSwapDir::FilenoBelongsHere(fn, D0, D1, D2)) - continue; - - files[k] = swapfileno; - ++k; - } - - closedir(dir_pointer); - - if (k == 0) - return 0; - - qsort(files, k, sizeof(int), rev_int_sort); - - if (k > 10) - k = 10; - - for (n = 0; n < k; ++n) { - debugs(36, 3, "storeDirClean: Cleaning file "<< std::setfill('0') << std::hex << std::uppercase << std::setw(8) << files[n]); - snprintf(p2, MAXPATHLEN + 1, "%s/%08X", p1, files[n]); - safeunlink(p2, 0); - ++statCounter.swap.files_cleaned; - } - - debugs(36, 3, "Cleaned " << k << " unused files from " << p1); - return k; -} - -void -UFSSwapDir::CleanEvent(void *unused) -{ - static int swap_index = 0; - int i; - int j = 0; - int n = 0; - /* - * Assert that there are UFS cache_dirs configured, otherwise - * we should never be called. - */ - assert(NumberOfUFSDirs); - - if (NULL == UFSDirToGlobalDirMapping) { - SwapDir *sd; - /* - * Initialize the little array that translates UFS cache_dir - * number into the Config.cacheSwap.swapDirs array index. - */ - UFSDirToGlobalDirMapping = (int *)xcalloc(NumberOfUFSDirs, sizeof(*UFSDirToGlobalDirMapping)); - - for (i = 0, n = 0; i < Config.cacheSwap.n_configured; ++i) { - /* This is bogus, the controller should just clean each instance once */ - sd = dynamic_cast (INDEXSD(i)); - - if (!UFSSwapDir::IsUFSDir(sd)) - continue; - - UFSSwapDir *usd = dynamic_cast(sd); - - assert (usd); - - UFSDirToGlobalDirMapping[n] = i; - ++n; - - j += (usd->l1 * usd->l2); - } - - assert(n == NumberOfUFSDirs); - /* - * Start the commonUfsDirClean() swap_index with a random - * value. j equals the total number of UFS level 2 - * swap directories - */ - swap_index = (int) (squid_random() % j); - } - - /* if the rebuild is finished, start cleaning directories. */ - if (0 == StoreController::store_dirs_rebuilding) { - n = DirClean(swap_index); - ++swap_index; - } - - eventAdd("storeDirClean", CleanEvent, NULL, - 15.0 * exp(-0.25 * n), 1); -} - -int -UFSSwapDir::IsUFSDir(SwapDir * sd) -{ - UFSSwapDir *mySD = dynamic_cast(sd); - return mySD ? 1 : 0 ; -} - -/* - * Does swapfile number 'fn' belong in cachedir #F0, - * level1 dir #F1, level2 dir #F2? - * XXX: this is broken - it assumes all cache dirs use the same - * l1 and l2 scheme. -RBC 20021215. Partial fix is in place - - * if not UFSSwapDir return 0; - */ -int -UFSSwapDir::FilenoBelongsHere(int fn, int F0, int F1, int F2) -{ - int D1, D2; - int L1, L2; - int filn = fn; - assert(F0 < Config.cacheSwap.n_configured); - assert (UFSSwapDir::IsUFSDir (dynamic_cast(INDEXSD(F0)))); - UFSSwapDir *sd = dynamic_cast(INDEXSD(F0)); - - if (!sd) - return 0; - - L1 = sd->l1; - - L2 = sd->l2; - - D1 = ((filn / L2) / L2) % L1; - - if (F1 != D1) - return 0; - - D2 = (filn / L2) % L2; - - if (F2 != D2) - return 0; - - return 1; -} - - -int -UFSSwapDir::validFileno(sfileno filn, int flag) const -{ - if (filn < 0) - return 0; - - /* - * If flag is set it means out-of-range file number should - * be considered invalid. - */ - if (flag) - if (filn > map->capacity()) - return 0; - - return 1; -} - - - -/* - * UFSSwapDir::unlinkFile - * - * This routine unlinks a file and pulls it out of the bitmap. - * It used to be in commonUfsUnlink(), however an interface change - * forced this bit of code here. Eeek. - */ -void -UFSSwapDir::unlinkFile(sfileno f) -{ - debugs(79, 3, "UFSSwapDir::unlinkFile: unlinking fileno " << std::setfill('0') << - std::hex << std::uppercase << std::setw(8) << f << " '" << - fullPath(f,NULL) << "'"); - /* commonUfsDirMapBitReset(this, f); */ - IO->unlinkFile(fullPath(f,NULL)); -} - -bool -UFSSwapDir::unlinkdUseful() const -{ - // unlinkd may be useful only in workers - return IamWorkerProcess() && IO->io->unlinkdUseful(); -} - -void -UFSSwapDir::unlink(StoreEntry & e) -{ - debugs(79, 3, "storeUfsUnlink: dirno " << index << ", fileno "<< - std::setfill('0') << std::hex << std::uppercase << std::setw(8) << e.swap_filen); - if (e.swap_status == SWAPOUT_DONE) { - cur_size -= fs.blksize * sizeInBlocks(e.swap_file_sz); - --n_disk_objects; - } - replacementRemove(&e); - mapBitReset(e.swap_filen); - UFSSwapDir::unlinkFile(e.swap_filen); -} - -/* - * Add and remove the given StoreEntry from the replacement policy in - * use. - */ - -void -UFSSwapDir::replacementAdd(StoreEntry * e) -{ - debugs(47, 4, "UFSSwapDir::replacementAdd: added node " << e << " to dir " << index); - repl->Add(repl, e, &e->repl); -} - - -void -UFSSwapDir::replacementRemove(StoreEntry * e) -{ - StorePointer SD; - - if (e->swap_dirn < 0) - return; - - SD = INDEXSD(e->swap_dirn); - - assert (dynamic_cast(SD.getRaw()) == this); - - debugs(47, 4, "UFSSwapDir::replacementRemove: remove node " << e << " from dir " << index); - - repl->Remove(repl, e, &e->repl); -} - -void -UFSSwapDir::dump(StoreEntry & entry) const -{ - storeAppendPrintf(&entry, " %" PRIu64 " %d %d", maxSize() >> 20, l1, l2); - dumpOptions(&entry); -} - -char * -UFSSwapDir::fullPath(sfileno filn, char *fullpath) const -{ - LOCAL_ARRAY(char, fullfilename, MAXPATHLEN); - int L1 = l1; - int L2 = l2; - - if (!fullpath) - fullpath = fullfilename; - - fullpath[0] = '\0'; - - snprintf(fullpath, MAXPATHLEN, "%s/%02X/%02X/%08X", - path, - ((filn / L2) / L2) % L1, - (filn / L2) % L2, - filn); - - return fullpath; -} - -int -UFSSwapDir::callback() -{ - return IO->callback(); -} - -void -UFSSwapDir::sync() -{ - IO->sync(); -} - -void -UFSSwapDir::swappedOut(const StoreEntry &e) -{ - cur_size += fs.blksize * sizeInBlocks(e.swap_file_sz); - ++n_disk_objects; -} - -StoreSearch * -UFSSwapDir::search(String const url, HttpRequest *request) -{ - if (url.size()) - fatal ("Cannot search by url yet\n"); - - return new StoreSearchUFS (this); -} - -CBDATA_CLASS_INIT(StoreSearchUFS); -StoreSearchUFS::StoreSearchUFS(RefCount aSwapDir) : sd(aSwapDir), walker (sd->repl->WalkInit(sd->repl)), current (NULL), _done (false) -{} - -/* do not link -StoreSearchUFS::StoreSearchUFS(StoreSearchUFS const &); -*/ - -StoreSearchUFS::~StoreSearchUFS() -{ - walker->Done(walker); - walker = NULL; -} - -void -StoreSearchUFS::next(void (aCallback)(void *cbdata), void *aCallbackArgs) -{ - next(); - aCallback(aCallbackArgs); -} - -bool -StoreSearchUFS::next() -{ - /* the walker API doesn't make sense. the store entries referred to are already readwrite - * from their hash table entries - */ - - if (walker) - current = const_cast(walker->Next(walker)); - - if (current == NULL) - _done = true; - - return current != NULL; -} - -bool -StoreSearchUFS::error() const -{ - return false; -} - -bool -StoreSearchUFS::isDone() const -{ - return _done; -} - -StoreEntry * -StoreSearchUFS::currentItem() -{ - return current; -} === removed file 'src/fs/ufs/ufscommon.cc' --- src/fs/ufs/ufscommon.cc 2012-07-23 07:02:06 +0000 +++ src/fs/ufs/ufscommon.cc 1970-01-01 00:00:00 +0000 @@ -1,841 +0,0 @@ -/* - * $Id$ - * - * DEBUG: section 47 Store Directory Routines - * AUTHOR: Robert Collins - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - * Copyright (c) 2003, Robert Collins - */ - -#include "squid.h" -#include "ufscommon.h" -#include "Store.h" -#include "fde.h" -#include "SquidTime.h" -#include "StoreMeta.h" -#include "Generic.h" -#include "StoreMetaUnpacker.h" -#include "RefCount.h" -#include "StoreSwapLogData.h" -#include "swap_log_op.h" - - -CBDATA_CLASS_INIT(RebuildState); - -/// Parse a swap header entry created on a system with 32-bit size_t and sfileno -/// this is typical of 32-bit systems without large file support -/// NP: SQUID_MD5_DIGEST_LENGTH is very risky still. -class UFSSwapLogParser_v1_32bs:public UFSSwapLogParser -{ -public: - /// version 1 cache swap.state entry with 32-bit size_t (swap_file_sz) - /// time_t an sfileno have no variation from the v1 baseline format - struct StoreSwapLogDataOld { - char op; - sfileno swap_filen; - time_t timestamp; - time_t lastref; - time_t expires; - time_t lastmod; - uint32_t swap_file_sz; - uint16_t refcount; - uint16_t flags; - unsigned char key[SQUID_MD5_DIGEST_LENGTH]; - }; - UFSSwapLogParser_v1_32bs(FILE *fp):UFSSwapLogParser(fp) { - record_size = sizeof(UFSSwapLogParser_v1_32bs::StoreSwapLogDataOld); - } - /// Convert the on-disk 32-bit format to our current format while reading - bool ReadRecord(StoreSwapLogData &swapData) { - UFSSwapLogParser_v1_32bs::StoreSwapLogDataOld readData; - int bytes = sizeof(UFSSwapLogParser_v1_32bs::StoreSwapLogDataOld); - - assert(log); - - if (fread(&readData, bytes, 1, log) != 1) { - return false; - } - swapData.op = readData.op; - swapData.swap_filen = readData.swap_filen; - swapData.timestamp = readData.timestamp; - swapData.lastref = readData.lastref; - swapData.expires = readData.expires; - swapData.lastmod = readData.lastmod; - swapData.swap_file_sz = readData.swap_file_sz; - swapData.refcount = readData.refcount; - swapData.flags = readData.flags; - memcpy(swapData.key, readData.key, SQUID_MD5_DIGEST_LENGTH); - return true; - } -}; - -#if UNUSED_CODE -/// Parse a swap header entry created on a system with 32-bit size_t, time_t and sfileno -/// this is typical of 32-bit systems without large file support and with old kernels -/// NP: SQUID_MD5_DIGEST_LENGTH is very risky still. -class UFSSwapLogParser_v1_32bst:public UFSSwapLogParser -{ -public: - /// version 1 cache swap.state entry with 32-bit size_t (swap_file_sz) - /// time_t also differs - /// sfileno has no variation from the v1 baseline format - struct StoreSwapLogDataOld { - char op; - sfileno swap_filen; - int32_t timestamp; - int32_t lastref; - int32_t expires; - int32_t lastmod; - uint32_t swap_file_sz; - uint16_t refcount; - uint16_t flags; - unsigned char key[SQUID_MD5_DIGEST_LENGTH]; - }; - UFSSwapLogParser_v1_32bst(FILE *fp):UFSSwapLogParser(fp) { - record_size = sizeof(UFSSwapLogParser_v1_32bst::StoreSwapLogDataOld); - } - /// Convert the on-disk 32-bit format to our current format while reading - bool ReadRecord(StoreSwapLogData &swapData) { - UFSSwapLogParser_v1_32bst::StoreSwapLogDataOld readData; - int bytes = sizeof(UFSSwapLogParser_v1_32bst::StoreSwapLogDataOld); - - assert(log); - - if (fread(&readData, bytes, 1, log) != 1) { - return false; - } - swapData.op = readData.op; - swapData.swap_filen = readData.swap_filen; - swapData.timestamp = readData.timestamp; - swapData.lastref = readData.lastref; - swapData.expires = readData.expires; - swapData.lastmod = readData.lastmod; - swapData.swap_file_sz = readData.swap_file_sz; - swapData.refcount = readData.refcount; - swapData.flags = readData.flags; - memcpy(swapData.key, readData.key, SQUID_MD5_DIGEST_LENGTH); - return true; - } -}; - -/// Parse a swap header entry created on a system with 64-bit size_t and sfileno -/// this is typical of 64-bit systems prior to this patch fixing sfileno to 32-bits -/// NP: SQUID_MD5_DIGEST_LENGTH is very risky still. -class UFSSwapLogParser_v1_64bfn:public UFSSwapLogParser -{ -public: - /// version 1 cache swap.state entry with 64-bit sfileno - struct StoreSwapLogDataOld { - char op; - int64_t swap_filen; - time_t timestamp; - time_t lastref; - time_t expires; - time_t lastmod; - uint64_t swap_file_sz; - uint16_t refcount; - uint16_t flags; - unsigned char key[SQUID_MD5_DIGEST_LENGTH]; - }; - UFSSwapLogParser_v1_64bfn(FILE *fp):UFSSwapLogParser(fp) { - record_size = sizeof(UFSSwapLogParser_v1_64bfn::StoreSwapLogDataOld); - } - /// Convert the on-disk 64-bit format to our current format while reading - bool ReadRecord(StoreSwapLogData &swapData) { - UFSSwapLogParser_v1_64bfn::StoreSwapLogDataOld readData; - int bytes = sizeof(UFSSwapLogParser_v1_64bfn::StoreSwapLogDataOld); - - assert(log); - - if (fread(&readData, bytes, 1, log) != 1) { - return false; - } - swapData.op = readData.op; - if ((readData.swap_filen>>32) != 0) { - fatalf("File ID on record is greater than maximum cache file ID."); - } - swapData.swap_filen = (int32_t)readData.swap_filen; - swapData.timestamp = readData.timestamp; - swapData.lastref = readData.lastref; - swapData.expires = readData.expires; - swapData.lastmod = readData.lastmod; - swapData.swap_file_sz = readData.swap_file_sz; - swapData.refcount = readData.refcount; - swapData.flags = readData.flags; - memcpy(swapData.key, readData.key, SQUID_MD5_DIGEST_LENGTH); - return true; - } -}; - -class UFSSwapLogParser_v1:public UFSSwapLogParser -{ -public: - UFSSwapLogParser_v1(FILE *fp):UFSSwapLogParser(fp) { - record_size = sizeof(StoreSwapLogData); - } - bool ReadRecord(StoreSwapLogData &swapData); -}; - - -bool UFSSwapLogParser_v1::ReadRecord(StoreSwapLogData &swapData) -{ - int bytes = sizeof(StoreSwapLogData); - - assert(log); - - if (fread(&swapData, bytes, 1, log) != 1) { - return false; - } - return true; -} -#endif /* UNUSED_CODE */ - -/// swap.state v2 log parser -class UFSSwapLogParser_v2: public UFSSwapLogParser -{ -public: - UFSSwapLogParser_v2(FILE *fp): UFSSwapLogParser(fp) { - record_size = sizeof(StoreSwapLogData); - } - bool ReadRecord(StoreSwapLogData &swapData) { - assert(log); - return fread(&swapData, sizeof(StoreSwapLogData), 1, log) == 1; - } -}; - - -UFSSwapLogParser *UFSSwapLogParser::GetUFSSwapLogParser(FILE *fp) -{ - StoreSwapLogHeader header; - - assert(fp); - - if (fread(&header, sizeof(StoreSwapLogHeader), 1, fp) != 1) - return NULL; - - if (header.op != SWAP_LOG_VERSION) { - debugs(47, 1, "Old swap file detected..."); - fseek(fp, 0, SEEK_SET); - return new UFSSwapLogParser_v1_32bs(fp); // Um. 32-bits except time_t, and can't determine that. - } - - debugs(47, 2, "Swap file version: " << header.version); - - if (header.version == 1) { - if (fseek(fp, header.record_size, SEEK_SET) != 0) - return NULL; - - debugs(47, DBG_IMPORTANT, "Rejecting swap file v1 to avoid cache " << - "index corruption. Forcing a full cache index rebuild. " << - "See Squid bug #3441."); - return NULL; - -#if UNUSED_CODE - // baseline - // 32-bit sfileno - // native time_t (hopefully 64-bit) - // 64-bit file size - if (header.record_size == sizeof(StoreSwapLogData)) { - debugs(47, 1, "Version 1 of swap file with LFS support detected... "); - return new UFSSwapLogParser_v1(fp); - } - - // which means we have a 3-way grid of permutations to import (yuck!) - // 1) sfileno 32-bit / 64-bit (64-bit was broken) - // 2) time_t 32-bit / 64-bit - // 3) size_t 32-bit / 64-bit (32-bit was pre-LFS) - - // 32-bit systems... - // only LFS (size_t) differs from baseline - if (header.record_size == sizeof(struct UFSSwapLogParser_v1_32bs::StoreSwapLogDataOld)) { - debugs(47, 1, "Version 1 (32-bit) swap file without LFS support detected... "); - return new UFSSwapLogParser_v1_32bs(fp); - } - // LFS (size_t) and timestamps (time_t) differs from baseline - if (header.record_size == sizeof(struct UFSSwapLogParser_v1_32bst::StoreSwapLogDataOld)) { - debugs(47, 1, "Version 1 (32-bit) swap file with short timestamps and without LFS support detected... "); - return new UFSSwapLogParser_v1_32bst(fp); - } - // No downgrade for 64-bit timestamps to 32-bit. - - // 64-bit systems - // sfileno was 64-bit for a some builds - if (header.record_size == sizeof(struct UFSSwapLogParser_v1_64bfn::StoreSwapLogDataOld)) { - debugs(47, 1, "Version 1 (64-bit) swap file with broken sfileno detected... "); - return new UFSSwapLogParser_v1_64bfn(fp); - } - // NP: 64-bit system with 32-bit size_t/time_t are not handled. - - debugs(47, 1, "WARNING: The swap file has wrong format!... "); - debugs(47, 1, "NOTE: Cannot safely downgrade caches to short (32-bit) timestamps."); - return NULL; -#endif - } - - if (header.version >= 2) { - if (!header.sane()) { - debugs(47, DBG_IMPORTANT, "ERROR: Corrupted v" << header.version << - " swap file header."); - return NULL; - } - - if (fseek(fp, header.record_size, SEEK_SET) != 0) - return NULL; - - if (header.version == 2) - return new UFSSwapLogParser_v2(fp); - } - - // TODO: v3: write to disk in network-order bytes for the larger fields? - - debugs(47, DBG_IMPORTANT, "Unknown swap file version: " << header.version); - return NULL; -} - -int UFSSwapLogParser::SwapLogEntries() -{ - struct stat sb; - - if (log_entries >= 0) - return log_entries; - - if (log && record_size && 0 == fstat(fileno(log), &sb)) { - log_entries = sb.st_size/record_size; - return log_entries; - } - - return 0; -} - - - - -RebuildState::RebuildState (RefCount aSwapDir) : sd (aSwapDir),LogParser(NULL), e(NULL), fromLog(true), _done (false) -{ - /* - * If the swap.state file exists in the cache_dir, then - * we'll use commonUfsDirRebuildFromSwapLog(), otherwise we'll - * use commonUfsDirRebuildFromDirectory() to open up each file - * and suck in the meta data. - */ - int clean = 0; - int zeroLengthLog = 0; - FILE *fp = sd->openTmpSwapLog(&clean, &zeroLengthLog); - - if (fp && !zeroLengthLog) - LogParser = UFSSwapLogParser::GetUFSSwapLogParser(fp); - - if (LogParser == NULL ) { - fromLog = false; - - if (fp != NULL) - fclose(fp); - - } else { - fromLog = true; - flags.clean = (unsigned int) clean; - } - - if (!clean) - flags.need_to_validate = 1; - - debugs(47, DBG_IMPORTANT, "Rebuilding storage in " << sd->path << " (" << - (clean ? "clean log" : (LogParser ? "dirty log" : "no log")) << ")"); -} - -RebuildState::~RebuildState() -{ - sd->closeTmpSwapLog(); - - if (LogParser) - delete LogParser; -} - -void -RebuildState::RebuildStep(void *data) -{ - RebuildState *rb = (RebuildState *)data; - rb->rebuildStep(); - - if (!rb->isDone()) - eventAdd("storeRebuild", RebuildStep, rb, 0.01, 1); - else { - -- StoreController::store_dirs_rebuilding; - storeRebuildComplete(&rb->counts); - delete rb; - } -} - -/// load entries from swap.state or files until we run out of entries or time -void -RebuildState::rebuildStep() -{ - currentEntry(NULL); - - // Balance our desire to maximize the number of entries processed at once - // (and, hence, minimize overheads and total rebuild time) with a - // requirement to also process Coordinator events, disk I/Os, etc. - const int maxSpentMsec = 50; // keep small: most RAM I/Os are under 1ms - const timeval loopStart = current_time; - - const int totalEntries = LogParser ? LogParser->SwapLogEntries() : -1; - - while (!isDone()) { - if (fromLog) - rebuildFromSwapLog(); - else - rebuildFromDirectory(); - - // TODO: teach storeRebuildProgress to handle totalEntries <= 0 - if (totalEntries > 0 && (n_read % 4000 == 0)) - storeRebuildProgress(sd->index, totalEntries, n_read); - - if (opt_foreground_rebuild) - continue; // skip "few entries at a time" check below - - getCurrentTime(); - const double elapsedMsec = tvSubMsec(loopStart, current_time); - if (elapsedMsec > maxSpentMsec || elapsedMsec < 0) { - debugs(47, 5, HERE << "pausing after " << n_read << " entries in " << - elapsedMsec << "ms; " << (elapsedMsec/n_read) << "ms per entry"); - break; - } - } -} - -/// process one cache file -void -RebuildState::rebuildFromDirectory() -{ - cache_key key[SQUID_MD5_DIGEST_LENGTH]; - - struct stat sb; - int fd = -1; - assert(this != NULL); - debugs(47, 3, "commonUfsDirRebuildFromDirectory: DIR #" << sd->index); - - assert(fd == -1); - sfileno filn = 0; - int size; - fd = getNextFile(&filn, &size); - - if (fd == -2) { - debugs(47, DBG_IMPORTANT, "Done scanning " << sd->path << " dir (" << - n_read << " entries)"); - _done = true; - return; - } else if (fd < 0) { - return; - } - - assert(fd > -1); - /* lets get file stats here */ - - ++n_read; - - if (fstat(fd, &sb) < 0) { - debugs(47, 1, "commonUfsDirRebuildFromDirectory: fstat(FD " << fd << "): " << xstrerror()); - file_close(fd); - --store_open_disk_fd; - fd = -1; - return; - } - - MemBuf buf; - buf.init(SM_PAGE_SIZE, SM_PAGE_SIZE); - if (!storeRebuildLoadEntry(fd, sd->index, buf, counts)) - return; - - StoreEntry tmpe; - const bool loaded = storeRebuildParseEntry(buf, tmpe, key, counts, - (int64_t)sb.st_size); - - file_close(fd); - --store_open_disk_fd; - fd = -1; - - if (!loaded) { - // XXX: shouldn't this be a call to commonUfsUnlink? - sd->unlinkFile(filn); // should we unlink in all failure cases? - return; - } - - if (!storeRebuildKeepEntry(tmpe, key, counts)) - return; - - ++counts.objcount; - // tmpe.dump(5); - currentEntry(sd->addDiskRestore(key, - filn, - tmpe.swap_file_sz, - tmpe.expires, - tmpe.timestamp, - tmpe.lastref, - tmpe.lastmod, - tmpe.refcount, /* refcount */ - tmpe.flags, /* flags */ - (int) flags.clean)); - storeDirSwapLog(currentEntry(), SWAP_LOG_ADD); -} - -StoreEntry * -RebuildState::currentEntry() const -{ - return e; -} - -void -RebuildState::currentEntry(StoreEntry *newValue) -{ - e = newValue; -} - -/// process one swap log entry -void -RebuildState::rebuildFromSwapLog() -{ - StoreSwapLogData swapData; - - if (LogParser->ReadRecord(swapData) != 1) { - debugs(47, 1, "Done reading " << sd->path << " swaplog (" << n_read << " entries)"); - LogParser->Close(); - delete LogParser; - LogParser = NULL; - _done = true; - return; - } - - ++n_read; - - if (!swapData.sane()) { - ++counts.invalid; - return; - } - - /* - * BC: during 2.4 development, we changed the way swap file - * numbers are assigned and stored. The high 16 bits used - * to encode the SD index number. There used to be a call - * to storeDirProperFileno here that re-assigned the index - * bits. Now, for backwards compatibility, we just need - * to mask it off. - */ - swapData.swap_filen &= 0x00FFFFFF; - - debugs(47, 3, "commonUfsDirRebuildFromSwapLog: " << - swap_log_op_str[(int) swapData.op] << " " << - storeKeyText(swapData.key) << " "<< std::setfill('0') << - std::hex << std::uppercase << std::setw(8) << - swapData.swap_filen); - - if (swapData.op == SWAP_LOG_ADD) { - (void) 0; - } else if (swapData.op == SWAP_LOG_DEL) { - /* Delete unless we already have a newer copy anywhere in any store */ - /* this needs to become - * 1) unpack url - * 2) make synthetic request with headers ?? or otherwise search - * for a matching object in the store - * TODO FIXME change to new async api - */ - currentEntry (Store::Root().get(swapData.key)); - - if (currentEntry() != NULL && swapData.lastref >= e->lastref) { - undoAdd(); - --counts.objcount; - ++counts.cancelcount; - } - return; - } else { - const double - x = ::log(static_cast(++counts.bad_log_op)) / ::log(10.0); - - if (0.0 == x - (double) (int) x) - debugs(47, 1, "WARNING: " << counts.bad_log_op << " invalid swap log entries found"); - - ++counts.invalid; - - return; - } - - ++counts.scancount; // XXX: should not this be incremented earlier? - - if (!sd->validFileno(swapData.swap_filen, 0)) { - ++counts.invalid; - return; - } - - if (EBIT_TEST(swapData.flags, KEY_PRIVATE)) { - ++counts.badflags; - return; - } - - /* this needs to become - * 1) unpack url - * 2) make synthetic request with headers ?? or otherwise search - * for a matching object in the store - * TODO FIXME change to new async api - */ - currentEntry (Store::Root().get(swapData.key)); - - int used; /* is swapfile already in use? */ - - used = sd->mapBitTest(swapData.swap_filen); - - /* If this URL already exists in the cache, does the swap log - * appear to have a newer entry? Compare 'lastref' from the - * swap log to e->lastref. */ - /* is the log entry newer than current entry? */ - int disk_entry_newer = currentEntry() ? (swapData.lastref > currentEntry()->lastref ? 1 : 0) : 0; - - if (used && !disk_entry_newer) { - /* log entry is old, ignore it */ - ++counts.clashcount; - return; - } else if (used && currentEntry() && currentEntry()->swap_filen == swapData.swap_filen && currentEntry()->swap_dirn == sd->index) { - /* swapfile taken, same URL, newer, update meta */ - - if (currentEntry()->store_status == STORE_OK) { - currentEntry()->lastref = swapData.timestamp; - currentEntry()->timestamp = swapData.timestamp; - currentEntry()->expires = swapData.expires; - currentEntry()->lastmod = swapData.lastmod; - currentEntry()->flags = swapData.flags; - currentEntry()->refcount += swapData.refcount; - sd->dereference(*currentEntry()); - } else { - debug_trap("commonUfsDirRebuildFromSwapLog: bad condition"); - debugs(47, 1, "\tSee " << __FILE__ << ":" << __LINE__); - } - return; - } else if (used) { - /* swapfile in use, not by this URL, log entry is newer */ - /* This is sorta bad: the log entry should NOT be newer at this - * point. If the log is dirty, the filesize check should have - * caught this. If the log is clean, there should never be a - * newer entry. */ - debugs(47, 1, "WARNING: newer swaplog entry for dirno " << - sd->index << ", fileno "<< std::setfill('0') << std::hex << - std::uppercase << std::setw(8) << swapData.swap_filen); - - /* I'm tempted to remove the swapfile here just to be safe, - * but there is a bad race condition in the NOVM version if - * the swapfile has recently been opened for writing, but - * not yet opened for reading. Because we can't map - * swapfiles back to StoreEntrys, we don't know the state - * of the entry using that file. */ - /* We'll assume the existing entry is valid, probably because - * were in a slow rebuild and the the swap file number got taken - * and the validation procedure hasn't run. */ - assert(flags.need_to_validate); - ++counts.clashcount; - return; - } else if (currentEntry() && !disk_entry_newer) { - /* key already exists, current entry is newer */ - /* keep old, ignore new */ - ++counts.dupcount; - return; - } else if (currentEntry()) { - /* key already exists, this swapfile not being used */ - /* junk old, load new */ - undoAdd(); - --counts.objcount; - ++counts.dupcount; - } else { - /* URL doesnt exist, swapfile not in use */ - /* load new */ - (void) 0; - } - - ++counts.objcount; - - currentEntry(sd->addDiskRestore(swapData.key, - swapData.swap_filen, - swapData.swap_file_sz, - swapData.expires, - swapData.timestamp, - swapData.lastref, - swapData.lastmod, - swapData.refcount, - swapData.flags, - (int) flags.clean)); - - storeDirSwapLog(currentEntry(), SWAP_LOG_ADD); -} - -/// undo the effects of adding an entry in rebuildFromSwapLog() -void -RebuildState::undoAdd() -{ - StoreEntry *added = currentEntry(); - assert(added); - currentEntry(NULL); - - // TODO: Why bother with these two if we are going to release?! - added->expireNow(); - added->releaseRequest(); - - if (added->swap_filen > -1) { - UFSSwapDir *sde = dynamic_cast(INDEXSD(added->swap_dirn)); - assert(sde); - sde->undoAddDiskRestore(added); - } - - added->release(); -} - -int -RebuildState::getNextFile(sfileno * filn_p, int *size) -{ - int fd = -1; - int dirs_opened = 0; - debugs(47, 3, "commonUfsDirGetNextFile: flag=" << flags.init << ", " << - sd->index << ": /"<< std::setfill('0') << std::hex << - std::uppercase << std::setw(2) << curlvl1 << "/" << std::setw(2) << - curlvl2); - - if (done) - return -2; - - while (fd < 0 && done == 0) { - fd = -1; - - if (0 == flags.init) { /* initialize, open first file */ - done = 0; - curlvl1 = 0; - curlvl2 = 0; - in_dir = 0; - flags.init = 1; - assert(Config.cacheSwap.n_configured > 0); - } - - if (0 == in_dir) { /* we need to read in a new directory */ - snprintf(fullpath, MAXPATHLEN, "%s/%02X/%02X", - sd->path, - curlvl1, curlvl2); - - if (dirs_opened) - return -1; - - td = opendir(fullpath); - - ++dirs_opened; - - if (td == NULL) { - debugs(47, 1, "commonUfsDirGetNextFile: opendir: " << fullpath << ": " << xstrerror()); - } else { - entry = readdir(td); /* skip . and .. */ - entry = readdir(td); - - if (entry == NULL && errno == ENOENT) - debugs(47, 1, "commonUfsDirGetNextFile: directory does not exist!."); - debugs(47, 3, "commonUfsDirGetNextFile: Directory " << fullpath); - } - } - - if (td != NULL && (entry = readdir(td)) != NULL) { - ++in_dir; - - if (sscanf(entry->d_name, "%x", &fn) != 1) { - debugs(47, 3, "commonUfsDirGetNextFile: invalid " << entry->d_name); - continue; - } - - if (!UFSSwapDir::FilenoBelongsHere(fn, sd->index, curlvl1, curlvl2)) { - debugs(47, 3, "commonUfsDirGetNextFile: "<< std::setfill('0') << - std::hex << std::uppercase << std::setw(8) << fn << - " does not belong in " << std::dec << sd->index << "/" << - curlvl1 << "/" << curlvl2); - - continue; - } - - if (sd->mapBitTest(fn)) { - debugs(47, 3, "commonUfsDirGetNextFile: Locked, continuing with next."); - continue; - } - - snprintf(fullfilename, MAXPATHLEN, "%s/%s", - fullpath, entry->d_name); - debugs(47, 3, "commonUfsDirGetNextFile: Opening " << fullfilename); - fd = file_open(fullfilename, O_RDONLY | O_BINARY); - - if (fd < 0) - debugs(47, 1, "commonUfsDirGetNextFile: " << fullfilename << ": " << xstrerror()); - else - ++store_open_disk_fd; - - continue; - } - - if (td != NULL) - closedir(td); - - td = NULL; - - in_dir = 0; - - if (sd->validL2(++curlvl2)) - continue; - - curlvl2 = 0; - - if (sd->validL1(++curlvl1)) - continue; - - curlvl1 = 0; - - done = 1; - } - - *filn_p = fn; - return fd; -} - -bool -RebuildState::error() const -{ - return false; -} - -bool -RebuildState::isDone() const -{ - return _done; -} - -StoreEntry * -RebuildState::currentItem() -{ - return currentEntry(); -} - -#if !_USE_INLINE_ -#include "ufscommon.cci" -#endif === removed file 'src/fs/ufs/ufscommon.cci' --- src/fs/ufs/ufscommon.cci 2009-01-21 03:47:47 +0000 +++ src/fs/ufs/ufscommon.cci 1970-01-01 00:00:00 +0000 @@ -1,34 +0,0 @@ -/* - * $Id$ - * - * DEBUG: section 47 Store Directory Routines - * AUTHOR: Duane Wessels - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ - === removed file 'src/fs/ufs/ufscommon.h' --- src/fs/ufs/ufscommon.h 2012-07-20 23:11:02 +0000 +++ src/fs/ufs/ufscommon.h 1970-01-01 00:00:00 +0000 @@ -1,425 +0,0 @@ -/* - * $Id$ - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - */ -#ifndef SQUID_UFSCOMMON_H -#define SQUID_UFSCOMMON_H - - -#define DefaultLevelOneDirs 16 -#define DefaultLevelTwoDirs 256 -#define STORE_META_BUFSZ 4096 - -class UFSStrategy; -class ConfigOptionVector; -class DiskIOModule; -class StoreSearch; -class FileMap; - -#include "SwapDir.h" - -/// \ingroup UFS -class UFSSwapDir : public SwapDir -{ - -public: - static int IsUFSDir(SwapDir* sd); - static int DirClean(int swap_index); - static int FilenoBelongsHere(int fn, int F0, int F1, int F2); - - UFSSwapDir(char const *aType, const char *aModuleType); - virtual void init(); - virtual void create(); - virtual void dump(StoreEntry &) const; - ~UFSSwapDir(); - virtual StoreSearch *search(String const url, HttpRequest *); - virtual bool doubleCheck(StoreEntry &); - virtual bool unlinkdUseful() const; - virtual void unlink(StoreEntry &); - virtual void statfs(StoreEntry &)const; - virtual void maintain(); - virtual bool canStore(const StoreEntry &e, int64_t diskSpaceNeeded, int &load) const; - virtual void reference(StoreEntry &); - virtual bool dereference(StoreEntry &); - virtual StoreIOState::Pointer createStoreIO(StoreEntry &, StoreIOState::STFNCB *, StoreIOState::STIOCB *, void *); - virtual StoreIOState::Pointer openStoreIO(StoreEntry &, StoreIOState::STFNCB *, StoreIOState::STIOCB *, void *); - virtual void openLog(); - virtual void closeLog(); - virtual int writeCleanStart(); - virtual void writeCleanDone(); - virtual void logEntry(const StoreEntry & e, int op) const; - virtual void parse(int index, char *path); - virtual void reconfigure(); - virtual int callback(); - virtual void sync(); - virtual void swappedOut(const StoreEntry &e); - virtual uint64_t currentSize() const { return cur_size; } - virtual uint64_t currentCount() const { return n_disk_objects; } - - void unlinkFile(sfileno f); - // move down when unlink is a virtual method - //protected: - UFSStrategy *IO; - char *fullPath(sfileno, char *) const; - /* temp */ - void closeTmpSwapLog(); - FILE *openTmpSwapLog(int *clean_flag, int *zero_flag); - char *swapSubDir(int subdirn) const; - int mapBitTest(sfileno filn); - void mapBitReset(sfileno filn); - void mapBitSet(sfileno filn); - StoreEntry *addDiskRestore(const cache_key * key, - sfileno file_number, - uint64_t swap_file_sz, - time_t expires, - time_t timestamp, - time_t lastref, - time_t lastmod, - uint32_t refcount, - uint16_t flags, - int clean); - /// Undo the effects of UFSSwapDir::addDiskRestore(). - void undoAddDiskRestore(StoreEntry *e); - int validFileno(sfileno filn, int flag) const; - int mapBitAllocate(); - virtual ConfigOption *getOptionTree() const; - - void *fsdata; - - bool validL2(int) const; - bool validL1(int) const; - - void replacementAdd(StoreEntry *e); - void replacementRemove(StoreEntry *e); - -protected: - FileMap *map; - int suggest; - int l1; - int l2; - -private: - void parseSizeL1L2(); - static int NumberOfUFSDirs; - static int * UFSDirToGlobalDirMapping; - bool pathIsDirectory(const char *path)const; - int swaplog_fd; - static EVH CleanEvent; - bool verifyCacheDirs(); - void rebuild(); - int createDirectory(const char *path, int); - void createSwapSubDirs(); - void dumpEntry(StoreEntry &) const; - char *logFile(char const *ext = NULL)const; - void changeIO(DiskIOModule *); - bool optionIOParse(char const *option, const char *value, int reconfiguring); - void optionIODump(StoreEntry * e) const; - mutable ConfigOptionVector *currentIOOptions; - char const *ioType; - uint64_t cur_size; ///< currently used space in the storage area - uint64_t n_disk_objects; ///< total number of objects stored -}; - -#include "RefCount.h" -#include "DiskIO/IORequestor.h" - -/** - * UFS dir specific IO calls - * - \todo This should be whittled away. - * DiskIOModule should be providing the entire needed API. - */ - -class DiskIOStrategy; - -class DiskFile; - -/// \ingroup UFS -class UFSStrategy -{ - -public: - UFSStrategy (DiskIOStrategy *); - virtual ~UFSStrategy (); - /* Not implemented */ - UFSStrategy (UFSStrategy const &); - UFSStrategy &operator=(UFSStrategy const &); - - virtual bool shedLoad(); - - virtual int load(); - - StoreIOState::Pointer createState(SwapDir *SD, StoreEntry *e, StoreIOState::STIOCB * callback, void *callback_data) const; - /* UFS specific */ - virtual RefCount newFile (char const *path); - StoreIOState::Pointer open(SwapDir *, StoreEntry *, StoreIOState::STFNCB *, - StoreIOState::STIOCB *, void *); - StoreIOState::Pointer create(SwapDir *, StoreEntry *, StoreIOState::STFNCB *, - StoreIOState::STIOCB *, void *); - - virtual void unlinkFile (char const *); - virtual void sync(); - - virtual int callback(); - - /** Init per-instance logic */ - virtual void init(); - - /** cachemgr output on the IO instance stats */ - virtual void statfs(StoreEntry & sentry)const; - - /** The io strategy in use */ - DiskIOStrategy *io; -protected: - - friend class UFSSwapDir; -}; - -/** Common ufs-store-dir logic */ - -class ReadRequest; - -/// \ingroup UFS -class UFSStoreState : public StoreIOState, public IORequestor -{ - -public: - void * operator new (size_t); - void operator delete (void *); - UFSStoreState(SwapDir * SD, StoreEntry * anEntry, STIOCB * callback_, void *callback_data_); - ~UFSStoreState(); - virtual void close(int how); - virtual void closeCompleted(); - // protected: - virtual void ioCompletedNotification(); - virtual void readCompleted(const char *buf, int len, int errflag, RefCount); - virtual void writeCompleted(int errflag, size_t len, RefCount); - RefCount theFile; - bool opening; - bool creating; - bool closing; - bool reading; - bool writing; - void read_(char *buf, size_t size, off_t offset, STRCB * callback, void *callback_data); - void write(char const *buf, size_t size, off_t offset, FREE * free_func); - -protected: - virtual void doCloseCallback (int errflag); - - class _queued_read - { - - public: - MEMPROXY_CLASS(UFSStoreState::_queued_read); - char *buf; - size_t size; - off_t offset; - STRCB *callback; - void *callback_data; - - }; - - class _queued_write - { - - public: - MEMPROXY_CLASS(UFSStoreState::_queued_write); - char const *buf; - size_t size; - off_t offset; - FREE *free_func; - - }; - - /** \todo These should be in the IO strategy */ - - struct { - /** - * DPW 2006-05-24 - * the write_draining flag is used to avoid recursion inside - * the UFSStoreState::drainWriteQueue() method. - */ - bool write_draining; - /** - * DPW 2006-05-24 - * The try_closing flag is set by UFSStoreState::tryClosing() - * when UFSStoreState wants to close the file, but cannot - * because of pending I/Os. If set, UFSStoreState will - * try to close again in the I/O callbacks. - */ - bool try_closing; - } flags; - link_list *pending_reads; - link_list *pending_writes; - void queueRead(char *, size_t, off_t, STRCB *, void *); - void queueWrite(char const *, size_t, off_t, FREE *); - bool kickReadQueue(); - void drainWriteQueue(); - void tryClosing(); - char *read_buf; - -private: - CBDATA_CLASS(UFSStoreState); - void openDone(); - void freePending(); - void doWrite(); -}; - -MEMPROXY_CLASS_INLINE(UFSStoreState::_queued_read); -MEMPROXY_CLASS_INLINE(UFSStoreState::_queued_write); - - -#include "StoreSearch.h" - -/// \ingroup UFS -class StoreSearchUFS : public StoreSearch -{ - -public: - StoreSearchUFS(RefCount sd); - StoreSearchUFS(StoreSearchUFS const &); - virtual ~StoreSearchUFS(); - - /** \todo Iterator API - garh, wrong place */ - /** - * callback the client when a new StoreEntry is available - * or an error occurs - */ - virtual void next(void (callback)(void *cbdata), void *cbdata); - - /** - \retval true if a new StoreEntry is immediately available - \retval false if a new StoreEntry is NOT immediately available - */ - virtual bool next(); - - virtual bool error() const; - virtual bool isDone() const; - virtual StoreEntry *currentItem(); - - RefCount sd; - RemovalPolicyWalker *walker; - -private: - CBDATA_CLASS2(StoreSearchUFS); - /// \bug (callback) should be hidden behind a proper human readable name - void (callback)(void *cbdata); - void *cbdata; - StoreEntry * current; - bool _done; -}; - - -class StoreSwapLogData; - -/// \ingroup UFS -class UFSSwapLogParser -{ - -public: - FILE *log; - int log_entries; - int record_size; - - UFSSwapLogParser(FILE *fp):log(fp),log_entries(-1), record_size(0) { - } - virtual ~UFSSwapLogParser() {}; - - static UFSSwapLogParser *GetUFSSwapLogParser(FILE *fp); - - virtual bool ReadRecord(StoreSwapLogData &swapData) = 0; - int SwapLogEntries(); - void Close() { - if (log) { - fclose(log); - log = NULL; - } - } -}; - - -/// \ingroup UFS -class RebuildState : public RefCountable -{ - -public: - static EVH RebuildStep; - - RebuildState(RefCount sd); - ~RebuildState(); - - virtual bool error() const; - virtual bool isDone() const; - virtual StoreEntry *currentItem(); - - RefCount sd; - int n_read; - /* FILE *log;*/ - UFSSwapLogParser *LogParser; - int curlvl1; - int curlvl2; - - struct { - unsigned int need_to_validate:1; - unsigned int clean:1; - unsigned int init:1; - } flags; - int in_dir; - int done; - int fn; - - dirent_t *entry; - DIR *td; - char fullpath[MAXPATHLEN]; - char fullfilename[MAXPATHLEN]; - - struct _store_rebuild_data counts; - -private: - CBDATA_CLASS2(RebuildState); - void rebuildFromDirectory(); - void rebuildFromSwapLog(); - void rebuildStep(); - void undoAdd(); - int getNextFile(sfileno *, int *size); - StoreEntry *currentEntry() const; - void currentEntry(StoreEntry *); - StoreEntry *e; - bool fromLog; - bool _done; - /// \bug (callback) should be hidden behind a proper human readable name - void (callback)(void *cbdata); - void *cbdata; -}; - -#if _USE_INLINE_ -#include "ufscommon.cci" -#endif - -#endif /* SQUID_UFSCOMMON_H */ === modified file 'src/tests/testCoss.cc' --- src/tests/testCoss.cc 2012-01-20 18:55:04 +0000 +++ src/tests/testCoss.cc 2012-07-31 21:45:27 +0000 @@ -4,7 +4,6 @@ #include "Store.h" #include "SwapDir.h" #include "DiskIO/DiskIOModule.h" -#include "fs/ufs/ufscommon.h" #include "fs/coss/CossSwapDir.h" #include "Mem.h" #include "MemObject.h" === modified file 'src/tests/testDiskIO.cc' --- src/tests/testDiskIO.cc 2012-01-20 18:55:04 +0000 +++ src/tests/testDiskIO.cc 2012-07-31 21:45:27 +0000 @@ -5,7 +5,6 @@ #include "Store.h" #include "SwapDir.h" #include "DiskIO/DiskIOModule.h" -#include "fs/ufs/ufscommon.h" #if 0 // AYJ: COSS in Squid-3 is disabled. #include "fs/coss/CossSwapDir.h" #endif === modified file 'src/tests/testNull.cc' --- src/tests/testNull.cc 2012-01-20 18:55:04 +0000 +++ src/tests/testNull.cc 2012-07-31 21:45:27 +0000 @@ -4,7 +4,6 @@ #include "Store.h" #include "SwapDir.h" #include "DiskIO/DiskIOModule.h" -#include "fs/ufs/ufscommon.h" #include "fs/null/store_null.h" #include "Mem.h" #include "MemObject.h" === modified file 'src/tests/testUfs.cc' --- src/tests/testUfs.cc 2012-01-20 18:55:04 +0000 +++ src/tests/testUfs.cc 2012-08-07 15:15:08 +0000 @@ -1,15 +1,16 @@ #define SQUID_UNIT_TEST 1 #include "squid.h" -#include "testUfs.h" -#include "Store.h" -#include "SwapDir.h" + #include "DiskIO/DiskIOModule.h" -#include "fs/ufs/ufscommon.h" +#include "HttpHeader.h" +#include "HttpReply.h" #include "Mem.h" #include "MemObject.h" -#include "HttpHeader.h" -#include "HttpReply.h" #include "testStoreSupport.h" +#include "testUfs.h" +#include "Store.h" +#include "SwapDir.h" +#include "fs/ufs/UFSSwapDir.h" #if HAVE_STDEXCEPT #include @@ -19,7 +20,7 @@ CPPUNIT_TEST_SUITE_REGISTRATION( testUfs ); -typedef RefCount SwapDirPointer; +typedef RefCount SwapDirPointer; extern REMOVALPOLICYCREATE createRemovalPolicy_lru; /* XXX fails with --enable-removal-policies=heap */ static void @@ -89,9 +90,9 @@ Store::Root(new StoreController); - SwapDirPointer aStore (new UFSSwapDir("ufs", "Blocking")); + SwapDirPointer aStore (new Fs::Ufs::UFSSwapDir("ufs", "Blocking")); - aStore->IO = new UFSStrategy(DiskIOModule::Find("Blocking")->createStrategy()); + aStore->IO = new Fs::Ufs::UFSStrategy(DiskIOModule::Find("Blocking")->createStrategy()); addSwapDir(aStore); @@ -232,7 +233,7 @@ CPPUNIT_ASSERT(!store_table); // or StoreHashIndex ctor will abort below Store::Root(new StoreController); - SwapDirPointer aStore (new UFSSwapDir("ufs", "Blocking")); + SwapDirPointer aStore (new Fs::Ufs::UFSSwapDir("ufs", "Blocking")); addSwapDir(aStore); commonInit(); Config.replPolicy = new RemovalPolicySettings; # Begin bundle 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