# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: kinkie@squid-cache.org-20120731221707-c3qlkezbeu90q9qg # target_branch: ../trunk # testament_sha1: 9113122de7e30c9adcf59a57ff518dec8525b0ca # timestamp: 2012-08-01 00:17:11 +0200 # base_revision_id: kinkie@squid-cache.org-20120724085954-\ # lfixpilhl2g8k5ev # # Begin patch === modified file 'src/Makefile.am' --- src/Makefile.am 2012-07-18 16:21:47 +0000 +++ src/Makefile.am 2012-07-25 20:56:12 +0000 @@ -15,7 +15,7 @@ DNSSOURCE = dns_internal.cc DNSHELPER = endif - + DNSSOURCE += \ SquidDns.h \ DnsLookupDetails.h \ === modified file 'src/fs/Makefile.am' --- src/fs/Makefile.am 2011-09-10 16:38:36 +0000 +++ src/fs/Makefile.am 2012-07-31 21:49:03 +0000 @@ -24,9 +24,17 @@ ufs/StoreFSufs.cc \ ufs/store_dir_ufs.cc \ ufs/store_io_ufs.cc \ - ufs/ufscommon.cci \ - ufs/ufscommon.cc \ - ufs/ufscommon.h + 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-07-31 21:45:27 +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 === 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-07-31 21:45:27 +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) === 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-07-25 20:56:12 +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) === 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-07-31 21:03:40 +0000 @@ -0,0 +1,540 @@ +/* + * RebuildState.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 "RebuildState.h" +#include "SquidTime.h" +#include "StoreSwapLogData.h" +#include "UFSSwapLogParser.h" + +CBDATA_CLASS_INIT(RebuildState); + + +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(); +} + === 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-07-31 22:15:40 +0000 @@ -0,0 +1,97 @@ +/* + * RebuildState.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_REBUILDSTATE_H +#define SQUID_FS_UFS_REBUILDSTATE_H + +class StoreEntry; +class UFSSwapLogParser; + +#include "RefCount.h" +#include "UFSSwapDir.h" +#include "structs.h" + +/// \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; +}; + + +#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-07-31 22:17:07 +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; === 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-07-26 17:45:44 +0000 @@ -0,0 +1,92 @@ +/* + * StoreSearchUFS.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 "cbdata.h" +#include "StoreSearchUFS.h" +#include "UFSSwapDir.h" + +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; +} + === 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-07-31 22:15:40 +0000 @@ -0,0 +1,79 @@ +/* + * StoreSearchUFS.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_STORESEARCHUFS_H +#define SQUID_FS_UFS_STORESEARCHUFS_H + +#include "StoreSearch.h" +#include "UFSSwapDir.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; +}; + + + + +#endif /* SQUID_FS_UFS_STORESEARCHUFS_H */ === 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-07-31 22:15:40 +0000 @@ -0,0 +1,129 @@ +/* + * UFSStoreState.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_UFSSTORESTATE_H +#define SQUID_FS_UFS_UFSSTORESTATE_H + +#include "DiskIO/IORequestor.h" +#include "StoreIOState.h" + +/// \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); + + + +#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-07-31 22:17:07 +0000 @@ -0,0 +1,182 @@ +/* + * UFSStrategy.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 "DiskIO/DiskIOStrategy.h" +#include "UFSStrategy.h" +#include "UFSStoreState.h" +#include "UFSSwapDir.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); +} + +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/UFSStrategy.h' --- src/fs/ufs/UFSStrategy.h 1970-01-01 00:00:00 +0000 +++ src/fs/ufs/UFSStrategy.h 2012-07-31 22:15:40 +0000 @@ -0,0 +1,85 @@ +/* + * UFSStrategy.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_UFSSTRATEGY_H +#define SQUID_FS_UFS_UFSSTRATEGY_H + +class Swapdir; +class StoreEntry; +class DiskIOStrategy; + +#include "DiskIO/DiskFile.h" +#include "StoreIOState.h" + +/// \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; +}; + + + +#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-07-31 22:17:07 +0000 @@ -0,0 +1,1421 @@ +/* + * UFSSwapDir.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-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 UFSSwapDir::NumberOfUFSDirs = 0; +int *UFSSwapDir::UFSDirToGlobalDirMapping = NULL; + +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; +}; + + +UFSCleanLog::UFSCleanLog(SwapDir *aSwapDir) : cur(NULL),newLog(NULL),cln(NULL),outbuf(NULL), + outbuf_offset(0), fd(-1),walker(NULL), sd(aSwapDir) +{} + + +/* + * 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; + } +} + + + + + +/* + * 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; +} + +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; +} + + + +/* ========== 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; +} + +/* + * 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; +} + +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; +} + +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); +} + +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); +} + + +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; +} + === 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-07-31 22:15:40 +0000 @@ -0,0 +1,151 @@ +/* + * UFSSwapDir.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_UFSSWAPDIR_H +#define SQUID_FS_UFS_UFSSWAPDIR_H + +class HttpRequest; +class ConfigOptionVector; +class FileMap; +class DiskIOModule; + +#include "SquidString.h" +#include "Store.h" +#include "StoreIOState.h" +#include "StoreSearch.h" +#include "SwapDir.h" +#include "swap_log_op.h" +#include "UFSStrategy.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 +}; + + + +#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-07-31 21:23:21 +0000 @@ -0,0 +1,204 @@ +/* + * 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 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; + } +}; + +/// 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; +} + + + === 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-07-31 22:15:40 +0000 @@ -0,0 +1,66 @@ +/* + * 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 + +class StoreSwapLogData; + +#include +/// \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; + } + } +}; + + + + +#endif /* SQUID_FS_UFS_UFSSWAPLOGPARSER_H */ === modified 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 2012-07-31 21:45:27 +0000 @@ -36,7 +36,6 @@ #include "squid-old.h" #include "Store.h" #include "fde.h" -#include "ufscommon.h" #include "StoreSwapLogData.h" #include "ConfigOption.h" #include "DiskIO/DiskIOStrategy.h" @@ -49,1423 +48,5 @@ #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; -} === modified file 'src/fs/ufs/store_io_ufs.cc' --- src/fs/ufs/store_io_ufs.cc 2012-01-20 18:55:04 +0000 +++ src/fs/ufs/store_io_ufs.cc 2012-07-31 21:45:27 +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,45 +42,9 @@ #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); -} +#include "UFSStrategy.h" +#include "UFSStoreState.h" + CBDATA_CLASS_INIT(UFSStoreState); @@ -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); -} - === 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-07-31 21:45:27 +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 # Begin bundle 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 jChjZdUjGP2Jabab//F3JFOFCQ2zX32w