New upstream version 8.1.0

This commit is contained in:
geos_one
2025-08-10 01:34:16 +02:00
commit c891bb7105
4398 changed files with 838833 additions and 0 deletions

562
fsck/source/app/App.cpp Normal file
View File

@@ -0,0 +1,562 @@
#include <common/components/RegistrationDatagramListener.h>
#include <program/Program.h>
#include <toolkit/FsckTkEx.h>
#include <csignal>
#include <syslog.h>
#define APP_WORKERS_DIRECT_NUM 1
#define APP_SYSLOG_IDENTIFIER "beegfs-fsck"
App::App(int argc, char** argv)
{
this->argc = argc;
this->argv = argv;
this->appResult = APPCODE_NO_ERROR;
this->cfg = NULL;
this->netFilter = NULL;
this->tcpOnlyFilter = NULL;
this->log = NULL;
this->mgmtNodes = NULL;
this->metaNodes = NULL;
this->storageNodes = NULL;
this->internodeSyncer = NULL;
this->targetMapper = NULL;
this->targetStateStore = NULL;
this->buddyGroupMapper = NULL;
this->metaBuddyGroupMapper = NULL;
this->workQueue = NULL;
this->ackStore = NULL;
this->netMessageFactory = NULL;
this->dgramListener = NULL;
this->modificationEventHandler = NULL;
this->runMode = NULL;
}
App::~App()
{
// Note: Logging of the common lib classes is not working here, because this is called
// from class Program (so the thread-specific app-pointer isn't set in this context).
workersDelete();
SAFE_DELETE(this->dgramListener);
SAFE_DELETE(this->netMessageFactory);
SAFE_DELETE(this->ackStore);
SAFE_DELETE(this->workQueue);
SAFE_DELETE(this->storageNodes);
SAFE_DELETE(this->metaNodes);
SAFE_DELETE(this->mgmtNodes);
this->localNode.reset();
SAFE_DELETE(this->buddyGroupMapper);
SAFE_DELETE(this->metaBuddyGroupMapper);
SAFE_DELETE(this->targetStateStore);
SAFE_DELETE(this->targetMapper);
SAFE_DELETE(this->internodeSyncer);
SAFE_DELETE(this->log);
SAFE_DELETE(this->tcpOnlyFilter);
SAFE_DELETE(this->netFilter);
SAFE_DELETE(this->cfg);
SAFE_DELETE(this->runMode);
Logger::destroyLogger();
closelog();
}
void App::run()
{
try
{
openlog(APP_SYSLOG_IDENTIFIER, LOG_NDELAY | LOG_PID | LOG_CONS, LOG_DAEMON);
this->cfg = new Config(argc, argv);
runNormal();
}
catch (InvalidConfigException& e)
{
std::cerr << std::endl;
std::cerr << "Error: " << e.what() << std::endl;
std::cerr << std::endl;
std::cerr << std::endl;
if(this->log)
log->logErr(e.what() );
this->appResult = APPCODE_INVALID_CONFIG;
return;
}
catch (std::exception& e)
{
std::cerr << std::endl;
std::cerr << "Unrecoverable error: " << e.what() << std::endl;
std::cerr << std::endl;
if(this->log)
log->logErr(e.what() );
appResult = APPCODE_RUNTIME_ERROR;
return;
}
}
void App::runNormal()
{
bool componentsStarted = false;
auto runModeEnum = Program::getApp()->getConfig()->determineRunMode();
if (runModeEnum == RunMode_CHECKFS || runModeEnum == RunMode_ENABLEQUOTA)
{
// check if running as root
if(geteuid())
{
std::cerr << std::endl
<< "Running beegfs-fsck requires root privileges."
<< std::endl << std::endl << std::endl;
ModeHelp().execute();
appResult = APPCODE_INITIALIZATION_ERROR;
return;
}
if (runModeEnum == RunMode_CHECKFS)
runMode = new ModeCheckFS();
else if (runModeEnum == RunMode_ENABLEQUOTA)
runMode = new ModeEnableQuota();
}
else if (runModeEnum == RunMode_HELP)
{
appResult = ModeHelp().execute();
return;
}
else
{
ModeHelp().execute();
appResult = APPCODE_INVALID_CONFIG;
return;
}
initDataObjects(argc, argv);
// wait for mgmtd
if ( !cfg->getSysMgmtdHost().length() )
throw InvalidConfigException("Management host undefined");
bool mgmtWaitRes = waitForMgmtNode();
if(!mgmtWaitRes)
{ // typically user just pressed ctrl+c in this case
log->logErr("Waiting for beegfs-mgmtd canceled");
appResult = APPCODE_RUNTIME_ERROR;
return;
}
// init components
try
{
initComponents();
}
catch (ComponentInitException& e)
{
FsckTkEx::fsckOutput(e.what(), OutputOptions_DOUBLELINEBREAK | OutputOptions_STDERR);
FsckTkEx::fsckOutput("A hard error occurred. BeeGFS Fsck will abort.",
OutputOptions_LINEBREAK | OutputOptions_STDERR);
appResult = APPCODE_INITIALIZATION_ERROR;
return;
}
// log system and configuration info
logInfos();
// start component threads
startComponents();
componentsStarted = true;
try
{
appResult = runMode->execute();
}
catch (InvalidConfigException& e)
{
ModeHelp modeHelp;
modeHelp.execute();
appResult = APPCODE_INVALID_CONFIG;
}
catch (std::exception &e)
{
FsckTkEx::fsckOutput("Unrecoverable error. BeeGFS Fsck will abort.",
OutputOptions_LINEBREAK | OutputOptions_ADDLINEBREAKBEFORE | OutputOptions_STDERR);
FsckTkEx::fsckOutput(e.what(), OutputOptions_LINEBREAK | OutputOptions_STDERR);
}
// self-termination
if(componentsStarted)
{
stopComponents();
joinComponents();
}
}
void App::initDataObjects(int argc, char** argv)
{
this->netFilter = new NetFilter(cfg->getConnNetFilterFile() );
this->tcpOnlyFilter = new NetFilter(cfg->getConnTcpOnlyFilterFile() );
Logger::createLogger(cfg->getLogLevel(), cfg->getLogType(), cfg->getLogNoDate(),
cfg->getLogStdFile(), cfg->getLogNumLines(), cfg->getLogNumRotatedFiles());
this->log = new LogContext("App");
std::string interfacesFilename = this->cfg->getConnInterfacesFile();
if ( interfacesFilename.length() )
Config::loadStringListFile(interfacesFilename.c_str(), this->allowedInterfaces);
this->targetMapper = new TargetMapper();
this->targetStateStore = new TargetStateStore(NODETYPE_Storage);
this->buddyGroupMapper = new MirrorBuddyGroupMapper(targetMapper);
this->metaBuddyGroupMapper = new MirrorBuddyGroupMapper();
this->mgmtNodes = new NodeStoreServers(NODETYPE_Mgmt, false);
this->metaNodes = new NodeStoreServers(NODETYPE_Meta, false);
this->storageNodes = new NodeStoreServers(NODETYPE_Storage, false);
this->workQueue = new MultiWorkQueue();
this->ackStore = new AcknowledgmentStore();
initLocalNodeInfo();
registerSignalHandler();
this->netMessageFactory = new NetMessageFactory();
}
void App::findAllowedRDMAInterfaces(NicAddressList& outList) const
{
Config* cfg = this->getConfig();
if(cfg->getConnUseRDMA() && RDMASocket::rdmaDevicesExist() )
{
bool foundRdmaInterfaces = NetworkInterfaceCard::checkAndAddRdmaCapability(outList);
if (foundRdmaInterfaces)
outList.sort(NetworkInterfaceCard::NicAddrComp{&allowedInterfaces}); // re-sort the niclist
}
}
void App::findAllowedInterfaces(NicAddressList& outList) const
{
// discover local NICs and filter them
NetworkInterfaceCard::findAllInterfaces(allowedInterfaces, outList);
outList.sort(NetworkInterfaceCard::NicAddrComp{&allowedInterfaces});
}
void App::initLocalNodeInfo()
{
findAllowedInterfaces(localNicList);
findAllowedRDMAInterfaces(localNicList);
if ( this->localNicList.empty() )
throw InvalidConfigException("Couldn't find any usable NIC");
initRoutingTable();
updateRoutingTable();
std::string nodeID = System::getHostname();
this->localNode = std::make_shared<LocalNode>(NODETYPE_Client, nodeID, NumNodeID(), 0, 0,
this->localNicList);
}
void App::initComponents()
{
this->log->log(Log_DEBUG, "Initializing components...");
// Note: We choose a random udp port here to avoid conflicts with the client
unsigned short udpListenPort = 0;
this->dgramListener = new DatagramListener(netFilter, localNicList, ackStore, udpListenPort,
this->cfg->getConnRestrictOutboundInterfaces());
// update the local node info with udp port
this->localNode->updateInterfaces(dgramListener->getUDPPort(), 0, this->localNicList);
this->internodeSyncer = new InternodeSyncer();
workersInit();
this->log->log(Log_DEBUG, "Components initialized.");
}
void App::startComponents()
{
log->log(Log_SPAM, "Starting up components...");
// make sure child threads don't receive SIGINT/SIGTERM (blocked signals are inherited)
PThread::blockInterruptSignals();
dgramListener->start();
internodeSyncer->start();
internodeSyncer->waitForServers();
workersStart();
PThread::unblockInterruptSignals(); // main app thread may receive SIGINT/SIGTERM
log->log(Log_DEBUG, "Components running.");
}
void App::stopComponents()
{
// note: this method may not wait for termination of the components, because that could
// lead to a deadlock (when calling from signal handler)
workersStop();
if(this->internodeSyncer)
this->internodeSyncer->selfTerminate();
if ( dgramListener )
{
dgramListener->selfTerminate();
dgramListener->sendDummyToSelfUDP();
}
this->selfTerminate();
}
void App::joinComponents()
{
log->log(4, "Joining component threads...");
this->internodeSyncer->join();
/* (note: we need one thread for which we do an untimed join, so this should be a quite reliably
terminating thread) */
this->dgramListener->join();
workersJoin();
}
void App::workersInit()
{
unsigned numWorkers = cfg->getTuneNumWorkers();
for(unsigned i=0; i < numWorkers; i++)
{
Worker* worker = new Worker(
std::string("Worker") + StringTk::intToStr(i+1), workQueue, QueueWorkType_INDIRECT);
workerList.push_back(worker);
}
for(unsigned i=0; i < APP_WORKERS_DIRECT_NUM; i++)
{
Worker* worker = new Worker(
std::string("DirectWorker") + StringTk::intToStr(i+1), workQueue, QueueWorkType_DIRECT);
workerList.push_back(worker);
}
}
void App::workersStart()
{
for ( WorkerListIter iter = workerList.begin(); iter != workerList.end(); iter++ )
{
(*iter)->start();
}
}
void App::workersStop()
{
// need two loops because we don't know if the worker that handles the work will be the same that
// received the self-terminate-request
for(WorkerListIter iter = workerList.begin(); iter != workerList.end(); iter++)
{
(*iter)->selfTerminate();
}
for(WorkerListIter iter = workerList.begin(); iter != workerList.end(); iter++)
{
workQueue->addDirectWork(new DummyWork() );
}
}
void App::workersDelete()
{
for(WorkerListIter iter = workerList.begin(); iter != workerList.end(); iter++)
{
delete(*iter);
}
workerList.clear();
}
void App::workersJoin()
{
for(WorkerListIter iter = workerList.begin(); iter != workerList.end(); iter++)
{
waitForComponentTermination(*iter);
}
}
void App::logInfos()
{
// print software version (BEEGFS_VERSION)
log->log(Log_NOTICE, std::string("Version: ") + BEEGFS_VERSION);
// print debug version info
LOG_DEBUG_CONTEXT(*log, Log_CRITICAL, "--DEBUG VERSION--");
// print local nodeID
log->log(Log_NOTICE, std::string("LocalNode: ") + localNode->getTypedNodeID() );
// list usable network interfaces
NicAddressList nicList(localNode->getNicList());
logUsableNICs(log, nicList);
// print net filters
if ( netFilter->getNumFilterEntries() )
{
this->log->log(2,
std::string("Net filters: ") + StringTk::uintToStr(netFilter->getNumFilterEntries()));
}
if(tcpOnlyFilter->getNumFilterEntries() )
{
this->log->log(Log_WARNING, std::string("TCP-only filters: ") +
StringTk::uintToStr(tcpOnlyFilter->getNumFilterEntries() ) );
}
}
/**
* Request mgmt heartbeat and wait for the mgmt node to appear in nodestore.
*
* @return true if mgmt heartbeat received, false on error or thread selftermination order
*/
bool App::waitForMgmtNode()
{
const unsigned waitTimeoutMS = 0; // infinite wait
const unsigned nameResolutionRetries = 3;
// choose a random udp port here
unsigned udpListenPort = 0;
unsigned udpMgmtdPort = cfg->getConnMgmtdPort();
std::string mgmtdHost = cfg->getSysMgmtdHost();
RegistrationDatagramListener regDGramLis(this->netFilter, this->localNicList, this->ackStore,
udpListenPort, this->cfg->getConnRestrictOutboundInterfaces());
regDGramLis.start();
log->log(Log_CRITICAL, "Waiting for beegfs-mgmtd@" +
mgmtdHost + ":" + StringTk::uintToStr(udpMgmtdPort) + "...");
bool gotMgmtd = NodesTk::waitForMgmtHeartbeat(
this, &regDGramLis, this->mgmtNodes, mgmtdHost, udpMgmtdPort, waitTimeoutMS,
nameResolutionRetries);
regDGramLis.selfTerminate();
regDGramLis.sendDummyToSelfUDP(); // for faster termination
regDGramLis.join();
return gotMgmtd;
}
void App::updateLocalNicList(NicAddressList& localNicList)
{
std::vector<AbstractNodeStore*> allNodes({ mgmtNodes, metaNodes, storageNodes});
updateLocalNicListAndRoutes(log, localNicList, allNodes);
localNode->updateInterfaces(0, 0, localNicList);
dgramListener->setLocalNicList(localNicList);
}
/*
* Handles expections that lead to the termination of a component.
* Initiates an application shutdown.
*/
void App::handleComponentException(std::exception& e)
{
const char* logContext = "App (component exception handler)";
LogContext log(logContext);
const auto componentName = PThread::getCurrentThreadName();
log.logErr(
"The component [" + componentName + "] encountered an unrecoverable error. " +
std::string("[SysErr: ") + System::getErrString() + "] " +
std::string("Exception message: ") + e.what() );
log.log(2, "Shutting down...");
shallAbort.set(1);
stopComponents();
}
void App::handleNetworkInterfaceFailure(const std::string& devname)
{
LOG(GENERAL, ERR, "Network interface failure.",
("Device", devname));
internodeSyncer->setForceCheckNetwork();
}
void App::registerSignalHandler()
{
signal(SIGINT, App::signalHandler);
signal(SIGTERM, App::signalHandler);
}
void App::signalHandler(int sig)
{
App* app = Program::getApp();
Logger* log = Logger::getLogger();
const char* logContext = "App::signalHandler";
// note: this might deadlock if the signal was thrown while the logger mutex is locked by the
// application thread (depending on whether the default mutex style is recursive). but
// even recursive mutexes are not acceptable in this case.
// we need something like a timed lock for the log mutex. if it succeeds within a
// few seconds, we know that we didn't hold the mutex lock. otherwise we simply skip the
// log message. this will only work if the mutex is non-recusive (which is unknown for
// the default mutex style).
// but it is very unlikely that the application thread holds the log mutex, because it
// joins the component threads and so it doesn't do anything else but sleeping!
switch(sig)
{
case SIGINT:
{
signal(sig, SIG_DFL); // reset the handler to its default
log->log(1, logContext, "Received a SIGINT. Shutting down...");
}
break;
case SIGTERM:
{
signal(sig, SIG_DFL); // reset the handler to its default
log->log(1, logContext, "Received a SIGTERM. Shutting down...");
}
break;
default:
{
signal(sig, SIG_DFL); // reset the handler to its default
log->log(1, logContext, "Received an unknown signal. Shutting down...");
}
break;
}
app->abort();
}
void App::abort()
{
shallAbort.set(1);
stopComponents();
}

223
fsck/source/app/App.h Normal file
View File

@@ -0,0 +1,223 @@
#ifndef APP_H_
#define APP_H_
#include <app/config/Config.h>
#include <common/app/log/LogContext.h>
#include <common/app/log/Logger.h>
#include <common/app/AbstractApp.h>
#include <common/nodes/NodeStore.h>
#include <common/toolkit/AcknowledgmentStore.h>
#include <common/toolkit/NetFilter.h>
#include <common/toolkit/NodesTk.h>
#include <common/Common.h>
#include <common/components/worker/Worker.h>
#include <common/components/worker/DummyWork.h>
#include <common/components/ComponentInitException.h>
#include <common/net/sock/RDMASocket.h>
#include <common/nodes/RootInfo.h>
#include <common/nodes/TargetMapper.h>
#include <common/nodes/LocalNode.h>
#include <components/DatagramListener.h>
#include <components/InternodeSyncer.h>
#include <components/ModificationEventHandler.h>
#include <modes/ModeHelp.h>
#include <modes/ModeCheckFS.h>
#include <modes/ModeEnableQuota.h>
#include <net/message/NetMessageFactory.h>
#ifndef BEEGFS_VERSION
#error BEEGFS_VERSION undefined
#endif
// program return codes
#define APPCODE_NO_ERROR 0
#define APPCODE_INVALID_CONFIG 1
#define APPCODE_INITIALIZATION_ERROR 2
#define APPCODE_COMMUNICATION_ERROR 3
#define APPCODE_RUNTIME_ERROR 4
#define APPCODE_USER_ABORTED 5
typedef std::list<Worker*> WorkerList;
typedef WorkerList::iterator WorkerListIter;
// forward declarations
class LogContext;
class App : public AbstractApp
{
public:
App(int argc, char** argv);
virtual ~App();
virtual void run() override;
void abort();
virtual void handleComponentException(std::exception& e) override;
virtual void handleNetworkInterfaceFailure(const std::string& devname) override;
bool getShallAbort()
{
return (shallAbort.read() != 0);
}
private:
int appResult;
int argc;
char** argv;
AtomicSizeT shallAbort;
Config* cfg;
LogContext* log;
NetFilter* netFilter;
NetFilter* tcpOnlyFilter; // for IPs that allow only plain TCP (no RDMA etc)
std::list<std::string> allowedInterfaces;
std::shared_ptr<Node> localNode;
NodeStore* mgmtNodes;
NodeStore* metaNodes;
NodeStore* storageNodes;
RootInfo metaRoot;
InternodeSyncer* internodeSyncer;
TargetMapper* targetMapper;
TargetStateStore* targetStateStore;
MirrorBuddyGroupMapper* buddyGroupMapper;
MirrorBuddyGroupMapper* metaBuddyGroupMapper;
MultiWorkQueue* workQueue;
AcknowledgmentStore* ackStore;
NetMessageFactory* netMessageFactory;
DatagramListener* dgramListener;
WorkerList workerList;
ModificationEventHandler* modificationEventHandler;
Mode* runMode;
void runNormal();
void workersInit();
void workersStart();
void workersStop();
void workersDelete();
void workersJoin();
void initDataObjects(int argc, char** argv);
void initLocalNodeInfo();
void initComponents();
bool initRunMode();
void startComponents();
void joinComponents();
virtual void stopComponents() override;
void logInfos();
bool waitForMgmtNode();
void registerSignalHandler();
static void signalHandler(int sig);
public:
virtual const ICommonConfig* getCommonConfig() const override
{
return cfg;
}
virtual const AbstractNetMessageFactory* getNetMessageFactory() const override
{
return netMessageFactory;
}
virtual const NetFilter* getNetFilter() const override
{
return netFilter;
}
virtual const NetFilter* getTcpOnlyFilter() const override
{
return tcpOnlyFilter;
}
Config* getConfig() const
{
return cfg;
}
DatagramListener* getDatagramListener() const
{
return dgramListener;
}
int getAppResult() const
{
return appResult;
}
NodeStore* getMgmtNodes() const
{
return mgmtNodes;
}
NodeStore* getMetaNodes()
{
return metaNodes;
}
NodeStore* getStorageNodes() const
{
return storageNodes;
}
Node& getLocalNode() const
{
return *localNode;
}
void updateLocalNicList(NicAddressList& localNicList);
MultiWorkQueue* getWorkQueue()
{
return workQueue;
}
InternodeSyncer* getInternodeSyncer()
{
return internodeSyncer;
}
TargetMapper* getTargetMapper()
{
return targetMapper;
}
TargetStateStore* getTargetStateStore()
{
return targetStateStore;
}
MirrorBuddyGroupMapper* getMirrorBuddyGroupMapper()
{
return buddyGroupMapper;
}
MirrorBuddyGroupMapper* getMetaMirrorBuddyGroupMapper()
{
return metaBuddyGroupMapper;
}
ModificationEventHandler* getModificationEventHandler()
{
return modificationEventHandler;
}
void setModificationEventHandler(ModificationEventHandler* handler)
{
modificationEventHandler = handler;
}
const RootInfo& getMetaRoot() const { return metaRoot; }
RootInfo& getMetaRoot() { return metaRoot; }
void findAllowedInterfaces(NicAddressList& outList) const;
void findAllowedRDMAInterfaces(NicAddressList& outList) const;
};
#endif // APP_H_

View File

@@ -0,0 +1,317 @@
#include <common/toolkit/StringTk.h>
#include "Config.h"
#define IGNORE_CONFIG_CLIENT_VALUE(keyStr) /* to be used in applyConfigMap() */ \
if(testConfigMapKeyMatch(iter, keyStr, addDashes) ) \
; \
else
// Note: Keep in sync with enum RunMode
RunModesElem const __RunModes[] =
{
{ "--checkfs", RunMode_CHECKFS },
{ "--enablequota", RunMode_ENABLEQUOTA },
{ NULL, RunMode_INVALID }
};
Config::Config(int argc, char** argv) :
AbstractConfig(argc, argv)
{
initConfig(argc, argv, false, true);
logType = LogType_LOGFILE;
}
/**
* Determine RunMode from config.
* If a valid RunMode exists in the config, the corresponding config element will be erased.
*/
enum RunMode Config::determineRunMode()
{
/* test for given help argument, e.g. in case the user wants to see mode-specific help with
arguments "--help --<mode>". */
StringMapIter iter = configMap.find(RUNMODE_HELP_KEY_STRING);
if(iter != configMap.end() )
{ // user did specify "--help"
/* note: it's important to remove the help arg here, because mode help will call this again
to find out whether user wants to see mode-specific help. */
eraseFromConfigMap(iter);
return RunMode_HELP;
}
// walk all defined modes to check whether we find any of them in the config
for(int i=0; __RunModes[i].modeString != NULL; i++)
{
iter = configMap.find(__RunModes[i].modeString);
if(iter != configMap.end() )
{ // we found a valid mode in the config
eraseFromConfigMap(iter);
return __RunModes[i].runMode;
}
}
// no valid mode found
return RunMode_INVALID;
}
/**
* Sets the default values for each configurable in the configMap.
*
* @param addDashes true to prepend "--" to all config keys.
*/
void Config::loadDefaults(bool addDashes)
{
AbstractConfig::loadDefaults(addDashes);
// re-definitions
configMapRedefine("cfgFile", createDefaultCfgFilename(), addDashes);
// own definitions
configMapRedefine("connInterfacesFile", "", addDashes);
configMapRedefine("tuneNumWorkers", "32", addDashes);
configMapRedefine("tunePreferredNodesFile", "", addDashes);
configMapRedefine("tuneDbFragmentSize", "0", addDashes);
configMapRedefine("tuneDentryCacheSize", "0", addDashes);
configMapRedefine("runDaemonized", "false", addDashes);
configMapRedefine("databasePath", CONFIG_DEFAULT_DBPATH, addDashes);
configMapRedefine("overwriteDbFile", "false", addDashes);
configMapRedefine("testDatabasePath", CONFIG_DEFAULT_TESTDBPATH, addDashes);
configMapRedefine("DatabaseNumMaxConns", "16", addDashes);
configMapRedefine("overrideRootMDS", "", addDashes);
configMapRedefine("logStdFile", CONFIG_DEFAULT_LOGFILE, addDashes);
configMapRedefine("logOutFile", CONFIG_DEFAULT_OUTFILE, addDashes);
configMapRedefine("logNoDate", "false", addDashes);
configMapRedefine("readOnly", "false", addDashes);
configMapRedefine("noFetch", "false", addDashes);
configMapRedefine("automatic", "false", addDashes);
configMapRedefine("runOffline", "false", addDashes);
configMapRedefine("forceRestart", "false", addDashes);
configMapRedefine("quotaEnabled", "false", addDashes);
configMapRedefine("ignoreDBDiskSpace", "false", addDashes);
}
/**
* @param addDashes true to prepend "--" to tested config keys for matching.
*/
void Config::applyConfigMap(bool enableException, bool addDashes)
{
AbstractConfig::applyConfigMap(false, addDashes);
for (StringMapIter iter = configMap.begin(); iter != configMap.end();)
{
bool unknownElement = false;
IGNORE_CONFIG_CLIENT_VALUE("logClientID")
IGNORE_CONFIG_CLIENT_VALUE("logType")
IGNORE_CONFIG_CLIENT_VALUE("connNumCommRetries")
IGNORE_CONFIG_CLIENT_VALUE("connUnmountRetries")
IGNORE_CONFIG_CLIENT_VALUE("connCommRetrySecs")
IGNORE_CONFIG_CLIENT_VALUE("connMaxConcurrentAttempts")
IGNORE_CONFIG_CLIENT_VALUE("connRDMAInterfacesFile")
IGNORE_CONFIG_CLIENT_VALUE("connTCPFallbackEnabled")
IGNORE_CONFIG_CLIENT_VALUE("connMessagingTimeouts")
IGNORE_CONFIG_CLIENT_VALUE("connInterfacesList")
IGNORE_CONFIG_CLIENT_VALUE("connRDMATimeouts")
IGNORE_CONFIG_CLIENT_VALUE("connRDMAFragmentSize")
IGNORE_CONFIG_CLIENT_VALUE("connRDMAKeyType")
IGNORE_CONFIG_CLIENT_VALUE("connRDMAMetaFragmentSize")
IGNORE_CONFIG_CLIENT_VALUE("connRDMAMetaBufNum")
IGNORE_CONFIG_CLIENT_VALUE("connRDMAMetaBufSize")
IGNORE_CONFIG_CLIENT_VALUE("tuneFileCacheType")
IGNORE_CONFIG_CLIENT_VALUE("tunePagedIOBufSize")
IGNORE_CONFIG_CLIENT_VALUE("tunePagedIOBufNum")
IGNORE_CONFIG_CLIENT_VALUE("tuneFileCacheBufSize")
IGNORE_CONFIG_CLIENT_VALUE("tuneFileCacheBufNum")
IGNORE_CONFIG_CLIENT_VALUE("tunePageCacheValidityMS")
IGNORE_CONFIG_CLIENT_VALUE("tuneAttribCacheValidityMS")
IGNORE_CONFIG_CLIENT_VALUE("tuneMaxWriteWorks")
IGNORE_CONFIG_CLIENT_VALUE("tuneMaxReadWorks")
IGNORE_CONFIG_CLIENT_VALUE("tuneAllowMultiSetWrite")
IGNORE_CONFIG_CLIENT_VALUE("tuneAllowMultiSetRead")
IGNORE_CONFIG_CLIENT_VALUE("tunePathBufSize")
IGNORE_CONFIG_CLIENT_VALUE("tunePathBufNum")
IGNORE_CONFIG_CLIENT_VALUE("tuneMaxReadWriteNum")
IGNORE_CONFIG_CLIENT_VALUE("tuneMaxReadWriteNodesNum")
IGNORE_CONFIG_CLIENT_VALUE("tuneMsgBufSize")
IGNORE_CONFIG_CLIENT_VALUE("tuneMsgBufNum")
IGNORE_CONFIG_CLIENT_VALUE("tuneRemoteFSync")
IGNORE_CONFIG_CLIENT_VALUE("tunePreferredMetaFile")
IGNORE_CONFIG_CLIENT_VALUE("tunePreferredStorageFile")
IGNORE_CONFIG_CLIENT_VALUE("tuneUseGlobalFileLocks")
IGNORE_CONFIG_CLIENT_VALUE("tuneRefreshOnGetAttr")
IGNORE_CONFIG_CLIENT_VALUE("tuneInodeBlockBits")
IGNORE_CONFIG_CLIENT_VALUE("tuneInodeBlockSize")
IGNORE_CONFIG_CLIENT_VALUE("tuneMaxClientMirrorSize") // was removed, kept here for compat
IGNORE_CONFIG_CLIENT_VALUE("tuneEarlyCloseResponse")
IGNORE_CONFIG_CLIENT_VALUE("tuneUseGlobalAppendLocks")
IGNORE_CONFIG_CLIENT_VALUE("tuneUseBufferedAppend")
IGNORE_CONFIG_CLIENT_VALUE("tuneStatFsCacheSecs")
IGNORE_CONFIG_CLIENT_VALUE("sysCacheInvalidationVersion")
IGNORE_CONFIG_CLIENT_VALUE("sysCreateHardlinksAsSymlinks")
IGNORE_CONFIG_CLIENT_VALUE("sysMountSanityCheckMS")
IGNORE_CONFIG_CLIENT_VALUE("sysSyncOnClose")
IGNORE_CONFIG_CLIENT_VALUE("sysSessionCheckOnClose")
IGNORE_CONFIG_CLIENT_VALUE("sysSessionChecksEnabled")
IGNORE_CONFIG_CLIENT_VALUE("sysTargetOfflineTimeoutSecs")
IGNORE_CONFIG_CLIENT_VALUE("sysInodeIDStyle")
IGNORE_CONFIG_CLIENT_VALUE("sysACLsEnabled")
IGNORE_CONFIG_CLIENT_VALUE("sysXAttrsEnabled")
IGNORE_CONFIG_CLIENT_VALUE("sysBypassFileAccessCheckOnMeta")
IGNORE_CONFIG_CLIENT_VALUE("sysXAttrsCheckCapabilities")
IGNORE_CONFIG_CLIENT_VALUE("tuneDirSubentryCacheValidityMS")
IGNORE_CONFIG_CLIENT_VALUE("tuneFileSubentryCacheValidityMS")
IGNORE_CONFIG_CLIENT_VALUE("tuneENOENTCacheValidityMS")
IGNORE_CONFIG_CLIENT_VALUE("tuneCoherentBuffers")
IGNORE_CONFIG_CLIENT_VALUE("sysFileEventLogMask")
IGNORE_CONFIG_CLIENT_VALUE("sysRenameEbusyAsXdev")
IGNORE_CONFIG_CLIENT_VALUE("tuneNumRetryWorkers")
IGNORE_CONFIG_CLIENT_VALUE("connHelperdPortTCP") // was removed, kept here for compat
if (testConfigMapKeyMatch(iter, "connInterfacesFile", addDashes))
connInterfacesFile = iter->second;
else if (testConfigMapKeyMatch(iter, "tuneNumWorkers", addDashes))
tuneNumWorkers = StringTk::strToUInt(iter->second);
else if (testConfigMapKeyMatch(iter, "tunePreferredNodesFile", addDashes))
tunePreferredNodesFile = iter->second;
else if (testConfigMapKeyMatch(iter, "tuneDbFragmentSize", addDashes))
tuneDbFragmentSize = StringTk::strToUInt64(iter->second.c_str());
else if (testConfigMapKeyMatch(iter, "tuneDentryCacheSize", addDashes))
tuneDentryCacheSize = StringTk::strToUInt64(iter->second.c_str());
else if (testConfigMapKeyMatch(iter, "runDaemonized", addDashes))
runDaemonized = StringTk::strToBool(iter->second);
else if (testConfigMapKeyMatch(iter, "databasePath", addDashes))
databasePath = iter->second;
else if (testConfigMapKeyMatch(iter, "overwriteDbFile", addDashes))
overwriteDbFile = StringTk::strToBool(iter->second);
else if (testConfigMapKeyMatch(iter, "testDatabasePath", addDashes))
testDatabasePath = iter->second;
else if (testConfigMapKeyMatch(iter, "databaseNumMaxConns", addDashes))
databaseNumMaxConns = StringTk::strHexToUInt(iter->second);
else if (testConfigMapKeyMatch(iter, "overrideRootMDS", addDashes))
overrideRootMDS = iter->second;
else if (testConfigMapKeyMatch(iter, "logStdFile", addDashes))
logStdFile = iter->second;
else if (testConfigMapKeyMatch(iter, "logOutFile", addDashes))
logOutFile = iter->second;
else if (testConfigMapKeyMatch(iter, "readOnly", addDashes))
readOnly = StringTk::strToBool(iter->second);
else if (testConfigMapKeyMatch(iter, "noFetch", addDashes))
noFetch = StringTk::strToBool(iter->second);
else if (testConfigMapKeyMatch(iter, "automatic", addDashes))
automatic = StringTk::strToBool(iter->second);
else if (testConfigMapKeyMatch(iter, "runOffline", addDashes))
runOffline = StringTk::strToBool(iter->second);
else if (testConfigMapKeyMatch(iter, "forceRestart", addDashes))
forceRestart = StringTk::strToBool(iter->second);
else if (testConfigMapKeyMatch(iter, "quotaEnabled", addDashes))
quotaEnabled = StringTk::strToBool(iter->second);
else if (testConfigMapKeyMatch(iter, "ignoreDBDiskSpace", addDashes))
ignoreDBDiskSpace = StringTk::strToBool(iter->second);
else if (testConfigMapKeyMatch(iter, "checkMalformedChunk", addDashes))
checkFsActions.set(CHECK_MALFORMED_CHUNK);
else if (testConfigMapKeyMatch(iter, "checkFilesWithMissingTargets", addDashes))
checkFsActions.set(CHECK_FILES_WITH_MISSING_TARGETS);
else if (testConfigMapKeyMatch(iter, "checkOrphanedDentryByIDFiles", addDashes))
checkFsActions.set(CHECK_ORPHANED_DENTRY_BYIDFILES);
else if (testConfigMapKeyMatch(iter, "checkDirEntriesWithBrokenIDFile", addDashes))
checkFsActions.set(CHECK_DIRENTRIES_WITH_BROKENIDFILE);
else if (testConfigMapKeyMatch(iter, "checkOrphanedChunk", addDashes))
checkFsActions.set(CHECK_ORPHANED_CHUNK);
else if (testConfigMapKeyMatch(iter, "checkChunksInWrongPath", addDashes))
checkFsActions.set(CHECK_CHUNKS_IN_WRONGPATH);
else if (testConfigMapKeyMatch(iter, "checkWrongInodeOwner", addDashes))
checkFsActions.set(CHECK_WRONG_INODE_OWNER);
else if (testConfigMapKeyMatch(iter, "checkWrongOwnerInDentry", addDashes))
checkFsActions.set(CHECK_WRONG_OWNER_IN_DENTRY);
else if (testConfigMapKeyMatch(iter, "checkOrphanedContDir", addDashes))
checkFsActions.set(CHECK_ORPHANED_CONT_DIR);
else if (testConfigMapKeyMatch(iter, "checkOrphanedDirInode", addDashes))
checkFsActions.set(CHECK_ORPHANED_DIR_INODE);
else if (testConfigMapKeyMatch(iter, "checkOrphanedFileInode", addDashes))
checkFsActions.set(CHECK_ORPHANED_FILE_INODE);
else if (testConfigMapKeyMatch(iter, "checkDanglingDentry", addDashes))
checkFsActions.set(CHECK_DANGLING_DENTRY);
else if (testConfigMapKeyMatch(iter, "checkMissingContDir", addDashes))
checkFsActions.set(CHECK_MISSING_CONT_DIR);
else if (testConfigMapKeyMatch(iter, "checkWrongFileAttribs", addDashes))
checkFsActions.set(CHECK_WRONG_FILE_ATTRIBS);
else if (testConfigMapKeyMatch(iter, "checkWrongDirAttribs", addDashes))
checkFsActions.set(CHECK_WRONG_DIR_ATTRIBS);
else if (testConfigMapKeyMatch(iter, "checkOldStyledHardlinks", addDashes))
checkFsActions.set(CHECK_OLD_STYLED_HARDLINKS);
else
{
// unknown element occurred
unknownElement = true;
if (enableException)
{
throw InvalidConfigException("The config argument '" + iter->first + "' is invalid");
}
}
// advance iterator (and remove handled element)
if (unknownElement)
{
// just skip the unknown element
iter++;
}
else
{
// remove this element from the map
iter = eraseFromConfigMap(iter);
}
}
}
void Config::initImplicitVals()
{
// tuneNumWorkers
if (!tuneNumWorkers)
tuneNumWorkers = BEEGFS_MAX(System::getNumOnlineCPUs() * 2, 4);
if (!tuneDbFragmentSize)
tuneDbFragmentSize = uint64_t(sysconf(_SC_PHYS_PAGES) ) * sysconf(_SC_PAGESIZE) / 2;
// just blindly assume that 384 bytes will be enough for a single cache entry. should be
if (!tuneDentryCacheSize)
tuneDentryCacheSize = tuneDbFragmentSize / 384;
// read in connAuthFile only if we are running as root.
// if not root, the program will abort anyway
if(!geteuid())
{
AbstractConfig::initConnAuthHash(connAuthFile, &connAuthHash);
}
}
std::string Config::createDefaultCfgFilename() const
{
struct stat statBuf;
const int statRes = stat(CONFIG_DEFAULT_CFGFILENAME, &statBuf);
if (!statRes && S_ISREG(statBuf.st_mode))
return CONFIG_DEFAULT_CFGFILENAME; // there appears to be a config file
return ""; // no default file otherwise
}

View File

@@ -0,0 +1,224 @@
#ifndef CONFIG_H_
#define CONFIG_H_
#include <common/app/config/AbstractConfig.h>
#include <bitset>
#define CONFIG_DEFAULT_CFGFILENAME "/etc/beegfs/beegfs-client.conf"
#define CONFIG_DEFAULT_LOGFILE "/var/log/beegfs-fsck.log"
#define CONFIG_DEFAULT_OUTFILE "/var/log/beegfs-fsck.out"
#define CONFIG_DEFAULT_DBPATH "/var/lib/beegfs/"
#define CONFIG_DEFAULT_TESTDBPATH "/tmp/beegfs-fsck/"
#define RUNMODE_HELP_KEY_STRING "--help" /* key for usage help */
#define __RUNMODES_SIZE \
( (sizeof(__RunModes) ) / (sizeof(RunModesElem) ) - 1)
/* -1 because last elem is NULL */
enum CheckFsActions
{
CHECK_MALFORMED_CHUNK = 0,
CHECK_FILES_WITH_MISSING_TARGETS = 1,
CHECK_ORPHANED_DENTRY_BYIDFILES = 2,
CHECK_DIRENTRIES_WITH_BROKENIDFILE = 3,
CHECK_ORPHANED_CHUNK = 4,
CHECK_CHUNKS_IN_WRONGPATH = 5,
CHECK_WRONG_INODE_OWNER = 6,
CHECK_WRONG_OWNER_IN_DENTRY = 7,
CHECK_ORPHANED_CONT_DIR = 8,
CHECK_ORPHANED_DIR_INODE = 9,
CHECK_ORPHANED_FILE_INODE = 10,
CHECK_DANGLING_DENTRY = 11,
CHECK_MISSING_CONT_DIR = 12,
CHECK_WRONG_FILE_ATTRIBS = 13,
CHECK_WRONG_DIR_ATTRIBS = 14,
CHECK_OLD_STYLED_HARDLINKS = 15,
CHECK_FS_ACTIONS_COUNT = 16
};
// Note: Keep in sync with __RunModes array
enum RunMode
{
RunMode_CHECKFS = 0,
RunMode_ENABLEQUOTA = 1,
RunMode_HELP = 2,
RunMode_INVALID = 3 /* not valid as index in RunModes array */
};
struct RunModesElem
{
const char* modeString;
enum RunMode runMode;
};
extern RunModesElem const __RunModes[];
class Config : public AbstractConfig
{
public:
Config(int argc, char** argv);
enum RunMode determineRunMode();
private:
// configurables
std::string connInterfacesFile;
unsigned tuneNumWorkers;
std::string tunePreferredNodesFile;
size_t tuneDbFragmentSize;
size_t tuneDentryCacheSize;
bool runDaemonized;
std::string databasePath;
bool overwriteDbFile;
// only relevant for unit testing, to give the used databasePath
std::string testDatabasePath;
unsigned databaseNumMaxConns;
std::string overrideRootMDS; // not tested well, should only be used by developers
// file for fsck output (not the log messages, but the output, which is also on the console)
std::string logOutFile;
bool readOnly;
bool noFetch;
bool automatic;
bool runOffline;
bool forceRestart;
bool quotaEnabled;
bool ignoreDBDiskSpace;
std::bitset<CHECK_FS_ACTIONS_COUNT> checkFsActions;
// internals
virtual void loadDefaults(bool addDashes) override;
virtual void applyConfigMap(bool enableException, bool addDashes) override;
virtual void initImplicitVals() override;
std::string createDefaultCfgFilename() const;
public:
// getters & setters
const StringMap* getUnknownConfigArgs() const
{
return getConfigMap();
}
const std::string& getConnInterfacesFile() const
{
return connInterfacesFile;
}
unsigned getTuneNumWorkers() const
{
return tuneNumWorkers;
}
size_t getTuneDbFragmentSize() const
{
return tuneDbFragmentSize;
}
size_t getTuneDentryCacheSize() const
{
return tuneDentryCacheSize;
}
const std::string& getTunePreferredNodesFile() const
{
return tunePreferredNodesFile;
}
bool getRunDaemonized() const
{
return runDaemonized;
}
const std::string& getDatabasePath() const
{
return databasePath;
}
bool getOverwriteDbFile() const
{
return overwriteDbFile;
}
const std::string& getTestDatabasePath() const
{
return testDatabasePath;
}
unsigned getDatabaseNumMaxConns() const
{
return databaseNumMaxConns;
}
std::string getOverrideRootMDS() const
{
return overrideRootMDS;
}
bool getReadOnly() const
{
return readOnly;
}
bool getNoFetch() const
{
return noFetch;
}
bool getAutomatic() const
{
return automatic;
}
const std::string& getLogOutFile() const
{
return logOutFile;
}
bool getRunOffline() const
{
return runOffline;
}
bool getForceRestart() const
{
return forceRestart;
}
bool getQuotaEnabled() const
{
return quotaEnabled;
}
bool getIgnoreDBDiskSpace() const
{
return ignoreDBDiskSpace;
}
std::bitset<CHECK_FS_ACTIONS_COUNT> getCheckFsActions() const
{
return checkFsActions;
}
void disableAutomaticRepairMode()
{
this->automatic = false;
}
void setReadOnly()
{
this->readOnly = true;
}
};
#endif /*CONFIG_H_*/

View File

@@ -0,0 +1,203 @@
#include "DataFetcher.h"
#include <common/Common.h>
#include <common/storage/Storagedata.h>
#include <components/worker/RetrieveChunksWork.h>
#include <components/worker/RetrieveDirEntriesWork.h>
#include <components/worker/RetrieveFsIDsWork.h>
#include <components/worker/RetrieveInodesWork.h>
#include <program/Program.h>
#include <toolkit/FsckTkEx.h>
#include <toolkit/FsckException.h>
DataFetcher::DataFetcher(FsckDB& db, bool forceRestart)
: database(&db),
workQueue(Program::getApp()->getWorkQueue() ),
generatedPackages(0),
forceRestart(forceRestart)
{
}
FhgfsOpsErr DataFetcher::execute()
{
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
NodeStore* metaNodeStore = Program::getApp()->getMetaNodes();
auto metaNodeList = metaNodeStore->referenceAllNodes();
printStatus();
retrieveDirEntries(metaNodeList);
retrieveInodes(metaNodeList);
const bool retrieveRes = retrieveChunks();
if (!retrieveRes)
{
retVal = FhgfsOpsErr_INUSE;
Program::getApp()->abort();
}
// wait for all packages to finish, because we cannot proceed if not all data was fetched
// BUT : update output each OUTPUT_INTERVAL_MS ms
while (!finishedPackages.timedWaitForCount(generatedPackages, DATAFETCHER_OUTPUT_INTERVAL_MS))
{
printStatus();
if (retVal != FhgfsOpsErr_INUSE && Program::getApp()->getShallAbort())
{
// setting retVal to INTERRUPTED
// but still, we needed to wait for the workers to terminate, because of the
// SynchronizedCounter (this object cannnot be destroyed before all workers terminate)
retVal = FhgfsOpsErr_INTERRUPTED;
}
}
if (retVal == FhgfsOpsErr_SUCCESS && fatalErrorsFound.read() > 0)
retVal = FhgfsOpsErr_INTERNAL;
if(retVal == FhgfsOpsErr_SUCCESS)
{
std::set<FsckTargetID> allUsedTargets;
while(!this->usedTargets.empty() )
{
allUsedTargets.insert(this->usedTargets.front().begin(), this->usedTargets.front().end() );
this->usedTargets.pop_front();
}
std::list<FsckTargetID> usedTargetsList(allUsedTargets.begin(), allUsedTargets.end() );
this->database->getUsedTargetIDsTable()->insert(usedTargetsList,
this->database->getUsedTargetIDsTable()->newBulkHandle() );
}
printStatus(true);
FsckTkEx::fsckOutput(""); // just a new line
return retVal;
}
void DataFetcher::retrieveDirEntries(const std::vector<NodeHandle>& nodes)
{
for (auto nodeIter = nodes.begin(); nodeIter != nodes.end(); nodeIter++)
{
Node& node = **nodeIter;
int requestsPerNode = 2;
unsigned hashDirsPerRequest = (unsigned)(META_DENTRIES_LEVEL1_SUBDIR_NUM/requestsPerNode);
unsigned hashDirStart = 0;
unsigned hashDirEnd = 0;
do
{
hashDirEnd = hashDirStart + hashDirsPerRequest;
// fetch DirEntries
// before we create a package we increment the generated packages counter
this->generatedPackages++;
this->usedTargets.insert(this->usedTargets.end(), std::set<FsckTargetID>() );
this->workQueue->addIndirectWork(
new RetrieveDirEntriesWork(database, node, &finishedPackages, fatalErrorsFound,
hashDirStart, BEEGFS_MIN(hashDirEnd, META_DENTRIES_LEVEL1_SUBDIR_NUM - 1),
&numDentriesFound, &numFileInodesFound, usedTargets.back()));
// fetch fsIDs
// before we create a package we increment the generated packages counter
this->generatedPackages++;
this->workQueue->addIndirectWork(
new RetrieveFsIDsWork(database, node, &finishedPackages, fatalErrorsFound, hashDirStart,
BEEGFS_MIN(hashDirEnd, META_DENTRIES_LEVEL1_SUBDIR_NUM - 1)));
hashDirStart = hashDirEnd + 1;
} while (hashDirEnd < META_DENTRIES_LEVEL1_SUBDIR_NUM);
}
}
void DataFetcher::retrieveInodes(const std::vector<NodeHandle>& nodes)
{
for (auto nodeIter = nodes.begin(); nodeIter != nodes.end(); nodeIter++)
{
Node& node = **nodeIter;
int requestsPerNode = 2;
unsigned hashDirsPerRequest = (unsigned)(META_INODES_LEVEL1_SUBDIR_NUM/ requestsPerNode);
unsigned hashDirStart = 0;
unsigned hashDirEnd = 0;
do
{
// before we create a package we increment the generated packages counter
this->generatedPackages++;
this->usedTargets.insert(this->usedTargets.end(), std::set<FsckTargetID>() );
hashDirEnd = hashDirStart + hashDirsPerRequest;
this->workQueue->addIndirectWork(
new RetrieveInodesWork(database, node, &finishedPackages, fatalErrorsFound,
hashDirStart, BEEGFS_MIN(hashDirEnd, META_INODES_LEVEL1_SUBDIR_NUM - 1),
&numFileInodesFound, &numDirInodesFound, usedTargets.back()));
hashDirStart = hashDirEnd + 1;
} while (hashDirEnd < META_INODES_LEVEL1_SUBDIR_NUM);
}
}
/**
* Add the RetrieveChunksWork for each storage node to the work queue.
* @returns true if successful, false if work can't be started because another fsck is already
* running or was aborted prematurely
*/
bool DataFetcher::retrieveChunks()
{
App* app = Program::getApp();
NodeStore* storageNodes = app->getStorageNodes();
// for each server create a work package to retrieve chunks
for (const auto& node : storageNodes->referenceAllNodes())
{
// before we create a package we increment the generated packages counter
this->generatedPackages++;
RetrieveChunksWork* retrieveWork = new RetrieveChunksWork(database, node, &finishedPackages,
&numChunksFound, forceRestart);
// node will be released inside of work package
workQueue->addIndirectWork(retrieveWork);
bool started;
retrieveWork->waitForStarted(&started);
if (!started)
return false;
}
return true;
}
void DataFetcher::printStatus(bool toLogFile)
{
uint64_t dentryCount = numDentriesFound.read();
uint64_t fileInodeCount = numFileInodesFound.read();
uint64_t dirInodeCount = numDirInodesFound.read();
uint64_t chunkCount = numChunksFound.read();
std::string outputStr = "Fetched data > Directory entries: " + StringTk::uint64ToStr(dentryCount)
+ " | Inodes: " + StringTk::uint64ToStr(fileInodeCount+dirInodeCount) + " | Chunks: " +
StringTk::uint64ToStr(chunkCount);
int outputFlags = OutputOptions_LINEDELETE;
if (!toLogFile)
outputFlags = outputFlags | OutputOptions_NOLOG;
FsckTkEx::fsckOutput(outputStr, outputFlags);
}

View File

@@ -0,0 +1,43 @@
#ifndef DATAFETCHER_H_
#define DATAFETCHER_H_
#include <common/components/worker/queue/MultiWorkQueue.h>
#include <common/nodes/Node.h>
#include <common/toolkit/SynchronizedCounter.h>
#include <database/FsckDB.h>
#define DATAFETCHER_OUTPUT_INTERVAL_MS 2000
class DataFetcher
{
private:
FsckDB* database;
MultiWorkQueue* workQueue;
AtomicUInt64 numDentriesFound;
AtomicUInt64 numFileInodesFound;
AtomicUInt64 numDirInodesFound;
AtomicUInt64 numChunksFound;
public:
DataFetcher(FsckDB& db, bool forceRestart);
FhgfsOpsErr execute();
private:
SynchronizedCounter finishedPackages;
AtomicUInt64 fatalErrorsFound;
unsigned generatedPackages;
bool forceRestart;
std::list<std::set<FsckTargetID> > usedTargets;
void retrieveDirEntries(const std::vector<NodeHandle>& nodes);
void retrieveInodes(const std::vector<NodeHandle>& nodes);
bool retrieveChunks();
void printStatus(bool toLogFile = false);
};
#endif /* DATAFETCHER_H_ */

View File

@@ -0,0 +1,53 @@
#include "DatagramListener.h"
#include <common/net/message/NetMessageLogHelper.h>
DatagramListener::DatagramListener(NetFilter* netFilter, NicAddressList& localNicList,
AcknowledgmentStore* ackStore, unsigned short udpPort, bool restrictOutboundInterfaces) :
AbstractDatagramListener("DGramLis", netFilter, localNicList, ackStore, udpPort,
restrictOutboundInterfaces)
{
}
DatagramListener::~DatagramListener()
{
}
void DatagramListener::handleIncomingMsg(struct sockaddr_in* fromAddr, NetMessage* msg)
{
HighResolutionStats stats; // currently ignored
std::shared_ptr<StandardSocket> sock = findSenderSock(fromAddr->sin_addr);
if (sock == nullptr)
{
log.log(Log_WARNING, "Could not handle incoming message: no socket");
return;
}
NetMessage::ResponseContext rctx(fromAddr, sock.get(), sendBuf, DGRAMMGR_SENDBUF_SIZE, &stats);
const auto messageType = netMessageTypeToStr(msg->getMsgType());
switch(msg->getMsgType() )
{
// valid messages within this context
case NETMSGTYPE_Heartbeat:
case NETMSGTYPE_FsckModificationEvent:
{
if(!msg->processIncoming(rctx) )
{
LOG(GENERAL, WARNING,
"Problem encountered during handling of incoming message.", messageType);
}
} break;
default:
{ // valid, but not within this context
log.logErr(
"Received a message that is invalid within the current context "
"from: " + Socket::ipaddrToStr(fromAddr->sin_addr) + "; "
"type: " + messageType );
} break;
};
}

View File

@@ -0,0 +1,22 @@
#ifndef DATAGRAMLISTENER_H_
#define DATAGRAMLISTENER_H_
#include <common/components/AbstractDatagramListener.h>
class DatagramListener : public AbstractDatagramListener
{
public:
DatagramListener(NetFilter* netFilter, NicAddressList& localNicList,
AcknowledgmentStore* ackStore, unsigned short udpPort,
bool restrictOutboundInterfaces);
virtual ~DatagramListener();
protected:
virtual void handleIncomingMsg(struct sockaddr_in* fromAddr, NetMessage* msg);
private:
};
#endif /*DATAGRAMLISTENER_H_*/

View File

@@ -0,0 +1,491 @@
#include <app/config/Config.h>
#include <app/App.h>
#include <common/toolkit/MessagingTk.h>
#include <common/toolkit/NodesTk.h>
#include <common/toolkit/Time.h>
#include <common/nodes/NodeStore.h>
#include <common/nodes/TargetCapacityPools.h>
#include <program/Program.h>
#include "InternodeSyncer.h"
#include <mutex>
#include <boost/lexical_cast.hpp>
InternodeSyncer::InternodeSyncer() :
PThread("XNodeSync"),
log("XNodeSync"),
serversDownloaded(false)
{
}
InternodeSyncer::~InternodeSyncer()
{
}
void InternodeSyncer::run()
{
try
{
registerSignalHandler();
// download all nodes,mappings,states and buddy groups
NumNodeIDList addedStorageNodes;
NumNodeIDList removedStorageNodes;
NumNodeIDList addedMetaNodes;
NumNodeIDList removedMetaNodes;
bool syncRes = downloadAndSyncNodes(addedStorageNodes, removedStorageNodes, addedMetaNodes,
removedMetaNodes);
if (!syncRes)
{
log.logErr("Error downloading nodes from mgmtd.");
Program::getApp()->abort();
return;
}
syncRes = downloadAndSyncTargetMappings();
if (!syncRes)
{
log.logErr("Error downloading target mappings from mgmtd.");
Program::getApp()->abort();
return;
}
originalTargetMap = Program::getApp()->getTargetMapper()->getMapping();
syncRes = downloadAndSyncTargetStates();
if (!syncRes)
{
log.logErr("Error downloading target states from mgmtd.");
Program::getApp()->abort();
return;
}
syncRes = downloadAndSyncMirrorBuddyGroups();
if ( !syncRes )
{
log.logErr("Error downloading mirror buddy groups from mgmtd.");
Program::getApp()->abort();
return;
}
Program::getApp()->getMirrorBuddyGroupMapper()->getMirrorBuddyGroups(
originalMirrorBuddyGroupMap);
syncRes = downloadAndSyncMetaMirrorBuddyGroups();
if ( !syncRes )
{
log.logErr("Error downloading metadata mirror buddy groups from mgmtd.");
Program::getApp()->abort();
return;
}
Program::getApp()->getMetaMirrorBuddyGroupMapper()->getMirrorBuddyGroups(
originalMetaMirrorBuddyGroupMap);
{
std::lock_guard<Mutex> lock(serversDownloadedMutex);
serversDownloaded = true;
serversDownloadedCondition.signal();
}
syncLoop();
log.log(Log_DEBUG, "Component stopped.");
}
catch(std::exception& e)
{
PThread::getCurrentThreadApp()->handleComponentException(e);
}
}
void InternodeSyncer::syncLoop()
{
const int sleepIntervalMS = 3*1000; // 3sec
const unsigned downloadNodesAndStatesIntervalMS = 30000; // 30 sec
const unsigned checkNetworkIntervalMS = 60*1000; // 1 minute
Time lastDownloadNodesAndStatesT;
Time lastCheckNetworkT;
while(!waitForSelfTerminateOrder(sleepIntervalMS) )
{
// download & sync nodes
if (lastDownloadNodesAndStatesT.elapsedMS() > downloadNodesAndStatesIntervalMS)
{
NumNodeIDList addedStorageNodes;
NumNodeIDList removedStorageNodes;
NumNodeIDList addedMetaNodes;
NumNodeIDList removedMetaNodes;
bool syncRes = downloadAndSyncNodes(addedStorageNodes, removedStorageNodes, addedMetaNodes,
removedMetaNodes);
if (!syncRes)
{
log.logErr("Error downloading nodes from mgmtd.");
Program::getApp()->abort();
break;
}
handleNodeChanges(NODETYPE_Meta, addedMetaNodes, removedMetaNodes);
handleNodeChanges(NODETYPE_Storage, addedStorageNodes, removedStorageNodes);
syncRes = downloadAndSyncTargetMappings();
if (!syncRes)
{
log.logErr("Error downloading target mappings from mgmtd.");
Program::getApp()->abort();
break;
}
handleTargetMappingChanges();
syncRes = downloadAndSyncTargetStates();
if (!syncRes)
{
log.logErr("Error downloading target states from mgmtd.");
Program::getApp()->abort();
break;
}
syncRes = downloadAndSyncMirrorBuddyGroups();
if ( !syncRes )
{
log.logErr("Error downloading mirror buddy groups from mgmtd.");
Program::getApp()->abort();
break;
}
handleBuddyGroupChanges();
lastDownloadNodesAndStatesT.setToNow();
}
bool checkNetworkForced = getAndResetForceCheckNetwork();
if( checkNetworkForced ||
(lastCheckNetworkT.elapsedMS() > checkNetworkIntervalMS))
{
checkNetwork();
lastCheckNetworkT.setToNow();
}
}
}
/**
* @return false on error.
*/
bool InternodeSyncer::downloadAndSyncTargetStates()
{
App* app = Program::getApp();
NodeStore* mgmtNodes = app->getMgmtNodes();
TargetStateStore* targetStateStore = app->getTargetStateStore();
auto node = mgmtNodes->referenceFirstNode();
if(!node)
return false;
UInt16List targetIDs;
UInt8List reachabilityStates;
UInt8List consistencyStates;
bool downloadRes = NodesTk::downloadTargetStates(*node, NODETYPE_Storage,
&targetIDs, &reachabilityStates, &consistencyStates, false);
if(downloadRes)
targetStateStore->syncStatesFromLists(targetIDs, reachabilityStates,
consistencyStates);
return downloadRes;
}
/**
* @return false on error
*/
bool InternodeSyncer::downloadAndSyncNodes(NumNodeIDList& addedStorageNodes,
NumNodeIDList& removedStorageNodes, NumNodeIDList& addedMetaNodes,
NumNodeIDList& removedMetaNodes)
{
const char* logContext = "Nodes sync";
App* app = Program::getApp();
NodeStoreServers* mgmtNodes = app->getMgmtNodes();
NodeStoreServers* metaNodes = app->getMetaNodes();
NodeStoreServers* storageNodes = app->getStorageNodes();
Node& localNode = app->getLocalNode();
auto mgmtNode = mgmtNodes->referenceFirstNode();
if (!mgmtNode)
return false;
{ // storage nodes
std::vector<NodeHandle> storageNodesList;
bool storageRes =
NodesTk::downloadNodes(*mgmtNode, NODETYPE_Storage, storageNodesList, false);
if(!storageRes)
return false;
storageNodes->syncNodes(storageNodesList, &addedStorageNodes, &removedStorageNodes,
&localNode);
printSyncNodesResults(NODETYPE_Storage, &addedStorageNodes, &removedStorageNodes);
}
{ // metadata nodes
std::vector<NodeHandle> metaNodesList;
NumNodeID rootNodeID;
bool rootIsBuddyMirrored;
bool metaRes =
NodesTk::downloadNodes(*mgmtNode, NODETYPE_Meta, metaNodesList, false, &rootNodeID,
&rootIsBuddyMirrored);
if(!metaRes)
return false;
metaNodes->syncNodes(metaNodesList, &addedMetaNodes, &removedMetaNodes, &localNode);
if (app->getMetaRoot().setIfDefault(rootNodeID, rootIsBuddyMirrored))
{
LogContext(logContext).log(Log_CRITICAL,
"Root NodeID (from sync results): " + rootNodeID.str() );
}
printSyncNodesResults(NODETYPE_Meta, &addedMetaNodes, &removedMetaNodes);
}
return true;
}
void InternodeSyncer::printSyncNodesResults(NodeType nodeType, NumNodeIDList* addedNodes,
NumNodeIDList* removedNodes)
{
const char* logContext = "Sync results";
if (!addedNodes->empty())
LogContext(logContext).log(Log_WARNING, std::string("Nodes added: ") +
StringTk::uintToStr(addedNodes->size() ) +
" (Type: " + boost::lexical_cast<std::string>(nodeType) + ")");
if (!removedNodes->empty())
LogContext(logContext).log(Log_WARNING, std::string("Nodes removed: ") +
StringTk::uintToStr(removedNodes->size() ) +
" (Type: " + boost::lexical_cast<std::string>(nodeType) + ")");
}
/**
* @return false on error
*/
bool InternodeSyncer::downloadAndSyncTargetMappings()
{
App* app = Program::getApp();
NodeStoreServers* mgmtNodes = app->getMgmtNodes();
TargetMapper* targetMapper = app->getTargetMapper();
bool retVal = true;
auto mgmtNode = mgmtNodes->referenceFirstNode();
if(!mgmtNode)
return false;
auto mappings = NodesTk::downloadTargetMappings(*mgmtNode, false);
if (mappings.first)
targetMapper->syncTargets(std::move(mappings.second));
else
retVal = false;
return retVal;
}
/**
* @return false on error
*/
bool InternodeSyncer::downloadAndSyncMirrorBuddyGroups()
{
App* app = Program::getApp();
NodeStoreServers* mgmtNodes = app->getMgmtNodes();
MirrorBuddyGroupMapper* buddyGroupMapper = app->getMirrorBuddyGroupMapper();
bool retVal = true;
auto mgmtNode = mgmtNodes->referenceFirstNode();
if(!mgmtNode)
return false;
UInt16List buddyGroupIDs;
UInt16List primaryTargetIDs;
UInt16List secondaryTargetIDs;
bool downloadRes = NodesTk::downloadMirrorBuddyGroups(*mgmtNode, NODETYPE_Storage,
&buddyGroupIDs, &primaryTargetIDs, &secondaryTargetIDs, false);
if(downloadRes)
buddyGroupMapper->syncGroupsFromLists(buddyGroupIDs, primaryTargetIDs, secondaryTargetIDs,
NumNodeID() );
else
retVal = false;
return retVal;
}
/**
* @return false on error
*/
bool InternodeSyncer::downloadAndSyncMetaMirrorBuddyGroups()
{
App* app = Program::getApp();
NodeStoreServers* mgmtNodes = app->getMgmtNodes();
MirrorBuddyGroupMapper* buddyGroupMapper = app->getMetaMirrorBuddyGroupMapper();
bool retVal = true;
auto mgmtNode = mgmtNodes->referenceFirstNode();
if(!mgmtNode)
return false;
UInt16List buddyGroupIDs;
UInt16List primaryTargetIDs;
UInt16List secondaryTargetIDs;
bool downloadRes = NodesTk::downloadMirrorBuddyGroups(*mgmtNode, NODETYPE_Meta, &buddyGroupIDs,
&primaryTargetIDs, &secondaryTargetIDs, false);
if(downloadRes)
buddyGroupMapper->syncGroupsFromLists(buddyGroupIDs, primaryTargetIDs, secondaryTargetIDs,
NumNodeID() );
else
retVal = false;
return retVal;
}
void InternodeSyncer::handleNodeChanges(NodeType nodeType, NumNodeIDList& addedNodes,
NumNodeIDList& removedNodes)
{
const char* logContext = "handleNodeChanges";
if (!addedNodes.empty())
LogContext(logContext).log(Log_WARNING,
std::string("Nodes added while beegfs-fsck was running: ")
+ StringTk::uintToStr(addedNodes.size())
+ " (Type: " + boost::lexical_cast<std::string>(nodeType) + ")");
if (!removedNodes.empty())
{
// removed nodes must lead to fsck stoppage
LogContext(logContext).log(Log_WARNING,
std::string("Nodes removed while beegfs-fsck was running: ")
+ StringTk::uintToStr(removedNodes.size())
+ " (Type: " + boost::lexical_cast<std::string>(nodeType) + ")");
Program::getApp()->abort();
}
}
void InternodeSyncer::handleTargetMappingChanges()
{
const char* logContext = "handleTargetMappingChanges";
TargetMap newTargetMap = Program::getApp()->getTargetMapper()->getMapping();
for ( TargetMapIter originalMapIter = originalTargetMap.begin();
originalMapIter != originalTargetMap.end(); originalMapIter++ )
{
uint16_t targetID = originalMapIter->first;
NumNodeID oldNodeID = originalMapIter->second;
TargetMapIter newMapIter = newTargetMap.find(targetID);
if ( newMapIter == newTargetMap.end() )
{
LogContext(logContext).log(Log_WARNING,
"Target removed while beegfs-fsck was running; beegfs-fsck can't continue; targetID: "
+ StringTk::uintToStr(targetID));
Program::getApp()->abort();
}
else
{
NumNodeID newNodeID = newMapIter->second;
if ( oldNodeID != newNodeID )
{
LogContext(logContext).log(Log_WARNING,
"Target re-mapped while beegfs-fsck was running; beegfs-fsck can't continue; "
"targetID: " + StringTk::uintToStr(targetID));
Program::getApp()->abort();
}
}
}
}
void InternodeSyncer::handleBuddyGroupChanges()
{
const char* logContext = "handleBuddyGroupChanges";
MirrorBuddyGroupMap newMirrorBuddyGroupMap;
Program::getApp()->getMirrorBuddyGroupMapper()->getMirrorBuddyGroups(newMirrorBuddyGroupMap);
for ( MirrorBuddyGroupMapIter originalMapIter = originalMirrorBuddyGroupMap.begin();
originalMapIter != originalMirrorBuddyGroupMap.end(); originalMapIter++ )
{
uint16_t buddyGroupID = originalMapIter->first;
MirrorBuddyGroup oldBuddyGroup = originalMapIter->second;
MirrorBuddyGroupMapIter newMapIter = newMirrorBuddyGroupMap.find(buddyGroupID);
if ( newMapIter == newMirrorBuddyGroupMap.end() )
{
LogContext(logContext).log(Log_WARNING,
"Mirror buddy group removed while beegfs-fsck was running; beegfs-fsck can't continue; "
"groupID: " + StringTk::uintToStr(buddyGroupID));
Program::getApp()->abort();
}
else
{
MirrorBuddyGroup newBuddyGroup = newMapIter->second;
if ( oldBuddyGroup.firstTargetID != newBuddyGroup.firstTargetID )
{
LogContext(logContext).log(Log_WARNING,
"Primary of mirror buddy group changed while beegfs-fsck was running; beegfs-fsck "
"can't continue; groupID: " + StringTk::uintToStr(buddyGroupID));
Program::getApp()->abort();
}
}
}
}
/**
* Blocks until list of servers has been downloaded from management node
*/
void InternodeSyncer::waitForServers()
{
std::lock_guard<Mutex> lock(serversDownloadedMutex);
while (!serversDownloaded)
serversDownloadedCondition.wait(&serversDownloadedMutex);
}
/**
* Inspect the available and allowed network interfaces for any changes.
*/
bool InternodeSyncer::checkNetwork()
{
App* app = Program::getApp();
NicAddressList newLocalNicList;
bool res = false;
app->findAllowedInterfaces(newLocalNicList);
app->findAllowedRDMAInterfaces(newLocalNicList);
if (!std::equal(newLocalNicList.begin(), newLocalNicList.end(), app->getLocalNicList().begin()))
{
log.log(Log_NOTICE, "checkNetwork: local interfaces have changed");
app->updateLocalNicList(newLocalNicList);
res = true;
}
return res;
}

View File

@@ -0,0 +1,95 @@
#ifndef INTERNODESYNCER_H_
#define INTERNODESYNCER_H_
#include <common/app/log/LogContext.h>
#include <common/components/ComponentInitException.h>
#include <common/nodes/MirrorBuddyGroupMapper.h>
#include <common/threading/PThread.h>
#include <common/Common.h>
#include <mutex>
class InternodeSyncer : public PThread
{
public:
InternodeSyncer();
virtual ~InternodeSyncer();
bool downloadAndSyncNodes(NumNodeIDList& addedStorageNodes,
NumNodeIDList& removedStorageNodes, NumNodeIDList& addedMetaNodes,
NumNodeIDList& removedMetaNodes);
bool downloadAndSyncTargetMappings();
bool downloadAndSyncMirrorBuddyGroups();
bool downloadAndSyncMetaMirrorBuddyGroups();
bool downloadAndSyncTargetStates();
private:
LogContext log;
Mutex forceNodesAndTargetStatesUpdateMutex;
bool forceNodesAndTargetStatesUpdate;
Mutex forceCheckNetworkMutex;
bool forceCheckNetwork; // true to force check of network interfaces
TargetMap originalTargetMap;
MirrorBuddyGroupMap originalMirrorBuddyGroupMap;
MirrorBuddyGroupMap originalMetaMirrorBuddyGroupMap;
virtual void run();
void syncLoop();
void handleNodeChanges(NodeType nodeType, NumNodeIDList& addedNodes,
NumNodeIDList& removedNodes);
void handleTargetMappingChanges();
void handleBuddyGroupChanges();
bool getAndResetForceCheckNetwork()
{
const std::lock_guard<Mutex> lock(forceCheckNetworkMutex);
bool retVal = this->forceCheckNetwork;
this->forceCheckNetwork = false;
return retVal;
}
bool checkNetwork();
Condition serversDownloadedCondition;
Mutex serversDownloadedMutex;
bool serversDownloaded;
public:
void waitForServers();
void setForceCheckNetwork()
{
const std::lock_guard<Mutex> lock(forceCheckNetworkMutex);
this->forceCheckNetwork = true;
}
private:
static void printSyncNodesResults(NodeType nodeType, NumNodeIDList* addedNodes,
NumNodeIDList* removedNodes);
void setForceNodesAndTargetStatesUpdate()
{
const std::lock_guard<Mutex> lock(forceNodesAndTargetStatesUpdateMutex);
this->forceNodesAndTargetStatesUpdate = true;
}
bool getAndResetForceNodesAndTargetStatesUpdate()
{
const std::lock_guard<Mutex> lock(forceNodesAndTargetStatesUpdateMutex);
bool retVal = this->forceNodesAndTargetStatesUpdate;
this->forceNodesAndTargetStatesUpdate = false;
return retVal;
}
};
#endif /* INTERNODESYNCER_H_ */

View File

@@ -0,0 +1,98 @@
#include "ModificationEventHandler.h"
#include <common/toolkit/ZipIterator.h>
#include <database/FsckDBException.h>
#include <program/Program.h>
#include <mutex>
ModificationEventHandler::ModificationEventHandler(FsckDBModificationEventsTable& table)
: PThread("ModificationEventHandler"),
table(&table)
{
}
void ModificationEventHandler::run()
{
FsckDBModificationEventsTable::BulkHandle bulkHandle(table->newBulkHandle() );
while ( !getSelfTerminate() )
{
std::unique_lock<Mutex> bufferListSafeLock(bufferListMutex); // LOCK BUFFER
// make sure to group at least MODHANDLER_MINSIZE_FLUSH flush elements (to not bother the DB
// with every single event)
if (bufferList.size() < MODHANDLER_MINSIZE_FLUSH)
{
bufferListSafeLock.unlock(); // UNLOCK BUFFER
const std::lock_guard<Mutex> lock(eventsAddedMutex);
eventsAddedCond.timedwait(&eventsAddedMutex, 2000);
continue;
}
else
{
// create a copy of the buffer list and flush this to DB, so that the buffer will become
// free immediately and the incoming messages do not have to wait for DB
FsckModificationEventList bufferListCopy;
bufferListCopy.splice(bufferListCopy.begin(), bufferList);
bufferListSafeLock.unlock(); // UNLOCK BUFFER
table->insert(bufferListCopy, bulkHandle);
}
}
// a last flush after component stopped
FsckModificationEventList bufferListCopy;
{
const std::lock_guard<Mutex> bufferListLock(bufferListMutex);
bufferListCopy.splice(bufferListCopy.begin(), bufferList);
}
table->insert(bufferListCopy, bulkHandle);
}
bool ModificationEventHandler::add(UInt8List& eventTypeList, StringList& entryIDList)
{
const char* logContext = "ModificationEventHandler (add)";
if ( unlikely(eventTypeList.size() != entryIDList.size()) )
{
LogContext(logContext).logErr("Unable to add events. The lists do not have equal sizes.");
return false;
}
while ( true )
{
{
const std::lock_guard<Mutex> lock(bufferListMutex);
if (this->bufferList.size() < MODHANDLER_MAXSIZE_EVENTLIST)
{
break;
}
}
{
const std::lock_guard<Mutex> lock(eventsFlushedMutex);
this->eventsFlushedCond.timedwait(&eventsFlushedMutex, 2000);
}
}
ZipIterRange<UInt8List, StringList> eventTypeEntryIDIter(eventTypeList, entryIDList);
{
const std::lock_guard<Mutex> bufferListLock(bufferListMutex);
for ( ; !eventTypeEntryIDIter.empty(); ++eventTypeEntryIDIter)
{
FsckModificationEvent event((ModificationEventType)*(eventTypeEntryIDIter()->first),
*(eventTypeEntryIDIter()->second) );
this->bufferList.push_back(event);
}
}
this->eventsAddedCond.signal();
return true;
}

View File

@@ -0,0 +1,44 @@
#ifndef MODIFICATIONEVENTHANDLER_H
#define MODIFICATIONEVENTHANDLER_H
#include <common/fsck/FsckModificationEvent.h>
#include <common/threading/Condition.h>
#include <database/FsckDBTable.h>
#define MODHANDLER_MAXSIZE_EVENTLIST 50000
#define MODHANDLER_MINSIZE_FLUSH 200
class ModificationEventHandler: public PThread
{
public:
ModificationEventHandler(FsckDBModificationEventsTable& table);
virtual void run();
bool add(UInt8List& eventTypeList, StringList& entryIDList);
private:
FsckDBModificationEventsTable* table;
FsckModificationEventList bufferList;
Mutex bufferListMutex;
Mutex bufferListCopyMutex;
Mutex flushMutex;
Mutex eventsAddedMutex;
Condition eventsAddedCond;
Mutex eventsFlushedMutex;
Condition eventsFlushedCond;
public:
void stop()
{
selfTerminate();
eventsAddedCond.signal();
join();
}
};
#endif /* MODIFICATIONEVENTHANDLER_H */

View File

@@ -0,0 +1,97 @@
#include "AdjustChunkPermissionsWork.h"
#include <common/net/message/fsck/AdjustChunkPermissionsMsg.h>
#include <common/net/message/fsck/AdjustChunkPermissionsRespMsg.h>
#include <common/toolkit/MessagingTk.h>
#include <toolkit/FsckException.h>
#include <program/Program.h>
AdjustChunkPermissionsWork::AdjustChunkPermissionsWork(Node& node, SynchronizedCounter* counter,
AtomicUInt64* fileCount, AtomicUInt64* errorCount)
: log("AdjustChunkPermissionsWork"),
node(node),
counter(counter),
fileCount(fileCount),
errorCount(errorCount)
{
}
AdjustChunkPermissionsWork::~AdjustChunkPermissionsWork()
{
}
void AdjustChunkPermissionsWork::process(char* bufIn, unsigned bufInLen, char* bufOut,
unsigned bufOutLen)
{
log.log(4, "Processing AdjustChunkPermissionsWork");
try
{
doWork(false);
doWork(true);
// work package finished => increment counter
this->counter->incCount();
}
catch (std::exception &e)
{
// exception thrown, but work package is finished => increment counter
this->counter->incCount();
// after incrementing counter, re-throw exception
throw;
}
log.log(4, "Processed AdjustChunkPermissionsWork");
}
void AdjustChunkPermissionsWork::doWork(bool isBuddyMirrored)
{
for ( unsigned firstLevelhashDirNum = 0;
firstLevelhashDirNum <= META_DENTRIES_LEVEL1_SUBDIR_NUM - 1; firstLevelhashDirNum++ )
{
for ( unsigned secondLevelhashDirNum = 0;
secondLevelhashDirNum < META_DENTRIES_LEVEL2_SUBDIR_NUM; secondLevelhashDirNum++ )
{
unsigned hashDirNum = StorageTk::mergeHashDirs(firstLevelhashDirNum,
secondLevelhashDirNum);
int64_t hashDirOffset = 0;
int64_t contDirOffset = 0;
std::string currentContDirID;
unsigned resultCount = 0;
do
{
AdjustChunkPermissionsMsg adjustChunkPermissionsMsg(hashDirNum, currentContDirID,
ADJUST_AT_ONCE, hashDirOffset, contDirOffset, isBuddyMirrored);
const auto respMsg = MessagingTk::requestResponse(node, adjustChunkPermissionsMsg,
NETMSGTYPE_AdjustChunkPermissionsResp);
if (respMsg)
{
auto* adjustChunkPermissionsRespMsg = (AdjustChunkPermissionsRespMsg*) respMsg.get();
// set new parameters
currentContDirID = adjustChunkPermissionsRespMsg->getCurrentContDirID();
hashDirOffset = adjustChunkPermissionsRespMsg->getNewHashDirOffset();
contDirOffset = adjustChunkPermissionsRespMsg->getNewContDirOffset();
resultCount = adjustChunkPermissionsRespMsg->getCount();
this->fileCount->increase(resultCount);
if (adjustChunkPermissionsRespMsg->getErrorCount() > 0)
this->errorCount->increase(adjustChunkPermissionsRespMsg->getErrorCount());
}
else
{
throw FsckException("Communication error occured with node " + node.getAlias());
}
// if any of the worker threads threw an exception, we should stop now!
if ( Program::getApp()->getSelfTerminate() )
return;
} while ( resultCount > 0 );
}
}
}

View File

@@ -0,0 +1,32 @@
#ifndef ADJUSTCHUNKPERMISSIONSWORK_H
#define ADJUSTCHUNKPERMISSIONSWORK_H
#include <common/app/log/LogContext.h>
#include <common/components/worker/Work.h>
#include <common/toolkit/SynchronizedCounter.h>
#include <database/FsckDB.h>
// the size of one packet, i.e. how many files are adjusted at once; basically just
// limited to have some control and give the user feedback
#define ADJUST_AT_ONCE 50
class AdjustChunkPermissionsWork : public Work
{
public:
AdjustChunkPermissionsWork(Node& node, SynchronizedCounter* counter, AtomicUInt64* fileCount,
AtomicUInt64* errorCount);
virtual ~AdjustChunkPermissionsWork();
void process(char* bufIn, unsigned bufInLen, char* bufOut, unsigned bufOutLen);
private:
LogContext log;
Node& node;
SynchronizedCounter* counter;
AtomicUInt64* fileCount;
AtomicUInt64* errorCount;
void doWork(bool isBuddyMirrored);
};
#endif /* ADJUSTCHUNKPERMISSIONSWORK_H */

View File

@@ -0,0 +1,152 @@
#include "RetrieveChunksWork.h"
#include <common/net/message/fsck/FetchFsckChunkListMsg.h>
#include <common/net/message/fsck/FetchFsckChunkListRespMsg.h>
#include <common/storage/Storagedata.h>
#include <common/toolkit/FsckTk.h>
#include <common/toolkit/MessagingTk.h>
#include <common/toolkit/StorageTk.h>
#include <database/FsckDBException.h>
#include <toolkit/FsckException.h>
#include <program/Program.h>
RetrieveChunksWork::RetrieveChunksWork(FsckDB* db, NodeHandle node, SynchronizedCounter* counter,
AtomicUInt64* numChunksFound, bool forceRestart) :
log("RetrieveChunksWork"), node(std::move(node)), counter(counter),
numChunksFound(numChunksFound),
chunks(db->getChunksTable()), chunksHandle(chunks->newBulkHandle()),
malformedChunks(db->getMalformedChunksList()),
forceRestart(forceRestart),
started(false), startedBarrier(2)
{
}
RetrieveChunksWork::~RetrieveChunksWork()
{
}
void RetrieveChunksWork::process(char* bufIn, unsigned bufInLen, char* bufOut, unsigned bufOutLen)
{
log.log(Log_DEBUG, "Processing RetrieveChunksWork");
try
{
doWork();
// flush buffers before signaling completion
chunks->flush(chunksHandle);
// work package finished => increment counter
this->counter->incCount();
}
catch (std::exception& e)
{
// exception thrown, but work package is finished => increment counter
this->counter->incCount();
// after incrementing counter, re-throw exception
throw;
}
log.log(Log_DEBUG, "Processed RetrieveChunksWork");
}
void RetrieveChunksWork::doWork()
{
// take the node associated with the current target and send a RetrieveChunksMsg to
// that node; the chunks are retrieved incrementally
if ( node )
{
std::string nodeID = node->getAlias();
FetchFsckChunkListStatus status = FetchFsckChunkListStatus_NOTSTARTED;
unsigned resultCount = 0;
do
{
FetchFsckChunkListMsg fetchFsckChunkListMsg(RETRIEVE_CHUNKS_PACKET_SIZE, status,
forceRestart);
const auto respMsg = MessagingTk::requestResponse(*node, fetchFsckChunkListMsg,
NETMSGTYPE_FetchFsckChunkListResp);
if (respMsg)
{
auto* fetchFsckChunkListRespMsg = (FetchFsckChunkListRespMsg*) respMsg.get();
FsckChunkList& chunks = fetchFsckChunkListRespMsg->getChunkList();
resultCount = chunks.size();
status = fetchFsckChunkListRespMsg->getStatus();
// check entry IDs
for (auto it = chunks.begin(); it != chunks.end(); )
{
if (db::EntryID::tryFromStr(it->getID()).first
&& it->getSavedPath()->str().size() <= db::Chunk::SAVED_PATH_SIZE)
{
++it;
continue;
}
++it;
malformedChunks->append(*std::prev(it));
chunks.erase(std::prev(it));
}
if (status == FetchFsckChunkListStatus_NOTSTARTED)
{
// Another fsck run is still in progress or was aborted, and --forceRestart was not
// set - this means we can't start a new chunk fetcher.
started = false;
startedBarrier.wait();
startedBarrier.wait();
return;
}
else if (status == FetchFsckChunkListStatus_READERROR)
{
throw FsckException("Read error occured while fetching chunks from node; nodeID: "
+ nodeID);
}
if (!started)
{
started = true;
startedBarrier.wait();
startedBarrier.wait();
}
this->chunks->insert(chunks, this->chunksHandle);
numChunksFound->increase(resultCount);
}
else
{
throw FsckException("Communication error occured with node; nodeID: " + nodeID);
}
if ( Program::getApp()->getShallAbort() )
break;
} while ( (resultCount > 0) || (status == FetchFsckChunkListStatus_RUNNING) );
}
else
{
// basically this should never ever happen
log.logErr("Requested node does not exist");
throw FsckException("Requested node does not exist");
}
}
/**
* Waits until started conditin is is signalled and returns the value of stared
* @param isStarted ptr to boolean which is set to whether the server replied it actually started
* the process. Note: this is a ptr and not a return to ensure the member variable
* started is no longer accessed after doWork is finished and the object possibly
* already deleted.
*/
void RetrieveChunksWork::waitForStarted(bool* isStarted)
{
startedBarrier.wait();
*isStarted = started;
startedBarrier.wait();
}

View File

@@ -0,0 +1,57 @@
#ifndef RETRIEVECHUNKSWORK_H
#define RETRIEVECHUNKSWORK_H
/*
* retrieve all chunks from one storage server and save them to DB
*/
#include <common/app/log/LogContext.h>
#include <common/components/worker/Work.h>
#include <common/threading/Barrier.h>
#include <common/toolkit/SynchronizedCounter.h>
#include <database/FsckDB.h>
#include <database/FsckDBTable.h>
// the size of one response packet, i.e. how many chunks are asked for at once
#define RETRIEVE_CHUNKS_PACKET_SIZE 400
class RetrieveChunksWork : public Work
{
public:
/*
* @param db database instance
* @param node pointer to the node to retrieve data from
* @param counter a pointer to a Synchronized counter; this is incremented by one at the end
* and the calling thread can wait for the counter
* @param numChunksFound
* @param forceRestart In case the storage servers' chunk fetchers still have data from a
* previous run, force a restart instead of aborting with an error
*/
RetrieveChunksWork(FsckDB* db, NodeHandle node, SynchronizedCounter* counter,
AtomicUInt64* numChunksFound, bool forceRestart);
virtual ~RetrieveChunksWork();
void process(char* bufIn, unsigned bufInLen, char* bufOut, unsigned bufOutLen);
void waitForStarted(bool* isStarted);
private:
LogContext log;
NodeHandle node;
SynchronizedCounter* counter;
AtomicUInt64* numChunksFound;
FsckDBChunksTable* chunks;
FsckDBChunksTable::BulkHandle chunksHandle;
DiskList<FsckChunk>* malformedChunks;
bool forceRestart;
void doWork();
bool started;
Barrier startedBarrier;
};
#endif /* RETRIEVECHUNKSWORK_H */

View File

@@ -0,0 +1,222 @@
#include "RetrieveDirEntriesWork.h"
#include <common/net/message/fsck/RetrieveDirEntriesMsg.h>
#include <common/net/message/fsck/RetrieveDirEntriesRespMsg.h>
#include <common/toolkit/MessagingTk.h>
#include <common/toolkit/MetaStorageTk.h>
#include <database/FsckDBException.h>
#include <toolkit/FsckException.h>
#include <program/Program.h>
#include <set>
RetrieveDirEntriesWork::RetrieveDirEntriesWork(FsckDB* db, Node& node, SynchronizedCounter* counter,
AtomicUInt64& errors, unsigned hashDirStart, unsigned hashDirEnd,
AtomicUInt64* numDentriesFound, AtomicUInt64* numFileInodesFound,
std::set<FsckTargetID>& usedTargets) :
log("RetrieveDirEntriesWork"), node(node), counter(counter), errors(&errors),
numDentriesFound(numDentriesFound), numFileInodesFound(numFileInodesFound),
usedTargets(&usedTargets), hashDirStart(hashDirStart), hashDirEnd(hashDirEnd),
dentries(db->getDentryTable()), dentriesHandle(dentries->newBulkHandle()),
files(db->getFileInodesTable()), filesHandle(files->newBulkHandle()),
contDirs(db->getContDirsTable()), contDirsHandle(contDirs->newBulkHandle())
{
}
void RetrieveDirEntriesWork::process(char* bufIn, unsigned bufInLen, char* bufOut,
unsigned bufOutLen)
{
log.log(4, "Processing RetrieveDirEntriesWork");
try
{
doWork(false);
doWork(true);
// flush buffers before signaling completion
dentries->flush(dentriesHandle);
files->flush(filesHandle);
contDirs->flush(contDirsHandle);
// work package finished => increment counter
this->counter->incCount();
}
catch (std::exception &e)
{
// exception thrown, but work package is finished => increment counter
this->counter->incCount();
// after incrementing counter, re-throw exception
throw;
}
log.log(4, "Processed RetrieveDirEntriesWork");
}
void RetrieveDirEntriesWork::doWork(bool isBuddyMirrored)
{
for ( unsigned firstLevelhashDirNum = hashDirStart; firstLevelhashDirNum <= hashDirEnd;
firstLevelhashDirNum++ )
{
for ( unsigned secondLevelhashDirNum = 0;
secondLevelhashDirNum < META_DENTRIES_LEVEL2_SUBDIR_NUM; secondLevelhashDirNum++ )
{
unsigned hashDirNum = StorageTk::mergeHashDirs(firstLevelhashDirNum,
secondLevelhashDirNum);
int64_t hashDirOffset = 0;
int64_t contDirOffset = 0;
std::string currentContDirID;
int resultCount = 0;
do
{
RetrieveDirEntriesMsg retrieveDirEntriesMsg(hashDirNum, currentContDirID,
RETRIEVE_DIR_ENTRIES_PACKET_SIZE, hashDirOffset, contDirOffset, isBuddyMirrored);
const auto respMsg = MessagingTk::requestResponse(node, retrieveDirEntriesMsg,
NETMSGTYPE_RetrieveDirEntriesResp);
if (respMsg)
{
auto* retrieveDirEntriesRespMsg = (RetrieveDirEntriesRespMsg*) respMsg.get();
// set new parameters
currentContDirID = retrieveDirEntriesRespMsg->getCurrentContDirID();
hashDirOffset = retrieveDirEntriesRespMsg->getNewHashDirOffset();
contDirOffset = retrieveDirEntriesRespMsg->getNewContDirOffset();
// parse directory entries
FsckDirEntryList& dirEntries = retrieveDirEntriesRespMsg->getDirEntries();
// this is the actual result count we are interested in, because if no dirEntries
// were read, there is nothing left on the server
resultCount = dirEntries.size();
// check dentry entry IDs
for (auto it = dirEntries.begin(); it != dirEntries.end(); )
{
if (db::EntryID::tryFromStr(it->getID()).first
&& db::EntryID::tryFromStr(it->getParentDirID()).first)
{
++it;
continue;
}
LOG(GENERAL, ERR, "Found dentry with invalid entry IDs.",
("node", it->getSaveNodeID()),
("isBuddyMirrored", it->getIsBuddyMirrored()),
("entryID", it->getID()),
("parentEntryID", it->getParentDirID()));
++it;
errors->increase();
dirEntries.erase(std::prev(it));
}
this->dentries->insert(dirEntries, this->dentriesHandle);
numDentriesFound->increase(resultCount);
// parse inlined file inodes
FsckFileInodeList& inlinedFileInodes =
retrieveDirEntriesRespMsg->getInlinedFileInodes();
// check inode entry IDs
for (auto it = inlinedFileInodes.begin(); it != inlinedFileInodes.end(); )
{
if (db::EntryID::tryFromStr(it->getID()).first
&& db::EntryID::tryFromStr(it->getParentDirID()).first
&& (!it->getPathInfo()->hasOrigFeature()
|| db::EntryID::tryFromStr(
it->getPathInfo()->getOrigParentEntryID()).first))
{
++it;
continue;
}
LOG(GENERAL, ERR, "Found inode with invalid entry IDs.",
("node", it->getSaveNodeID()),
("isBuddyMirrored", it->getIsBuddyMirrored()),
("entryID", it->getID()),
("parentEntryID", it->getParentDirID()),
("origParent", it->getPathInfo()->getOrigParentEntryID()));
++it;
errors->increase();
inlinedFileInodes.erase(std::prev(it));
}
struct ops
{
static bool dentryCmp(const FsckDirEntry& a, const FsckDirEntry& b)
{
return a.getID() < b.getID();
}
static bool inodeCmp(const FsckFileInode& a, const FsckFileInode& b)
{
return a.getID() < b.getID();
}
};
dirEntries.sort(ops::dentryCmp);
inlinedFileInodes.sort(ops::inodeCmp);
this->files->insert(inlinedFileInodes, this->filesHandle);
numFileInodesFound->increase(inlinedFileInodes.size());
// add used targetIDs
for ( FsckFileInodeListIter iter = inlinedFileInodes.begin();
iter != inlinedFileInodes.end(); iter++ )
{
FsckTargetIDType fsckTargetIDType;
if (iter->getStripePatternType() == FsckStripePatternType_BUDDYMIRROR)
fsckTargetIDType = FsckTargetIDType_BUDDYGROUP;
else
fsckTargetIDType = FsckTargetIDType_TARGET;
for (auto targetsIter = iter->getStripeTargets().begin();
targetsIter != iter->getStripeTargets().end(); targetsIter++)
{
this->usedTargets->insert(FsckTargetID(*targetsIter, fsckTargetIDType) );
}
}
// parse all new cont. directories
FsckContDirList& contDirs = retrieveDirEntriesRespMsg->getContDirs();
// check entry IDs
for (auto it = contDirs.begin(); it != contDirs.end(); )
{
if (db::EntryID::tryFromStr(it->getID()).first)
{
++it;
continue;
}
LOG(GENERAL, ERR, "Found content directory with invalid entry ID.",
("node", it->getSaveNodeID()),
("isBuddyMirrored", it->getIsBuddyMirrored()),
("entryID", it->getID()));
++it;
errors->increase();
contDirs.erase(std::prev(it));
}
this->contDirs->insert(contDirs, this->contDirsHandle);
}
else
{
throw FsckException("Communication error occured with node " + node.getAlias());
}
// if any of the worker threads threw an exception, we should stop now!
if ( Program::getApp()->getShallAbort() )
return;
} while ( resultCount > 0 );
}
}
}

View File

@@ -0,0 +1,62 @@
#ifndef RETRIEVEDIRENTRIESWORK_H
#define RETRIEVEDIRENTRIESWORK_H
/*
* retrieve all dir entries from one node, inside a specified range of hashDirs and save them to DB
*/
#include <common/app/log/LogContext.h>
#include <common/components/worker/Work.h>
#include <common/toolkit/SynchronizedCounter.h>
#include <database/FsckDB.h>
#include <database/FsckDBTable.h>
// the size of one response packet, i.e. how many dentries are asked for at once
#define RETRIEVE_DIR_ENTRIES_PACKET_SIZE 500
class RetrieveDirEntriesWork : public Work
{
public:
/*
* @param db database instance
* @param node the node to retrieve data from
* @param counter a pointer to a Synchronized counter; this is incremented by one at the end
* and the calling thread can wait for the counter
* @param hashDirStart the first top-level hashDir to open
* @param hashDirEnd the last top-level hashDir to open
* @param numDentriesFound
* @param numFileInodesFound
*/
RetrieveDirEntriesWork(FsckDB* db, Node& node, SynchronizedCounter* counter,
AtomicUInt64& errors, unsigned hashDirStart, unsigned hashDirEnd,
AtomicUInt64* numDentriesFound, AtomicUInt64* numFileInodesFound,
std::set<FsckTargetID>& usedTargets);
void process(char* bufIn, unsigned bufInLen, char* bufOut, unsigned bufOutLen);
private:
LogContext log;
Node& node;
SynchronizedCounter* counter;
AtomicUInt64* errors;
AtomicUInt64* numDentriesFound;
AtomicUInt64* numFileInodesFound;
std::set<FsckTargetID>* usedTargets;
unsigned hashDirStart;
unsigned hashDirEnd;
FsckDBDentryTable* dentries;
FsckDBDentryTable::BulkHandle dentriesHandle;
FsckDBFileInodesTable* files;
FsckDBFileInodesTable::BulkHandle filesHandle;
FsckDBContDirsTable* contDirs;
FsckDBContDirsTable::BulkHandle contDirsHandle;
void doWork(bool isBuddyMirrored);
};
#endif /* RETRIEVEDIRENTRIESWORK_H */

View File

@@ -0,0 +1,125 @@
#include "RetrieveFsIDsWork.h"
#include <common/net/message/fsck/RetrieveFsIDsMsg.h>
#include <common/net/message/fsck/RetrieveFsIDsRespMsg.h>
#include <common/toolkit/MessagingTk.h>
#include <common/toolkit/MetaStorageTk.h>
#include <database/FsckDBException.h>
#include <toolkit/FsckException.h>
#include <program/Program.h>
RetrieveFsIDsWork::RetrieveFsIDsWork(FsckDB* db, Node& node, SynchronizedCounter* counter,
AtomicUInt64& errors, unsigned hashDirStart, unsigned hashDirEnd) :
log("RetrieveFsIDsWork"), node(node), counter(counter), errors(&errors),
hashDirStart(hashDirStart), hashDirEnd(hashDirEnd),
table(db->getFsIDsTable()), bulkHandle(table->newBulkHandle())
{
}
RetrieveFsIDsWork::~RetrieveFsIDsWork()
{
}
void RetrieveFsIDsWork::process(char* bufIn, unsigned bufInLen, char* bufOut,
unsigned bufOutLen)
{
log.log(4, "Processing RetrieveFsIDsWork");
try
{
doWork(false);
doWork(true);
table->flush(bulkHandle);
// work package finished => increment counter
this->counter->incCount();
}
catch (std::exception &e)
{
// exception thrown, but work package is finished => increment counter
this->counter->incCount();
// after incrementing counter, re-throw exception
throw;
}
log.log(4, "Processed RetrieveFsIDsWork");
}
void RetrieveFsIDsWork::doWork(bool isBuddyMirrored)
{
for ( unsigned firstLevelhashDirNum = hashDirStart; firstLevelhashDirNum <= hashDirEnd;
firstLevelhashDirNum++ )
{
for ( unsigned secondLevelhashDirNum = 0;
secondLevelhashDirNum < META_DENTRIES_LEVEL2_SUBDIR_NUM; secondLevelhashDirNum++ )
{
unsigned hashDirNum = StorageTk::mergeHashDirs(firstLevelhashDirNum,
secondLevelhashDirNum);
int64_t hashDirOffset = 0;
int64_t contDirOffset = 0;
std::string currentContDirID;
int resultCount = 0;
do
{
RetrieveFsIDsMsg retrieveFsIDsMsg(hashDirNum, isBuddyMirrored, currentContDirID,
RETRIEVE_FSIDS_PACKET_SIZE, hashDirOffset, contDirOffset);
const auto respMsg = MessagingTk::requestResponse(node, retrieveFsIDsMsg,
NETMSGTYPE_RetrieveFsIDsResp);
if (respMsg)
{
auto* retrieveFsIDsRespMsg = (RetrieveFsIDsRespMsg*) respMsg.get();
// set new parameters
currentContDirID = retrieveFsIDsRespMsg->getCurrentContDirID();
hashDirOffset = retrieveFsIDsRespMsg->getNewHashDirOffset();
contDirOffset = retrieveFsIDsRespMsg->getNewContDirOffset();
// parse FS-IDs
FsckFsIDList& fsIDs = retrieveFsIDsRespMsg->getFsIDs();
// this is the actual result count we are interested in, because if no fsIDs
// were read, there is nothing left on the server
resultCount = fsIDs.size();
// check entry IDs
for (auto it = fsIDs.begin(); it != fsIDs.end(); )
{
if (db::EntryID::tryFromStr(it->getID()).first
&& db::EntryID::tryFromStr(it->getParentDirID()).first)
{
++it;
continue;
}
LOG(GENERAL, ERR, "Found fsid file with invalid entry IDs.",
("node", it->getSaveNodeID()),
("isBuddyMirrored", it->getIsBuddyMirrored()),
("entryID", it->getID()),
("parentEntryID", it->getParentDirID()));
++it;
errors->increase();
fsIDs.erase(std::prev(it));
}
this->table->insert(fsIDs, this->bulkHandle);
}
else
{
throw FsckException("Communication error occured with node " + node.getAlias());
}
// if any of the worker threads threw an exception, we should stop now!
if ( Program::getApp()->getShallAbort() )
return;
} while ( resultCount > 0 );
}
}
}

View File

@@ -0,0 +1,49 @@
#ifndef RETRIEVEFSIDSWORK_H
#define RETRIEVEFSIDSWORK_H
/*
* retrieve all FS-IDs from one node, inside a specified range of hashDirs and save them to DB
*/
#include <common/app/log/LogContext.h>
#include <common/components/worker/Work.h>
#include <common/toolkit/SynchronizedCounter.h>
#include <database/FsckDB.h>
#include <database/FsckDBTable.h>
// the size of one response packet, i.e. how many fsids are asked for at once
#define RETRIEVE_FSIDS_PACKET_SIZE 1000
class RetrieveFsIDsWork : public Work
{
public:
/*
* @param db database instance
* @param node the node to retrieve data from
* @param counter a pointer to a Synchronized counter; this is incremented by one at the end
* and the calling thread can wait for the counter
* @param hashDirStart the first top-level hashDir to open
* @param hashDirEnd the last top-level hashDir to open
*/
RetrieveFsIDsWork(FsckDB* db, Node& node, SynchronizedCounter* counter, AtomicUInt64& errors,
unsigned hashDirStart, unsigned hashDirEnd);
virtual ~RetrieveFsIDsWork();
void process(char* bufIn, unsigned bufInLen, char* bufOut, unsigned bufOutLen);
private:
LogContext log;
Node& node;
SynchronizedCounter* counter;
AtomicUInt64* errors;
unsigned hashDirStart;
unsigned hashDirEnd;
FsckDBFsIDsTable* table;
FsckDBFsIDsTable::BulkHandle bulkHandle;
void doWork(bool isBuddyMirrored);
};
#endif /* RETRIEVEFSIDSWORK_H */

View File

@@ -0,0 +1,187 @@
#include "RetrieveInodesWork.h"
#include <common/net/message/fsck/RetrieveInodesMsg.h>
#include <common/net/message/fsck/RetrieveInodesRespMsg.h>
#include <common/toolkit/MetaStorageTk.h>
#include <common/toolkit/MessagingTk.h>
#include <database/FsckDBException.h>
#include <toolkit/FsckException.h>
#include <program/Program.h>
#include <set>
RetrieveInodesWork::RetrieveInodesWork(FsckDB* db, Node& node, SynchronizedCounter* counter,
AtomicUInt64& errors, unsigned hashDirStart, unsigned hashDirEnd,
AtomicUInt64* numFileInodesFound, AtomicUInt64* numDirInodesFound,
std::set<FsckTargetID>& usedTargets) :
log("RetrieveInodesWork"), node(node), counter(counter), errors(&errors),
usedTargets(&usedTargets), hashDirStart(hashDirStart), hashDirEnd(hashDirEnd),
numFileInodesFound(numFileInodesFound), numDirInodesFound(numDirInodesFound),
files(db->getFileInodesTable()), filesHandle(files->newBulkHandle()),
dirs(db->getDirInodesTable()), dirsHandle(dirs->newBulkHandle())
{
}
void RetrieveInodesWork::process(char* bufIn, unsigned bufInLen, char* bufOut,
unsigned bufOutLen)
{
log.log(4, "Processing RetrieveInodesWork");
try
{
doWork(false);
doWork(true);
// flush buffers before signaling completion
files->flush(filesHandle);
dirs->flush(dirsHandle);
// work package finished => increment counter
this->counter->incCount();
}
catch (std::exception &e)
{
// exception thrown, but work package is finished => increment counter
this->counter->incCount();
// after incrementing counter, re-throw exception
throw;
}
log.log(4, "Processed RetrieveInodesWork");
}
void RetrieveInodesWork::doWork(bool isBuddyMirrored)
{
const NumNodeID& metaRootID = Program::getApp()->getMetaRoot().getID();
const NumNodeID& nodeID = node.getNumID();
const NumNodeID nodeBuddyGroupID = NumNodeID(Program::getApp()->getMetaMirrorBuddyGroupMapper()
->getBuddyGroupID(node.getNumID().val()));
for ( unsigned firstLevelhashDirNum = hashDirStart; firstLevelhashDirNum <= hashDirEnd;
firstLevelhashDirNum++ )
{
for ( unsigned secondLevelhashDirNum = 0;
secondLevelhashDirNum < META_DENTRIES_LEVEL2_SUBDIR_NUM; secondLevelhashDirNum++ )
{
unsigned hashDirNum = StorageTk::mergeHashDirs(firstLevelhashDirNum,
secondLevelhashDirNum);
int64_t lastOffset = 0;
size_t fileInodeCount;
size_t dirInodeCount;
do
{
RetrieveInodesMsg retrieveInodesMsg(hashDirNum, lastOffset,
RETRIEVE_INODES_PACKET_SIZE, isBuddyMirrored);
const auto respMsg = MessagingTk::requestResponse(node, retrieveInodesMsg,
NETMSGTYPE_RetrieveInodesResp);
if (respMsg)
{
auto* retrieveInodesRespMsg = (RetrieveInodesRespMsg*) respMsg.get();
// set new parameters
lastOffset = retrieveInodesRespMsg->getLastOffset();
// parse all file inodes
FsckFileInodeList& fileInodes = retrieveInodesRespMsg->getFileInodes();
// check inode entry IDs
for (auto it = fileInodes.begin(); it != fileInodes.end(); )
{
if (db::EntryID::tryFromStr(it->getID()).first
&& db::EntryID::tryFromStr(it->getParentDirID()).first
&& (!it->getPathInfo()->hasOrigFeature()
|| db::EntryID::tryFromStr(
it->getPathInfo()->getOrigParentEntryID()).first))
{
++it;
continue;
}
LOG(GENERAL, ERR, "Found inode with invalid entry IDs.",
("node", it->getSaveNodeID()),
("isBuddyMirrored", it->getIsBuddyMirrored()),
("entryID", it->getID()),
("parentEntryID", it->getParentDirID()),
("origParent", it->getPathInfo()->getOrigParentEntryID()));
++it;
errors->increase();
fileInodes.erase(std::prev(it));
}
// add targetIDs
for (auto iter = fileInodes.begin(); iter != fileInodes.end(); iter++)
{
FsckTargetIDType fsckTargetIDType;
if (iter->getStripePatternType() == FsckStripePatternType_BUDDYMIRROR)
fsckTargetIDType = FsckTargetIDType_BUDDYGROUP;
else
fsckTargetIDType = FsckTargetIDType_TARGET;
for (auto targetsIter = iter->getStripeTargets().begin();
targetsIter != iter->getStripeTargets().end(); targetsIter++)
{
this->usedTargets->insert(FsckTargetID(*targetsIter, fsckTargetIDType) );
}
}
// parse all directory inodes
FsckDirInodeList& dirInodes = retrieveInodesRespMsg->getDirInodes();
// check inode entry IDs
for (auto it = dirInodes.begin(); it != dirInodes.end(); )
{
auto entryIDPair = db::EntryID::tryFromStr(it->getID());
if (!entryIDPair.first ||
!db::EntryID::tryFromStr(it->getParentDirID()).first)
{
LOG(GENERAL, ERR, "Found inode with invalid entry IDs.",
("node", it->getSaveNodeID()),
("isBuddyMirrored", it->getIsBuddyMirrored()),
("entryID", it->getID()),
("parentEntryID", it->getParentDirID()));
++it;
errors->increase();
dirInodes.erase(std::prev(it));
continue;
}
// remove root inodes from non root metas
if (entryIDPair.second.isRootDir() &&
((it->getIsBuddyMirrored() && nodeBuddyGroupID != metaRootID)
|| (!it->getIsBuddyMirrored() && nodeID != metaRootID)))
{
++it;
dirInodes.erase(std::prev(it));
continue;
}
++it;
}
fileInodeCount = fileInodes.size();
dirInodeCount = dirInodes.size();
this->files->insert(fileInodes, this->filesHandle);
this->dirs->insert(dirInodes, this->dirsHandle);
numFileInodesFound->increase(fileInodeCount);
numDirInodesFound->increase(dirInodeCount);
}
else
{
throw FsckException("Communication error occured with node " + node.getAlias());
}
// if any of the worker threads threw an exception, we should stop now!
if ( Program::getApp()->getShallAbort() )
return;
} while ( (fileInodeCount + dirInodeCount) > 0 );
}
}
}

View File

@@ -0,0 +1,58 @@
#ifndef RETRIEVEINODESWORK_H
#define RETRIEVEINODESWORK_H
/*
* retrieve all inodes from one node, inside a specified range of hashDirs and save them to DB
*/
#include <common/app/log/LogContext.h>
#include <common/components/worker/Work.h>
#include <common/toolkit/SynchronizedCounter.h>
#include <database/FsckDB.h>
#include <database/FsckDBTable.h>
// the size of one response packet, i.e. how many inodes are asked for at once
#define RETRIEVE_INODES_PACKET_SIZE 500
class RetrieveInodesWork : public Work
{
public:
/*
* @param db database instance
* @param node the node to retrieve data from
* @param counter a pointer to a Synchronized counter; this is incremented by one at the end
* and the calling thread can wait for the counter
* @param hashDirStart the first top-level hashDir to open
* @param hashDirEnd the last top-level hashDir to open
*/
RetrieveInodesWork(FsckDB* db, Node& node, SynchronizedCounter* counter,
AtomicUInt64& errors, unsigned hashDirStart, unsigned hashDirEnd,
AtomicUInt64* numFileInodesFound, AtomicUInt64* numDirInodesFound,
std::set<FsckTargetID>& usedTargets);
void process(char* bufIn, unsigned bufInLen, char* bufOut, unsigned bufOutLen);
private:
LogContext log;
Node& node;
SynchronizedCounter* counter;
AtomicUInt64* errors;
std::set<FsckTargetID>* usedTargets;
unsigned hashDirStart;
unsigned hashDirEnd;
AtomicUInt64* numFileInodesFound;
AtomicUInt64* numDirInodesFound;
FsckDBFileInodesTable* files;
FsckDBFileInodesTable::BulkHandle filesHandle;
FsckDBDirInodesTable* dirs;
FsckDBDirInodesTable::BulkHandle dirsHandle;
void doWork(bool isBuddyMirrored);
};
#endif /* RETRIEVEINODESWORK_H */

View File

@@ -0,0 +1,47 @@
#ifndef BUFFER_H_
#define BUFFER_H_
#include <database/Set.h>
template<typename Data>
class Buffer {
private:
Set<Data>* set;
SetFragment<Data>* currentFragment;
size_t fragmentSize;
Buffer(const Buffer&);
Buffer& operator=(const Buffer&);
public:
Buffer(Set<Data>& set, size_t fragmentSize)
: set(&set), currentFragment(NULL), fragmentSize(fragmentSize)
{}
~Buffer()
{
flush();
}
void flush()
{
if(currentFragment)
{
currentFragment->flush();
currentFragment = NULL;
}
}
void append(const Data& data)
{
if(!currentFragment)
currentFragment = set->newFragment();
currentFragment->append(data);
if(currentFragment->size() * sizeof(Data) >= fragmentSize)
flush();
}
};
#endif

View File

@@ -0,0 +1,55 @@
#ifndef CHUNK_H_
#define CHUNK_H_
#include <common/fsck/FsckChunk.h>
#include <database/EntryID.h>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
namespace db {
struct Chunk {
static const unsigned SAVED_PATH_SIZE = 43;
EntryID id; /* 12 */
uint32_t targetID; /* 16 */
uint32_t buddyGroupID; /* 20 */
/* savedPath layout is ... weird.
* we have two layouts:
* - <u16 _b16>/<u16 _b16>/<id _string>
* - u<origOwnerUID _b16>/<(ts >> 16) _b16>/<(ts >> 12) & 0xF _b16>/<id _string>
*
* where u16 are random 16 bit unsigneds each. that gives maximum path lengths of
* - 2 + 1 + 2 + 26
* - 9 + 1 + 4 + 1 + 1 + 1 + 26
* add a terminating NUL to each, the maximum path length becomes 44 bytes,
* which is also conveniently aligned for uint_64t followers */
char savedPath[SAVED_PATH_SIZE + 1]; /* 64 */
int64_t fileSize; /* 72 */
int64_t usedBlocks; /* 80 */
uint32_t uid; /* 84 */
uint32_t gid; /* 88 */
typedef boost::tuple<EntryID, uint32_t, uint32_t> KeyType;
KeyType pkey() const
{
return KeyType(id, targetID, buddyGroupID);
}
operator FsckChunk() const
{
Path path(savedPath);
return FsckChunk(id.str(), targetID, path, fileSize, usedBlocks, 0, 0, 0, uid,
gid, buddyGroupID);
}
};
}
#endif

View File

@@ -0,0 +1,26 @@
#ifndef CONTDIR_H_
#define CONTDIR_H_
#include <common/fsck/FsckContDir.h>
#include <database/EntryID.h>
namespace db {
struct ContDir {
EntryID id; /* 12 */
uint32_t saveNodeID; /* 16 */
uint32_t isBuddyMirrored:1; /* 20 */
typedef EntryID KeyType;
KeyType pkey() const { return id; }
operator FsckContDir() const
{
return FsckContDir(id.str(), NumNodeID(saveNodeID), isBuddyMirrored);
}
};
}
#endif

View File

@@ -0,0 +1,67 @@
#ifndef CURSOR_H_
#define CURSOR_H_
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
template<typename Obj>
class Cursor
{
public:
typedef Obj ElementType;
private:
class SourceBase
{
public:
virtual ~SourceBase() {}
virtual bool step() = 0;
virtual ElementType* get() = 0;
};
template<typename Inner>
class Source : public SourceBase
{
public:
Source(Inner inner)
: inner(inner)
{
}
bool step()
{
return inner.step();
}
ElementType* get()
{
return inner.get();
}
private:
Inner inner;
};
public:
template<typename Inner>
explicit Cursor(Inner inner)
: source(boost::make_shared<Source<Inner> >(inner) )
{
}
bool step()
{
return source->step();
}
ElementType* get()
{
return source->get();
}
private:
boost::shared_ptr<SourceBase> source;
};
#endif

View File

@@ -0,0 +1,67 @@
#ifndef DIRENTRY_H_
#define DIRENTRY_H_
#include <common/fsck/FsckDirEntry.h>
#include <database/EntryID.h>
namespace db {
struct DirEntry
{
EntryID id; /* 12 */
EntryID parentDirID; /* 24 */
uint32_t entryOwnerNodeID; /* 28 */
uint32_t inodeOwnerNodeID; /* 32 */
uint32_t saveNodeID; /* 36 */
int32_t saveDevice; /* 40 */
uint64_t saveInode; /* 48 */
union {
struct {
char text[16]; /* +16 */
} inlined;
struct {
/* take name from a specific offset in some other file,
* identified by a u64 for convenience */
uint64_t fileID; /* +8 */
uint64_t fileOffset; /* +16 */
} extended;
} name; /* 64 */
// the identifying key for dir entries is actually (parent, name). since storing the name as
// part of the key is very inefficient, and existence of duplicate names per parent can be
// easily excluded, we instead number all dir entries to create a surrogate key (seqNo), which
// will also work as (parent, seqNo).
//
// the actual primary key of the table is (id, parent, GFID, seqNo) though. this is because
// most querys only need the id, a few queries need the full fsid linking identifier
// (id, parent, GFID), and deletion always need (parent, seqNo). this also means that the
// deletion tracker set for dir entries will be rather large per entry, but it does maek queries
// more efficient.
uint64_t seqNo; /* 72 */
uint32_t entryType:16;
uint32_t fileNameInlined:1;
uint32_t hasInlinedInode:1;
uint32_t isBuddyMirrored:1; /* 76 */
typedef boost::tuple<EntryID, EntryID, uint32_t, int32_t, uint32_t, uint64_t> KeyType;
KeyType pkey() const
{
return KeyType(id, parentDirID, saveNodeID, saveDevice, saveInode, seqNo);
}
operator FsckDirEntry() const
{
return FsckDirEntry(id.str(), "[<unresolved>]", parentDirID.str(),
NumNodeID(entryOwnerNodeID), NumNodeID(inodeOwnerNodeID), FsckDirEntryType(entryType),
hasInlinedInode, NumNodeID(saveNodeID), saveDevice, saveInode, isBuddyMirrored, seqNo);
}
};
}
#endif

View File

@@ -0,0 +1,40 @@
#ifndef DIRINODE_H_
#define DIRINODE_H_
#include <common/fsck/FsckDirInode.h>
#include <database/EntryID.h>
namespace db {
struct DirInode {
EntryID id; /* 12 */
EntryID parentDirID; /* 24 */
uint32_t parentNodeID; /* 28 */
uint32_t ownerNodeID; /* 32 */
uint32_t saveNodeID; /* 36 */
uint32_t stripePatternType:16;
uint32_t readable:1;
uint32_t isBuddyMirrored:1;
uint32_t isMismirrored:1; /* 40 */
uint64_t size; /* 48 */
uint64_t numHardlinks; /* 56 */
typedef EntryID KeyType;
EntryID pkey() const { return id; }
operator FsckDirInode() const
{
return FsckDirInode(id.str(), parentDirID.str(), NumNodeID(parentNodeID),
NumNodeID(ownerNodeID), size, numHardlinks, UInt16Vector(),
FsckStripePatternType(stripePatternType), NumNodeID(saveNodeID), isBuddyMirrored,
readable, isMismirrored);
}
};
}
#endif

View File

@@ -0,0 +1,149 @@
#ifndef DISKLIST_H_
#define DISKLIST_H_
#include <common/threading/Mutex.h>
#include <common/toolkit/serialization/Serialization.h>
#include <cerrno>
#include <stdexcept>
#include <string>
#include <mutex>
#include <vector>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
struct DiskListDoesNotExist : public std::runtime_error
{
DiskListDoesNotExist(const std::string& file)
: runtime_error(file)
{}
};
template<typename Data>
class DiskList;
template<typename Data>
class DiskListCursor
{
friend class DiskList<Data>;
public:
bool step()
{
uint64_t itemLen;
{
ssize_t readRes = ::pread(fd, &itemLen, sizeof(itemLen), offset);
if (readRes == 0)
return false;
if (readRes < (ssize_t) sizeof(itemLen))
throw std::runtime_error("could not read from disk list file: " + std::string(strerror(errno)));
}
itemLen = LE_TO_HOST_64(itemLen);
std::unique_ptr<char[]> itemBuf(new char[itemLen]);
ssize_t readRes = ::pread(fd, itemBuf.get(), itemLen, offset + sizeof(itemLen));
if (readRes < (ssize_t) itemLen)
throw std::runtime_error("could not read from disk list file: " + std::string(strerror(errno)));
Deserializer des(itemBuf.get(), itemLen);
des % item;
if (!des.good())
throw std::runtime_error("could not read from disk list file: " + std::string(strerror(errno)));
offset += sizeof(itemLen) + itemLen;
return true;
}
Data* get()
{
return &item;
}
private:
int fd;
size_t offset;
Data item;
DiskListCursor(int fd):
fd(fd), offset(0)
{
}
};
template<typename Data>
class DiskList {
private:
std::string file;
int fd;
uint64_t itemCount;
Mutex mtx;
DiskList(const DiskList&);
DiskList& operator=(const DiskList&);
public:
DiskList(const std::string& file, bool allowCreate = true) :
file(file), itemCount(0)
{
fd = ::open(file.c_str(), O_RDWR | (allowCreate ? O_CREAT : 0), 0660);
if(fd < 0)
{
int eno = errno;
if(!allowCreate && errno == ENOENT)
throw DiskListDoesNotExist(file);
else
throw std::runtime_error("could not open disk list file " + file + ": " + strerror(eno));
}
}
~DiskList()
{
if(fd >= 0)
close(fd);
}
const std::string filename() const { return file; }
void append(const Data& data)
{
std::lock_guard<Mutex> lock(mtx);
boost::scoped_array<char> buffer;
ssize_t size = serializeIntoNewBuffer(data, buffer);
if (size < 0)
throw std::runtime_error("error serializing disk list item");
uint64_t bufSize = HOST_TO_LE_64(size);
if (::write(fd, &bufSize, sizeof(bufSize)) < (ssize_t) sizeof(bufSize))
throw std::runtime_error("error writing disk list item: " + std::string(strerror(errno)));
if (::write(fd, buffer.get(), size) < size)
throw std::runtime_error("error writing disk list item: " + std::string(strerror(errno)));
}
DiskListCursor<Data> cursor()
{
return DiskListCursor<Data>(fd);
}
void clear()
{
if (fd >= 0)
{
if (ftruncate(fd, 0) < 0)
throw std::runtime_error("error clearing disk list: " + std::string(strerror(errno)));
}
}
};
#endif

View File

@@ -0,0 +1,88 @@
#ifndef DISTINCT_H_
#define DISTINCT_H_
#include <boost/type_traits/decay.hpp>
#include <boost/utility/result_of.hpp>
template<typename Source, typename KeyExtract>
class Distinct
{
private:
typedef typename boost::decay<
typename boost::result_of<KeyExtract(typename Source::ElementType&)>::type
>::type KeyType;
public:
typedef typename Source::ElementType ElementType;
typedef typename Source::MarkerType MarkerType;
public:
Distinct(Source source, KeyExtract keyExtract)
: source(source), keyExtract(keyExtract), hasKey(false)
{}
bool step()
{
if(!this->source.step() )
return false;
while(this->hasKey && this->key == this->keyExtract(*this->source.get() ) )
{
if(!this->source.step() )
return false;
}
this->hasKey = true;
this->key = this->keyExtract(*this->source.get() );
return true;
}
ElementType* get()
{
return this->source.get();
}
MarkerType mark() const
{
return this->source.mark();
}
void restore(MarkerType mark)
{
this->source.restore(mark);
this->hasKey = true;
this->key = this->keyExtract(*this->source.get() );
}
private:
Source source;
KeyType key;
KeyExtract keyExtract;
bool hasKey;
};
namespace db {
template<typename KeyExtract>
struct DistinctOp
{
KeyExtract keyExtract;
template<typename Source>
friend Distinct<Source, KeyExtract> operator|(Source source, const DistinctOp& op)
{
return Distinct<Source, KeyExtract>(source, op.keyExtract);
}
};
template<typename KeyExtract>
inline DistinctOp<KeyExtract> distinctBy(KeyExtract keyExtract)
{
DistinctOp<KeyExtract> result = {keyExtract};
return result;
}
}
#endif

View File

@@ -0,0 +1,163 @@
#ifndef ENTRYID_H_
#define ENTRYID_H_
#include <common/storage/Metadata.h>
#include <algorithm>
#include <cstdio>
#include <exception>
#include <string>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
namespace db {
struct EntryID {
unsigned sequence;
unsigned timestamp;
unsigned nodeID;
// required for queries, since they use boost::tuple to avoid writing out huge comparison
// operators
EntryID()
: sequence(0), timestamp(0), nodeID(0)
{}
EntryID(unsigned sequence, unsigned timestamp, unsigned nodeID)
: sequence(sequence), timestamp(timestamp), nodeID(nodeID)
{
if(nodeID == 0 && (timestamp != 0 || sequence > 3) )
throw std::exception();
}
bool isSpecial() const { return nodeID == 0; }
bool operator<(const EntryID& other) const
{
return boost::make_tuple(sequence, timestamp, nodeID)
< boost::make_tuple(other.sequence, other.timestamp, other.nodeID);
}
bool operator==(const EntryID& other) const
{
return this->sequence == other.sequence
&& this->timestamp == other.timestamp
&& this->nodeID == other.nodeID;
}
bool operator!=(const EntryID& other) const
{
return !(*this == other);
}
bool isDisposalDir() const
{
return *this == disposal() || *this == mdisposal();
}
bool isRootDir() const
{
return *this == root();
}
std::string str() const
{
if(isSpecial() )
{
if(*this == anchor() )
return "";
if(*this == root() )
return META_ROOTDIR_ID_STR;
if(*this == disposal() )
return META_DISPOSALDIR_ID_STR;
if(*this == mdisposal())
return META_MIRRORDISPOSALDIR_ID_STR;
throw std::exception();
}
char buffer[3*8 + 2 + 1];
sprintf(buffer, "%X-%X-%X", sequence, timestamp, nodeID);
return buffer;
}
static EntryID anchor() { return EntryID(0, 0, 0); }
static EntryID root() { return EntryID(1, 0, 0); }
static EntryID disposal() { return EntryID(2, 0, 0); }
static EntryID mdisposal() { return EntryID(3, 0, 0); }
static EntryID fromStr(const std::string& str)
{
auto pair = tryFromStr(str);
if (!pair.first)
throw std::exception();
return pair.second;
}
static std::pair<bool, EntryID> tryFromStr(const std::string& str)
{
if (str.empty())
return {true, anchor()};
if (str == META_ROOTDIR_ID_STR)
return {true, root()};
if (str == META_DISPOSALDIR_ID_STR)
return {true, disposal()};
if (str == META_MIRRORDISPOSALDIR_ID_STR)
return {true, mdisposal()};
std::string::size_type sep1;
std::string::size_type sep2;
sep1 = str.find('-');
sep2 = str.find('-', sep1 + 1);
if (sep1 == str.npos || sep2 == str.npos)
return {false, {}};
std::string seqStr = str.substr(0, sep1);
std::string tsStr = str.substr(sep1 + 1, sep2 - (sep1 + 1));
std::string nodeStr = str.substr(sep2 + 1);
if (seqStr.empty() || tsStr.empty() || nodeStr.empty() ||
seqStr.size() > 8 || tsStr.size() > 8 || nodeStr.size() > 8)
return {false, {}};
struct
{
static bool notHex(char c)
{
return !std::isxdigit(c);
}
bool operator()(const std::string& str) const
{
return std::count_if(str.begin(), str.end(), notHex) == 0;
}
} onlyHex;
if (!onlyHex(seqStr) || !onlyHex(tsStr) || !onlyHex(nodeStr) )
return {false, {}};
unsigned seq;
unsigned ts;
unsigned node;
std::sscanf(seqStr.c_str(), "%x", &seq);
std::sscanf(tsStr.c_str(), "%x", &ts);
std::sscanf(nodeStr.c_str(), "%x", &node);
return {true, EntryID(seq, ts, node)};
}
};
}
#endif

View File

@@ -0,0 +1,99 @@
#ifndef FILEINODE_H_
#define FILEINODE_H_
#include <common/fsck/FsckFileInode.h>
#include <database/EntryID.h>
namespace db {
struct FileInode {
EntryID id; /* 12 */
EntryID parentDirID; /* 24 */
EntryID origParentEntryID; /* 36 */
uint32_t parentNodeID; /* 40 */
uint32_t saveNodeID; /* 44 */
uint32_t origParentUID; /* 48 */
uint32_t uid; /* 52 */
uint32_t gid; /* 56 */
uint64_t fileSize; /* 64 */
uint64_t usedBlocks; /* 72 */
uint64_t numHardlinks; /* 80 */
uint64_t saveInode; /* 88 */
int32_t saveDevice; /* 92 */
uint32_t chunkSize; /* 96 */
enum {
NTARGETS = 6
};
uint16_t targets[NTARGETS]; /* 108 */
uint32_t isInlined:1;
uint32_t pathInfoFlags:4;
uint32_t stripePatternType:4;
uint32_t stripePatternSize:20;
uint32_t readable:1;
uint32_t isBuddyMirrored:1;
uint32_t isMismirrored:1; /* 112 */
typedef EntryID KeyType;
EntryID pkey() const { return id; }
FsckFileInode toInodeWithoutStripes() const
{
PathInfo info(origParentUID, origParentEntryID.str(), pathInfoFlags);
return FsckFileInode(id.str(), parentDirID.str(), NumNodeID(parentNodeID), info, uid, gid,
fileSize, numHardlinks, usedBlocks, {}, FsckStripePatternType(stripePatternType),
chunkSize, NumNodeID(saveNodeID), saveInode, saveDevice, isInlined, isBuddyMirrored,
readable, isMismirrored);
}
friend std::ostream& operator<<(std::ostream& os, FileInode const& obj)
{
os << "-------------" << "\n";
os << "FsckFileInode" << "\n";
os << "-------------" << "\n";
os << "EntryID: " << obj.id.str() << "\n";
os << "Parent dir's entryID: " << obj.parentDirID.str() << "\n";
os << "Orig parent dir's entryID: " << obj.origParentEntryID.str() << "\n";
os << "parent nodeID: " << obj.parentNodeID << "\n";
os << "save nodeID: " << obj.saveNodeID << "\n";
os << "origParentUID: " << obj.origParentUID << "\n";
os << "uid: " << obj.uid << "\n";
os << "gid: " << obj.gid << "\n";
os << "fileSize: " << obj.fileSize << "\n";
os << "usedBlocks: " << obj.usedBlocks << "\n";
os << "numHardlinks: " << obj.numHardlinks << "\n";
os << "saveInode: " << obj.saveInode << "\n";
os << "saveDevice: " << obj.saveDevice << "\n";
os << "chunkSize: " << obj.chunkSize << "\n";
os << "Stripe targets: [ ";
for (int i=0; i< NTARGETS; i++)
{
os << obj.targets[i] << " ";
}
os << "]\n";
os << "isInlined: " << obj.isInlined << "\n";
os << "pathInfoFlags: " << obj.pathInfoFlags << "\n";
os << "stripePatternType: " << obj.stripePatternType << "\n";
os << "stripePatternSize: " << obj.stripePatternSize << "\n";
os << "readable: " << obj.readable << "\n";
os << "isBuddyMirrored: " << obj.isBuddyMirrored << "\n";
os << "isMismirrored: " << obj.isMismirrored << "\n\n";
return os;
}
};
}
#endif

View File

@@ -0,0 +1,72 @@
#ifndef FILTER_H_
#define FILTER_H_
template<typename Source, class Pred>
class Filter
{
public:
typedef typename Source::ElementType ElementType;
typedef typename Source::MarkerType MarkerType;
public:
Filter(Source source, Pred pred)
: source(source), pred(pred)
{
}
bool step()
{
while(this->source.step() )
{
if(this->pred(*get() ) )
return true;
}
return false;
}
ElementType* get()
{
return this->source.get();
}
MarkerType mark() const
{
return this->source.mark();
}
void restore(MarkerType mark)
{
this->source.restore(mark);
}
private:
Source source;
Pred pred;
};
namespace db {
template<typename Pred>
struct FilterOp
{
Pred pred;
template<typename Source>
friend Filter<Source, Pred> operator|(Source source, const FilterOp& op)
{
return Filter<Source, Pred>(source, op.pred);
}
};
template<typename Pred>
inline FilterOp<Pred> where(Pred pred)
{
FilterOp<Pred> result = {pred};
return result;
}
}
#endif

View File

@@ -0,0 +1,38 @@
#ifndef FSID_H_
#define FSID_H_
#include <common/fsck/FsckFsID.h>
#include <database/EntryID.h>
#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
namespace db {
struct FsID {
EntryID id; /* 12 */
EntryID parentDirID; /* 24 */
uint32_t saveNodeID; /* 28 */
int32_t saveDevice; /* 32 */
uint64_t saveInode; /* 40 */
uint64_t isBuddyMirrored:1; /* 48 */
typedef boost::tuple<EntryID, EntryID, uint32_t, int32_t, uint32_t> KeyType;
KeyType pkey() const
{
return KeyType(id, parentDirID, saveNodeID, saveDevice, saveInode);
}
operator FsckFsID() const
{
return FsckFsID(id.str(), parentDirID.str(), NumNodeID(saveNodeID), saveDevice, saveInode,
isBuddyMirrored);
}
};
}
#endif

View File

@@ -0,0 +1,40 @@
#include "FsckDB.h"
#include <program/Program.h>
#include <common/toolkit/StorageTk.h>
#include <common/toolkit/StringTk.h>
#include <database/FsckDBException.h>
#include <database/FsckDBTable.h>
#include <toolkit/FsckTkEx.h>
#include <cstdio>
FsckDB::FsckDB(const std::string& databasePath, size_t fragmentSize, size_t nameCacheLimit,
bool allowCreate)
: log("FsckDB"),
databasePath(databasePath),
dentryTable(new FsckDBDentryTable(databasePath, fragmentSize, nameCacheLimit, allowCreate) ),
fileInodesTable(new FsckDBFileInodesTable(databasePath, fragmentSize, allowCreate) ),
dirInodesTable(new FsckDBDirInodesTable(databasePath, fragmentSize, allowCreate) ),
chunksTable(new FsckDBChunksTable(databasePath, fragmentSize, allowCreate) ),
contDirsTable(new FsckDBContDirsTable(databasePath, fragmentSize, allowCreate) ),
fsIDsTable(new FsckDBFsIDsTable(databasePath, fragmentSize, allowCreate) ),
usedTargetIDsTable(new FsckDBUsedTargetIDsTable(databasePath, fragmentSize, allowCreate) ),
modificationEventsTable(new FsckDBModificationEventsTable(databasePath, fragmentSize,
allowCreate) ),
malformedChunks(databasePath + "/malformedChunks")
{
}
void FsckDB::clear()
{
this->dentryTable->clear();
this->fileInodesTable->clear();
this->dirInodesTable->clear();
this->chunksTable->clear();
this->contDirsTable->clear();
this->fsIDsTable->clear();
this->usedTargetIDsTable->clear();
this->modificationEventsTable->clear();
this->malformedChunks.clear();
}

View File

@@ -0,0 +1,178 @@
/*
* This class is intended to be the interface to the sqlite DB. It can be created once in the
* applicationm, and can then be used in all threads, as sqlite claims to be thread-safe in
* "serialized" mode (which is the default, http://sqlite.org/threadsafe.html).
*/
#ifndef FSCKDB_H_
#define FSCKDB_H_
#include <common/app/log/LogContext.h>
#include <common/Common.h>
#include <common/fsck/FsckChunk.h>
#include <common/fsck/FsckContDir.h>
#include <common/fsck/FsckDirEntry.h>
#include <common/fsck/FsckDirInode.h>
#include <common/fsck/FsckFileInode.h>
#include <common/fsck/FsckFsID.h>
#include <common/fsck/FsckTargetID.h>
#include <common/fsck/FsckDuplicateInodeInfo.h>
#include <common/nodes/TargetMapper.h>
#include <common/storage/striping/StripePattern.h>
#include <common/storage/StorageDefinitions.h>
#include <database/ContDir.h>
#include <database/Cursor.h>
#include <database/DirEntry.h>
#include <database/DirInode.h>
#include <database/DiskList.h>
#include <database/EntryID.h>
#include <database/FileInode.h>
#include <database/Filter.h>
#include <database/VectorSource.h>
#include <toolkit/FsckDefinitions.h>
#include <boost/scoped_ptr.hpp>
class FsckDBDentryTable;
class FsckDBFileInodesTable;
class FsckDBDirInodesTable;
class FsckDBChunksTable;
class FsckDBContDirsTable;
class FsckDBFsIDsTable;
class FsckDBUsedTargetIDsTable;
class FsckDBModificationEventsTable;
class FsckDBDentryErrorTable;
class FsckDBDirInodeErrorTable;
class FsckDBFsIDErrorTable;
class FsckDBFileInodeErrorTable;
class FsckDBChunkErrorTable;
class FsckDBContDirErrorTable;
class FsckDBStripeTargetErrorTable;
namespace checks {
struct InodeAttribs
{
uint64_t size;
uint64_t nlinks;
};
struct OptionalInodeAttribs
{
boost::optional<uint64_t> size;
boost::optional<uint64_t> nlinks;
void reset() {
size.reset();
nlinks.reset();
}
};
typedef std::pair<db::EntryID, std::set<FsckDuplicateInodeInfo>> DuplicatedInode;
}
class FsckDB
{
friend class TestDatabase;
public:
// FsckDB.cpp
FsckDB(const std::string& databasePath, size_t fragmentSize, size_t nameCacheLimit,
bool allowCreate);
void clear();
// FsckDBChecks.cpp
Cursor<checks::DuplicatedInode> findDuplicateInodeIDs();
Cursor<std::list<FsckChunk> > findDuplicateChunks();
Cursor<std::list<db::ContDir>> findDuplicateContDirs();
Cursor<db::DirEntry> findMismirroredDentries();
Cursor<db::DirInode> findMismirroredDirectories();
Cursor<db::FileInode> findMismirroredFiles();
Cursor<db::DirEntry> findDanglingDirEntries();
Cursor<db::DirEntry> findDirEntriesWithBrokenByIDFile();
Cursor<FsckFsID> findOrphanedFsIDFiles();
Cursor<FsckDirInode> findInodesWithWrongOwner();
Cursor<std::pair<db::DirEntry, NumNodeID> > findDirEntriesWithWrongOwner();
Cursor<FsckDirInode> findOrphanedDirInodes();
Cursor<FsckFileInode> findOrphanedFileInodes();
Cursor<FsckChunk> findOrphanedChunks();
Cursor<FsckDirInode> findInodesWithoutContDir();
Cursor<FsckContDir> findOrphanedContDirs();
Cursor<std::pair<FsckFileInode, checks::OptionalInodeAttribs> > findWrongInodeFileAttribs();
Cursor<std::pair<FsckDirInode, checks::InodeAttribs> > findWrongInodeDirAttribs();
Cursor<db::DirEntry> findFilesWithMissingStripeTargets(TargetMapper* targetMapper,
MirrorBuddyGroupMapper* buddyGroupMapper);
Cursor<std::pair<FsckChunk, FsckFileInode> > findChunksWithWrongPermissions();
Cursor<std::pair<FsckChunk, FsckFileInode> > findChunksInWrongPath();
Cursor<db::FileInode> findFilesWithMultipleHardlinks();
private:
LogContext log;
std::string databasePath;
boost::scoped_ptr<FsckDBDentryTable> dentryTable;
boost::scoped_ptr<FsckDBFileInodesTable> fileInodesTable;
boost::scoped_ptr<FsckDBDirInodesTable> dirInodesTable;
boost::scoped_ptr<FsckDBChunksTable> chunksTable;
boost::scoped_ptr<FsckDBContDirsTable> contDirsTable;
boost::scoped_ptr<FsckDBFsIDsTable> fsIDsTable;
boost::scoped_ptr<FsckDBUsedTargetIDsTable> usedTargetIDsTable;
boost::scoped_ptr<FsckDBModificationEventsTable> modificationEventsTable;
DiskList<FsckChunk> malformedChunks;
public:
FsckDBDentryTable* getDentryTable()
{
return this->dentryTable.get();
}
FsckDBFileInodesTable* getFileInodesTable()
{
return this->fileInodesTable.get();
}
FsckDBDirInodesTable* getDirInodesTable()
{
return this->dirInodesTable.get();
}
FsckDBChunksTable* getChunksTable()
{
return this->chunksTable.get();
}
FsckDBContDirsTable* getContDirsTable()
{
return this->contDirsTable.get();
}
FsckDBFsIDsTable* getFsIDsTable()
{
return this->fsIDsTable.get();
}
FsckDBUsedTargetIDsTable* getUsedTargetIDsTable()
{
return this->usedTargetIDsTable.get();
}
FsckDBModificationEventsTable* getModificationEventsTable()
{
return this->modificationEventsTable.get();
}
DiskList<FsckChunk>* getMalformedChunksList()
{
return &malformedChunks;
}
};
#endif /* FSCKDB_H_ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
#include "FsckDBException.h"

View File

@@ -0,0 +1,8 @@
#ifndef FSCKDBEXCEPTION_H
#define FSCKDBEXCEPTION_H
#include <common/toolkit/NamedException.h>
DECLARE_NAMEDEXCEPTION(FsckDBException, "FsckDBException")
#endif /*FSCKDBEXCEPTION_H*/

View File

@@ -0,0 +1,509 @@
#include "FsckDBTable.h"
#include <database/FsckDBException.h>
#include <toolkit/FsckTkEx.h>
#include <boost/make_shared.hpp>
static db::DirEntry fsckDirEntryToDbDirEntry(const FsckDirEntry& dentry)
{
db::DirEntry result = {
db::EntryID::fromStr(dentry.getID() ),
db::EntryID::fromStr(dentry.getParentDirID() ),
dentry.getEntryOwnerNodeID().val(),
dentry.getInodeOwnerNodeID().val(),
dentry.getSaveNodeID().val(), dentry.getSaveDevice(), dentry.getSaveInode(),
{},
dentry.getInternalID(),
dentry.getEntryType(),
dentry.getName().size() < sizeof(result.name.inlined.text),
dentry.getHasInlinedInode(),
dentry.getIsBuddyMirrored(),
};
if(result.fileNameInlined) {
::memset(result.name.inlined.text, '\0', sizeof(result.name.inlined.text));
::strncpy(result.name.inlined.text, dentry.getName().c_str(),
sizeof(result.name.inlined.text) - 1);
}
return result;
}
void FsckDBDentryTable::insert(FsckDirEntryList& dentries, const BulkHandle* handle)
{
NameBuffer& names = handle ? *handle->nameBuffer : getNameBuffer(0);
for(FsckDirEntryListIter it = dentries.begin(), end = dentries.end(); it != end; ++it)
{
db::DirEntry dentry = fsckDirEntryToDbDirEntry(*it);
ByParent link = { dentry.parentDirID, dentry.id, dentry.entryType };
if(!dentry.fileNameInlined)
{
dentry.name.extended.fileID = names.id();
dentry.name.extended.fileOffset = names.put(it->getName() );
}
if(handle)
{
handle->dentries->append(dentry);
handle->byParent->append(link);
}
else
{
this->table.insert(dentry);
this->byParent.insert(link);
}
}
}
void FsckDBDentryTable::updateFieldsExceptParent(FsckDirEntryList& dentries)
{
for(FsckDirEntryListIter it = dentries.begin(), end = dentries.end(); it != end; ++it)
{
db::DirEntry dentry = fsckDirEntryToDbDirEntry(*it);
this->table.remove(dentry.pkey() );
this->table.insert(dentry);
}
}
void FsckDBDentryTable::remove(FsckDirEntryList& dentries)
{
for(FsckDirEntryListIter it = dentries.begin(), end = dentries.end(); it != end; ++it)
this->table.remove(fsckDirEntryToDbDirEntry(*it).pkey() );
}
Table<db::DirEntry>::QueryType FsckDBDentryTable::get()
{
this->table.commitChanges();
return this->table.cursor();
}
Table<FsckDBDentryTable::ByParent>::QueryType FsckDBDentryTable::getByParent()
{
this->byParent.commitChanges();
return this->byParent.cursor();
}
std::pair<bool, db::DirEntry> FsckDBDentryTable::getAnyFor(db::EntryID id)
{
this->table.commitChanges();
struct ops
{
static db::EntryID onlyID(const db::DirEntry::KeyType& key) { return boost::get<0>(key); }
};
return this->table.getByKeyProjection(id, ops::onlyID);
}
std::string FsckDBDentryTable::getNameOf(const db::DirEntry& dentry)
{
if(dentry.fileNameInlined)
return dentry.name.inlined.text;
return getNameBuffer(dentry.name.extended.fileID).get(dentry.name.extended.fileOffset);
}
std::string FsckDBDentryTable::getPathOf(const db::DirEntry& dentry)
{
std::string result = getNameOf(dentry);
db::EntryID parent = dentry.parentDirID;
unsigned depth = 0;
while(!parent.isSpecial() )
{
depth++;
if(depth > 255)
return "[<unresolved>]/" + result;
const NameCacheEntry* cacheEntry = getFromCache(parent);
if(cacheEntry)
{
result = cacheEntry->name + "/" + result;
parent = cacheEntry->parent;
continue;
}
std::pair<bool, db::DirEntry> item = getAnyFor(parent);
if(!item.first)
return "[<unresolved>]/" + result;
std::string parentName = getNameOf(item.second);
result = parentName + "/" + result;
parent = item.second.parentDirID;
addToCache(item.second, parentName);
}
return getNameOf(parent) + "/" + result;
}
void FsckDBFileInodesTable::insert(FsckFileInodeList& fileInodes, const BulkHandle* handle)
{
for(FsckFileInodeListIter it = fileInodes.begin(), end = fileInodes.end(); it != end; ++it)
{
const std::vector<uint16_t>& stripes = it->getStripeTargets();
db::FileInode inode = {
db::EntryID::fromStr(it->getID() ),
db::EntryID::fromStr(it->getParentDirID() ),
db::EntryID::fromStr(it->getPathInfo()->getOrigParentEntryID() ),
it->getParentNodeID().val(),
it->getSaveNodeID().val(),
it->getPathInfo()->getOrigUID(),
it->getUserID(), it->getGroupID(),
(uint64_t) it->getFileSize(), it->getUsedBlocks(),
it->getNumHardLinks(),
it->getSaveInode(),
it->getSaveDevice(),
it->getChunkSize(),
{},
it->getIsInlined(),
uint32_t(it->getPathInfo()->getFlags() ),
it->getStripePatternType(),
uint32_t(stripes.size() ),
it->getReadable(),
it->getIsBuddyMirrored(),
it->getIsMismirrored(),
};
db::StripeTargets extraTargets = { inode.id, {}, 0 };
for(size_t i = 0; i < stripes.size(); i++)
{
if(i < inode.NTARGETS)
inode.targets[i] = stripes[i];
else
{
size_t offsetInPattern = (i - inode.NTARGETS) % extraTargets.NTARGETS;
if(offsetInPattern == 0)
extraTargets.firstTargetIndex = i;
extraTargets.targets[offsetInPattern] = stripes[i];
if(offsetInPattern == extraTargets.NTARGETS - 1)
{
if(handle)
std::get<1>(*handle)->append(extraTargets);
else
this->targets.insert(extraTargets);
extraTargets.firstTargetIndex = 0;
}
}
}
if(handle)
{
std::get<0>(*handle)->append(inode);
if(extraTargets.firstTargetIndex != 0)
std::get<1>(*handle)->append(extraTargets);
}
else
{
this->inodes.insert(inode);
if(extraTargets.firstTargetIndex != 0)
this->targets.insert(extraTargets);
}
}
}
void FsckDBFileInodesTable::update(FsckFileInodeList& inodes)
{
for(FsckFileInodeListIter it = inodes.begin(), end = inodes.end(); it != end; ++it)
{
FsckFileInodeList list(1, *it);
remove(list);
insert(list);
}
}
void FsckDBFileInodesTable::remove(FsckFileInodeList& fileInodes)
{
for(FsckFileInodeListIter it = fileInodes.begin(), end = fileInodes.end(); it != end; ++it)
{
this->inodes.remove(db::EntryID::fromStr(it->getID() ) );
this->targets.remove(db::EntryID::fromStr(it->getID() ) );
}
}
FsckDBFileInodesTable::InodesQueryType FsckDBFileInodesTable::getInodes()
{
this->inodes.commitChanges();
return
this->inodes.cursor()
| db::groupBy(UniqueInlinedInode() )
| db::select(SelectFirst() );
}
Table<db::StripeTargets, true>::QueryType FsckDBFileInodesTable::getTargets()
{
this->targets.commitChanges();
return this->targets.cursor();
}
UInt16Vector FsckDBFileInodesTable::getStripeTargetsByKey(const db::EntryID& id)
{
UInt16Vector allStripeTargets;
std::pair<bool, db::FileInode> result = this->get(id.str());
if (!result.first)
return allStripeTargets;
// firstly copy stripeTargets from FileInode object
auto inode = result.second;
uint32_t targetArrSize = inode.NTARGETS;
uint32_t numTargets = (inode.stripePatternSize > targetArrSize) ? targetArrSize
: inode.stripePatternSize;
std::copy(inode.targets, inode.targets + numTargets, std::back_inserter(allStripeTargets));
// if extraTargets are present then get them from targets table
uint32_t numExtraTargets = inode.stripePatternSize - numTargets;
if (numExtraTargets)
{
this->targets.commitChanges();
auto dbRes = this->targets.getAllByKey(id);
while (dbRes.step())
{
auto elem = dbRes.get();
for (int i=0; i<elem->NTARGETS; i++)
{
allStripeTargets.push_back(elem->targets[i]);
numExtraTargets -= 1;
if (numExtraTargets == 0) break;
}
if (numExtraTargets == 0) break;
}
}
return allStripeTargets;
}
std::pair<bool, db::FileInode> FsckDBFileInodesTable::get(std::string id)
{
this->inodes.commitChanges();
return this->inodes.getByKey(db::EntryID::fromStr(id) );
}
void FsckDBDirInodesTable::insert(FsckDirInodeList& dirInodes, const BulkHandle* handle)
{
for(FsckDirInodeListIter it = dirInodes.begin(), end = dirInodes.end(); it != end; ++it)
{
db::DirInode inode = {
db::EntryID::fromStr(it->getID() ),
db::EntryID::fromStr(it->getParentDirID() ),
it->getParentNodeID().val(),
it->getOwnerNodeID().val(),
it->getSaveNodeID().val(),
it->getStripePatternType(),
it->getReadable(),
it->getIsBuddyMirrored(),
it->getIsMismirrored(),
(uint64_t) it->getSize(),
it->getNumHardLinks(),
};
if(handle)
(*handle)->append(inode);
else
this->table.insert(inode);
}
}
void FsckDBDirInodesTable::update(FsckDirInodeList& inodes)
{
for(FsckDirInodeListIter it = inodes.begin(), end = inodes.end(); it != end; ++it)
{
FsckDirInodeList list(1, *it);
remove(list);
insert(list);
}
}
void FsckDBDirInodesTable::remove(FsckDirInodeList& dirInodes)
{
for(FsckDirInodeListIter it = dirInodes.begin(), end = dirInodes.end(); it != end; ++it)
this->table.remove(db::EntryID::fromStr(it->getID() ) );
}
Table<db::DirInode>::QueryType FsckDBDirInodesTable::get()
{
this->table.commitChanges();
return this->table.cursor();
}
std::pair<bool, FsckDirInode> FsckDBDirInodesTable::get(std::string id)
{
this->table.commitChanges();
return this->table.getByKey(db::EntryID::fromStr(id) );
}
static db::Chunk fsckChunkToDbChunk(FsckChunk& chunk)
{
db::Chunk result = {
db::EntryID::fromStr(chunk.getID() ),
chunk.getTargetID(), chunk.getBuddyGroupID(),
{},
chunk.getFileSize(), chunk.getUsedBlocks(),
chunk.getUserID(), chunk.getGroupID(),
};
strncpy(result.savedPath, chunk.getSavedPath()->str().c_str(),
sizeof(result.savedPath) - 1);
return result;
}
void FsckDBChunksTable::insert(FsckChunkList& chunks, const BulkHandle* handle)
{
for(FsckChunkListIter it = chunks.begin(), end = chunks.end(); it != end; ++it)
{
if(handle)
(*handle)->append(fsckChunkToDbChunk(*it) );
else
this->table.insert(fsckChunkToDbChunk(*it) );
}
}
void FsckDBChunksTable::update(FsckChunkList& chunks)
{
for(FsckChunkListIter it = chunks.begin(), end = chunks.end(); it != end; ++it)
{
db::Chunk chunk = fsckChunkToDbChunk(*it);
this->table.remove(chunk.pkey() );
this->table.insert(chunk);
}
}
void FsckDBChunksTable::remove(const db::Chunk::KeyType& id)
{
this->table.remove(id);
}
Table<db::Chunk>::QueryType FsckDBChunksTable::get()
{
this->table.commitChanges();
return this->table.cursor();
}
static db::ContDir fsckContDirToDbContDir(FsckContDir& contDir)
{
return {
db::EntryID::fromStr(contDir.getID()),
contDir.getSaveNodeID().val(),
contDir.getIsBuddyMirrored(),
};
}
void FsckDBContDirsTable::insert(FsckContDirList& contDirs, const BulkHandle* handle)
{
for(FsckContDirListIter it = contDirs.begin(), end = contDirs.end(); it != end; ++it)
{
if(handle)
(*handle)->append(fsckContDirToDbContDir(*it) );
else
this->table.insert(fsckContDirToDbContDir(*it) );
}
}
Table<db::ContDir>::QueryType FsckDBContDirsTable::get()
{
this->table.commitChanges();
return this->table.cursor();
}
static db::FsID fsckFsIDToDbFsID(FsckFsID& id)
{
return {
db::EntryID::fromStr(id.getID() ),
db::EntryID::fromStr(id.getParentDirID() ),
id.getSaveNodeID().val(), id.getSaveDevice(), id.getSaveInode(),
id.getIsBuddyMirrored(),
};
}
void FsckDBFsIDsTable::insert(FsckFsIDList& fsIDs, const BulkHandle* handle)
{
for(FsckFsIDListIter it = fsIDs.begin(), end = fsIDs.end(); it != end; ++it)
{
if(handle)
(*handle)->append(fsckFsIDToDbFsID(*it) );
else
this->table.insert(fsckFsIDToDbFsID(*it) );
}
}
void FsckDBFsIDsTable::remove(FsckFsIDList& fsIDs)
{
for(FsckFsIDListIter it = fsIDs.begin(), end = fsIDs.end(); it != end; ++it)
this->table.remove(fsckFsIDToDbFsID(*it).pkey() );
}
Table<db::FsID>::QueryType FsckDBFsIDsTable::get()
{
this->table.commitChanges();
return this->table.cursor();
}
void FsckDBUsedTargetIDsTable::insert(FsckTargetIDList& targetIDs, const BulkHandle& handle)
{
for(FsckTargetIDListIter it = targetIDs.begin(), end = targetIDs.end(); it != end; ++it)
{
db::UsedTarget target = { it->getID(), it->getTargetIDType() };
handle->append(target);
}
}
void FsckDBModificationEventsTable::insert(const FsckModificationEventList& events,
const BulkHandle& handle)
{
for(std::list<FsckModificationEvent>::const_iterator it = events.begin(), end = events.end();
it != end; ++it)
{
db::ModificationEvent event = { db::EntryID::fromStr(it->getEntryID() ) };
handle->append(event);
}
}

View File

@@ -0,0 +1,641 @@
#ifndef FsckDBTable_h_drBbZVcUQetDB8UVnMd1Uj
#define FsckDBTable_h_drBbZVcUQetDB8UVnMd1Uj
#include <common/fsck/FsckModificationEvent.h>
#include <database/Chunk.h>
#include <database/ContDir.h>
#include <database/DirEntry.h>
#include <database/DirInode.h>
#include <database/FileInode.h>
#include <database/FsID.h>
#include <database/Group.h>
#include <database/Table.h>
#include <database/ModificationEvent.h>
#include <database/StripeTargets.h>
#include <database/UsedTarget.h>
class FsckDBDentryTable
{
public:
struct ByParent
{
db::EntryID parent; /* 12 */
db::EntryID target; /* 24 */
uint32_t entryType; /* 28 */
typedef std::pair<db::EntryID, db::EntryID> KeyType;
KeyType pkey() const { return KeyType(parent, target); }
};
class NameBuffer
{
public:
NameBuffer(const std::string& path, unsigned id):
fileID(id)
{
fd = ::open(path.c_str(), O_RDWR | O_CREAT, 0660);
if(fd < 0)
throw std::runtime_error("could not open name file");
streamBuf.reserve(4096 * 1024);
writePos = ::lseek(fd, 0, SEEK_END);
if (writePos < 0)
{
close(fd);
fd = -1;
throw std::runtime_error("could not open name file");
}
}
NameBuffer(NameBuffer&&) = delete;
NameBuffer& operator=(NameBuffer&&) = delete;
~NameBuffer()
{
if (fd >= 0)
{
flush();
close(fd);
}
}
uint64_t put(const std::string& name)
{
if (streamBuf.size() + name.size() + 1 > streamBuf.capacity())
flush();
if (name.size() > streamBuf.capacity())
streamBuf.reserve(name.size());
uint64_t result = writePos + streamBuf.size();
streamBuf.insert(streamBuf.end(), name.begin(), name.end());
streamBuf.push_back(0);
return result;
}
std::string get(uint64_t offset)
{
flush();
char buffer[255 + 1];
ssize_t readRes = ::pread(fd, buffer, sizeof(buffer), offset);
// if the read did not fill the full buffer, we can
// a) have an error, or
// b) have a short read.
// errors should be reported directly. short reads are likely caused be trying to
// read past the end of the file. since we cannot possible have a sane read that
// stretches beyond writePos here (as we have flushed the write buffer), we can
// reliably detect both.
if (readRes < 0 || ssize_t(offset) + readRes > writePos)
throw std::runtime_error("could not read name file: " + std::string(strerror(errno)));
return buffer;
}
unsigned id() const { return fileID; }
private:
unsigned fileID;
std::vector<char> streamBuf;
int fd;
ssize_t writePos;
void flush()
{
ssize_t writeRes = ::pwrite(fd, &streamBuf[0], streamBuf.size(), writePos);
if (writeRes < 0 || size_t(writeRes) < streamBuf.size())
throw std::runtime_error("error in flush: " + std::string(strerror(errno)));
writePos += writeRes;
streamBuf.resize(0);
}
};
struct BulkHandle
{
boost::shared_ptr<Buffer<db::DirEntry> > dentries;
boost::shared_ptr<Buffer<ByParent> > byParent;
NameBuffer* nameBuffer;
};
private:
struct NameCacheEntry
{
db::EntryID id;
db::EntryID parent;
std::string name;
std::list<NameCacheEntry*>::iterator lruLink;
};
public:
FsckDBDentryTable(const std::string& dbPath, size_t fragmentSize, size_t nameCacheLimit,
bool allowCreate)
: dbPath(dbPath),
table(dbPath + "/dentries", fragmentSize, allowCreate),
byParent(dbPath + "/dentriesbyparent", fragmentSize, allowCreate),
insertSeqNo(0),
nameCacheLimit(nameCacheLimit)
{
}
void insert(FsckDirEntryList& dentries, const BulkHandle* handle = NULL);
void updateFieldsExceptParent(FsckDirEntryList& dentries);
void remove(FsckDirEntryList& dentries);
Table<db::DirEntry>::QueryType get();
std::pair<bool, db::DirEntry> getAnyFor(db::EntryID id);
Table<ByParent>::QueryType getByParent();
std::string getNameOf(const db::DirEntry& dentry);
std::string getPathOf(const db::DirEntry& dentry);
private:
std::string dbPath;
Table<db::DirEntry> table;
Table<ByParent> byParent;
std::map<uint64_t, boost::shared_ptr<NameBuffer> > nameBuffers;
uint64_t insertSeqNo;
std::map<db::EntryID, NameCacheEntry> nameCache;
std::list<NameCacheEntry*> nameCacheLRU;
size_t nameCacheLimit;
NameBuffer& getNameBuffer(unsigned id)
{
if(this->nameBuffers.count(id) )
return *this->nameBuffers[id];
const std::string& nextNameFile = dbPath + "/dentrynames."
+ StringTk::intToStr(id);
boost::shared_ptr<NameBuffer> buf = boost::make_shared<NameBuffer>(
nextNameFile, id);
this->nameBuffers[id] = buf;
return *buf;
}
void addToCache(const db::DirEntry& entry, const std::string& name)
{
if(nameCache.size() >= nameCacheLimit)
{
nameCache.erase(nameCacheLRU.front()->id);
nameCacheLRU.pop_front();
}
NameCacheEntry cacheEntry = { entry.id, entry.parentDirID, name, nameCacheLRU.end() };
std::pair<std::map<db::EntryID, NameCacheEntry>::iterator, bool> pos = nameCache.insert(
std::make_pair(entry.id, cacheEntry) );
if(!pos.second)
return;
pos.first->second.lruLink = nameCacheLRU.insert(nameCacheLRU.end(), &pos.first->second);
}
const NameCacheEntry* getFromCache(db::EntryID id)
{
std::map<db::EntryID, NameCacheEntry>::iterator pos = nameCache.find(id);
if(pos == nameCache.end() )
return NULL;
nameCacheLRU.splice(nameCacheLRU.end(), nameCacheLRU, pos->second.lruLink);
return &pos->second;
}
public:
void clear()
{
this->table.clear();
this->byParent.clear();
this->insertSeqNo = 0;
this->nameCache.clear();
this->nameCacheLRU.clear();
}
BulkHandle newBulkHandle()
{
BulkHandle result = {
this->table.bulkInsert(),
this->byParent.bulkInsert(),
&getNameBuffer(this->nameBuffers.size() ),
};
return result;
}
void flush(BulkHandle& handle)
{
handle = {{}, {}, nullptr};
}
void insert(FsckDirEntryList& dentries, const BulkHandle& handle)
{
insert(dentries, &handle);
}
std::string getNameOf(db::EntryID id)
{
if(id == db::EntryID::anchor() || id == db::EntryID::root() )
return "";
else
if(id == db::EntryID::disposal() )
return "[<disposal>]";
else
if(id == db::EntryID::mdisposal() )
return "[<mdisposal>]";
std::pair<bool, db::DirEntry> item = getAnyFor(id);
if(!item.first)
return "[<unresolved>]";
else
return getNameOf(item.second);
}
std::string getPathOf(db::EntryID id)
{
std::pair<bool, db::DirEntry> item = getAnyFor(id);
if(!item.first)
return "[<unresolved>]";
else
return getPathOf(item.second);
}
void commitChanges()
{
table.commitChanges();
}
};
class FsckDBFileInodesTable
{
public:
typedef std::tuple<
boost::shared_ptr<Buffer<db::FileInode> >,
boost::shared_ptr<Buffer<db::StripeTargets> > > BulkHandle;
struct UniqueInlinedInode
{
typedef boost::tuple<db::EntryID, uint64_t, uint32_t, int32_t> KeyType;
typedef db::FileInode ProjType;
typedef int GroupType;
KeyType key(const db::FileInode& fi)
{
return boost::make_tuple(fi.id, fi.saveInode, fi.saveNodeID, fi.saveDevice);
}
db::FileInode project(const db::FileInode& fi)
{
return fi;
}
void step(const db::FileInode& fi) {}
GroupType finish()
{
return 0;
}
};
struct SelectFirst
{
typedef db::FileInode result_type;
db::FileInode operator()(std::pair<db::FileInode, int>& pair) const
{
return pair.first;
}
};
typedef
Select<
Group<
Table<db::FileInode>::QueryType,
UniqueInlinedInode>,
SelectFirst> InodesQueryType;
public:
FsckDBFileInodesTable(const std::string& dbPath, size_t fragmentSize, bool allowCreate)
: inodes(dbPath + "/fileinodes", fragmentSize, allowCreate),
targets(dbPath + "/filetargets", fragmentSize, allowCreate)
{
}
void insert(FsckFileInodeList& fileInodes, const BulkHandle* handle = NULL);
void update(FsckFileInodeList& inodes);
void remove(FsckFileInodeList& fileInodes);
InodesQueryType getInodes();
Table<db::StripeTargets, true>::QueryType getTargets();
UInt16Vector getStripeTargetsByKey(const db::EntryID& id);
std::pair<bool, db::FileInode> get(std::string id);
private:
Table<db::FileInode> inodes;
Table<db::StripeTargets, true> targets;
public:
void clear()
{
inodes.clear();
targets.clear();
}
void remove(db::EntryID id)
{
inodes.remove(id);
targets.remove(id);
}
BulkHandle newBulkHandle()
{
return BulkHandle(this->inodes.bulkInsert(), this->targets.bulkInsert() );
}
void flush(BulkHandle& handle)
{
handle = {};
}
void insert(FsckFileInodeList& fileInodes, const BulkHandle& handle)
{
insert(fileInodes, &handle);
}
void commitChanges()
{
inodes.commitChanges();
targets.commitChanges();
}
};
class FsckDBDirInodesTable
{
public:
typedef boost::shared_ptr<Buffer<db::DirInode> > BulkHandle;
public:
FsckDBDirInodesTable(const std::string& dbPath, size_t fragmentSize, bool allowCreate)
: table(dbPath + "/dirinodes", fragmentSize, allowCreate)
{
}
void insert(FsckDirInodeList& dirInodes, const BulkHandle* handle = NULL);
void update(FsckDirInodeList& inodes);
void remove(FsckDirInodeList& dirInodes);
Table<db::DirInode>::QueryType get();
std::pair<bool, FsckDirInode> get(std::string id);
private:
Table<db::DirInode> table;
public:
void clear()
{
this->table.clear();
}
BulkHandle newBulkHandle()
{
return this->table.bulkInsert();
}
void flush(BulkHandle& handle)
{
handle = {};
}
void insert(FsckDirInodeList& fileInodes, const BulkHandle& handle)
{
insert(fileInodes, &handle);
}
void commitChanges()
{
table.commitChanges();
}
};
class FsckDBChunksTable
{
public:
typedef boost::shared_ptr<Buffer<db::Chunk> > BulkHandle;
public:
FsckDBChunksTable(const std::string& dbPath, size_t fragmentSize, bool allowCreate)
: table(dbPath + "/chunks", fragmentSize, allowCreate)
{
}
void insert(FsckChunkList& chunks, const BulkHandle* handle = NULL);
void update(FsckChunkList& chunks);
void remove(const db::Chunk::KeyType& id);
Table<db::Chunk>::QueryType get();
private:
Table<db::Chunk> table;
public:
void clear()
{
table.clear();
}
BulkHandle newBulkHandle()
{
return this->table.bulkInsert();
}
void flush(BulkHandle& handle)
{
handle = {};
}
void insert(FsckChunkList& chunks, const BulkHandle& handle)
{
insert(chunks, &handle);
}
void commitChanges()
{
table.commitChanges();
}
};
class FsckDBContDirsTable
{
public:
typedef boost::shared_ptr<Buffer<db::ContDir> > BulkHandle;
public:
FsckDBContDirsTable(const std::string& dbPath, size_t fragmentSize, bool allowCreate)
: table(dbPath + "/contdirs", fragmentSize, allowCreate)
{
}
void insert(FsckContDirList& contDirs, const BulkHandle* handle = NULL);
Table<db::ContDir>::QueryType get();
private:
Table<db::ContDir> table;
public:
void clear()
{
table.clear();
}
BulkHandle newBulkHandle()
{
return this->table.bulkInsert();
}
void flush(BulkHandle& handle)
{
handle = {};
}
void insert(FsckContDirList& contDirs, const BulkHandle& handle)
{
insert(contDirs, &handle);
}
void commitChanges()
{
table.commitChanges();
}
};
class FsckDBFsIDsTable
{
public:
typedef boost::shared_ptr<Buffer<db::FsID> > BulkHandle;
public:
FsckDBFsIDsTable(const std::string& dbPath, size_t fragmentSize, bool allowCreate)
: table(dbPath + "/fsids", fragmentSize, allowCreate)
{
}
void insert(FsckFsIDList& fsIDs, const BulkHandle* handle = NULL);
void remove(FsckFsIDList& fsIDs);
Table<db::FsID>::QueryType get();
private:
Table<db::FsID> table;
public:
void clear()
{
this->table.clear();
}
BulkHandle newBulkHandle()
{
return this->table.bulkInsert();
}
void flush(BulkHandle& handle)
{
handle = {};
}
void insert(FsckFsIDList& fsIDs, const BulkHandle& handle)
{
insert(fsIDs, &handle);
}
void commitChanges()
{
table.commitChanges();
}
};
template<typename Data>
class FsckUniqueTable
{
public:
typedef boost::shared_ptr<Buffer<Data> > BulkHandle;
protected:
FsckUniqueTable(const std::string& name, size_t bufferSize, bool allowCreate)
: set(name, allowCreate), bufferSize(bufferSize)
{}
private:
Set<Data> set;
size_t bufferSize;
std::vector<boost::weak_ptr<Buffer<Data> > > bulkHandles;
public:
void clear()
{
set.clear();
bulkHandles.clear();
}
BulkHandle newBulkHandle()
{
BulkHandle result(new Buffer<Data>(set, bufferSize) );
bulkHandles.push_back(result);
return result;
}
void flush(BulkHandle& handle)
{
handle = {};
}
SetFragmentCursor<Data> get()
{
if(!bulkHandles.empty() )
{
for(size_t i = 0; i < bulkHandles.size(); i++)
{
if(!bulkHandles[i].expired() )
throw std::runtime_error("still has open bulk handles");
}
bulkHandles.clear();
set.makeUnique();
}
return set.cursor();
}
};
class FsckDBUsedTargetIDsTable : public FsckUniqueTable<db::UsedTarget>
{
public:
FsckDBUsedTargetIDsTable(const std::string& dbPath, size_t fragmentSize, bool allowCreate)
: FsckUniqueTable<db::UsedTarget>(dbPath + "/usedtargets", fragmentSize, allowCreate)
{
}
void insert(FsckTargetIDList& targetIDs, const BulkHandle& handle);
};
class FsckDBModificationEventsTable : public FsckUniqueTable<db::ModificationEvent>
{
public:
FsckDBModificationEventsTable(const std::string& dbPath, size_t fragmentSize,
bool allowCreate)
: FsckUniqueTable<db::ModificationEvent>(dbPath + "/modevents", fragmentSize, allowCreate)
{
}
void insert(const FsckModificationEventList& events, const BulkHandle& handle);
};
#endif

View File

@@ -0,0 +1,131 @@
#ifndef GROUP_H_
#define GROUP_H_
#include <boost/utility/result_of.hpp>
#include <utility>
/*
* requires Ops : struct {
* typedef <key-type> KeyType;
* typedef <proj-type> ProjType;
* typedef <group-type> GroupType;
*
* KeyType key(Source::ElementType&);
* ProjType project(Source::ElementType&);
* void step(Source::ElementType&);
*
* GroupType finish();
* }
*
* yields sequence (Source::ElementType, <row-type>)
*/
template<typename Source, typename Ops>
class Group
{
private:
typedef typename Ops::KeyType KeyType;
typedef typename Ops::ProjType ProjType;
typedef typename Ops::GroupType GroupType;
public:
typedef std::pair<ProjType, GroupType> ElementType;
typedef typename Source::MarkerType MarkerType;
public:
Group(Source source, Ops ops)
: source(source), ops(ops), sourceIsActive(false)
{
}
bool step()
{
if(!this->sourceIsActive && !this->source.step() )
return false;
groupCurrentSet();
return true;
}
ElementType* get()
{
return &current;
}
MarkerType mark() const
{
return this->currentMark;
}
void restore(MarkerType mark)
{
this->source.restore(mark);
groupCurrentSet();
}
private:
void groupCurrentSet()
{
typename Source::ElementType* row = this->source.get();
this->currentMark = this->source.mark();
this->current.first = this->ops.project(*row);
this->ops.step(*row);
KeyType currentKey = this->ops.key(*row);
while(true)
{
if(!this->source.step() )
{
this->sourceIsActive = false;
break;
}
row = this->source.get();
if(!(currentKey == this->ops.key(*row) ) )
{
this->sourceIsActive = true;
break;
}
this->ops.step(*row);
}
this->current.second = this->ops.finish();
}
private:
Source source;
Ops ops;
ElementType current;
typename Source::MarkerType currentMark;
bool sourceIsActive;
};
namespace db {
template<typename Ops>
struct GroupOp
{
Ops ops;
template<typename Source>
friend Group<Source, Ops> operator|(Source source, const GroupOp& fn)
{
return Group<Source, Ops>(source, fn.ops);
}
};
template<typename Ops>
inline GroupOp<Ops> groupBy(Ops ops)
{
GroupOp<Ops> result = {ops};
return result;
}
}
#endif

View File

@@ -0,0 +1,189 @@
#ifndef LEFTJOINEQ_H_
#define LEFTJOINEQ_H_
#include <common/Common.h>
#include <cstddef>
#include <utility>
#include <boost/type_traits/decay.hpp>
#include <boost/utility/result_of.hpp>
template<typename Left, typename Right, typename KeyExtract>
class LeftJoinEq
{
private:
enum State {
s_next_left,
s_first_right,
s_next_right,
s_only_left,
};
typedef typename boost::decay<
typename boost::result_of<KeyExtract(typename Left::ElementType&)>::type
>::type LeftKey;
typedef typename boost::decay<
typename boost::result_of<KeyExtract(typename Right::ElementType&)>::type
>::type RightKey;
public:
typedef std::pair<typename Left::ElementType, typename Right::ElementType*> ElementType;
struct MarkerType
{
typename Left::MarkerType leftMark;
typename Right::MarkerType rightMark;
State state;
bool hasRightMark;
typename Right::MarkerType innerRightMark;
};
public:
LeftJoinEq(Left left, Right right, KeyExtract keyExtract)
: left(left), right(right), keyExtract(keyExtract), state(s_next_left),
hasRightMark(false)
{
if(!this->right.step() )
this->state = s_only_left;
}
bool step()
{
switch(this->state)
{
case s_only_left:
if(!this->left.step() )
return false;
return makeCurrent(false);
case s_next_left:
if(!this->left.step() )
{
this->state = s_only_left;
return false;
}
if(this->hasRightMark &&
this->keyExtract(*this->left.get() ) == this->rightKeyAtMark)
this->right.restore(this->rightMark);
else
this->hasRightMark = false;
BEEGFS_FALLTHROUGH;
case s_first_right: {
while(rightKey() < leftKey() )
{
if(!this->right.step() )
{
this->state = s_only_left;
return makeCurrent(false);
}
}
if(leftKey() == rightKey() )
{
this->state = s_next_right;
this->hasRightMark = true;
this->rightKeyAtMark = rightKey();
this->rightMark = this->right.mark();
return makeCurrent(true);
}
this->state = s_next_left;
return makeCurrent(false);
}
case s_next_right: {
if(!this->right.step() )
{
this->state = s_next_left;
return step();
}
if(leftKey() == rightKey() )
{
this->state = s_next_right;
return makeCurrent(true);
}
this->state = s_next_left;
return step();
}
}
return false;
}
ElementType* get()
{
return &current;
}
MarkerType mark() const
{
MarkerType result = { this->left.mark(), this->right.mark(), this->state,
this->hasRightMark, this->rightMark };
return result;
}
void restore(MarkerType mark)
{
this->hasRightMark = mark.hasRightMark;
if(mark.hasRightMark)
{
this->rightMark = mark.innerRightMark;
this->right.restore(mark.innerRightMark);
this->rightKeyAtMark = rightKey();
}
this->left.restore(mark.leftMark);
this->right.restore(mark.rightMark);
this->state = mark.state;
if(this->state == s_only_left)
makeCurrent(false);
else
makeCurrent(leftKey() == rightKey() );
}
private:
bool makeCurrent(bool hasRight)
{
if(hasRight)
this->current = ElementType(*this->left.get(), this->right.get() );
else
this->current = ElementType(*this->left.get(), nullptr);
return true;
}
LeftKey leftKey() { return this->keyExtract(*this->left.get() ); }
RightKey rightKey() { return this->keyExtract(*this->right.get() ); }
private:
Left left;
Right right;
KeyExtract keyExtract;
ElementType current;
State state;
bool hasRightMark;
RightKey rightKeyAtMark;
typename Right::MarkerType rightMark;
};
namespace db {
template<typename KeyExtract, typename Left, typename Right>
inline LeftJoinEq<Left, Right, KeyExtract> leftJoinBy(KeyExtract ex, Left left, Right right)
{
return LeftJoinEq<Left, Right, KeyExtract>(left, right, ex);
}
}
#endif

View File

@@ -0,0 +1,18 @@
#ifndef MODFICATIONEVENT_H_
#define MODFICATIONEVENT_H_
#include <database/EntryID.h>
namespace db {
struct ModificationEvent
{
EntryID id;
typedef EntryID KeyType;
EntryID pkey() const { return id; }
};
}
#endif

View File

@@ -0,0 +1,74 @@
#ifndef SELECT_H_
#define SELECT_H_
#include <boost/utility/result_of.hpp>
template<typename Source, typename Fn>
class Select
{
public:
typedef typename boost::result_of<Fn(typename Source::ElementType&)>::type ElementType;
typedef typename Source::MarkerType MarkerType;
public:
Select(Source source, Fn fn)
: source(source), fn(fn)
{
}
bool step()
{
if(!this->source.step() )
return false;
this->current = this->fn(*this->source.get() );
return true;
}
ElementType* get()
{
return &current;
}
MarkerType mark() const
{
return this->source.mark();
}
void restore(MarkerType mark)
{
this->source.restore(mark);
this->current = this->fn(*this->source.get() );
}
private:
Source source;
Fn fn;
ElementType current;
};
namespace db {
template<typename Pred>
struct SelectOp
{
Pred pred;
template<typename Source>
friend Select<Source, Pred> operator|(Source source, const SelectOp& op)
{
return Select<Source, Pred>(source, op.pred);
}
};
template<typename Pred>
inline SelectOp<Pred> select(Pred pred)
{
SelectOp<Pred> result = {pred};
return result;
}
}
#endif

380
fsck/source/database/Set.h Normal file
View File

@@ -0,0 +1,380 @@
#ifndef SET_H_
#define SET_H_
#include <common/threading/Mutex.h>
#include <database/SetFragment.h>
#include <database/SetFragmentCursor.h>
#include <database/Union.h>
#include <iomanip>
#include <fstream>
#include <limits>
#include <map>
#include <mutex>
#include <sstream>
#include <limits.h>
#include <boost/format.hpp>
template<typename Data>
class Set {
public:
typedef SetFragment<Data> Fragment;
typedef SetFragmentCursor<Data> Cursor;
private:
static const unsigned VERSION = 1;
std::string basename;
unsigned nextID;
std::map<std::string, Fragment*> openFragments;
bool dropped;
Mutex mutex;
typedef typename std::map<std::string, Fragment*>::iterator FragmentIter;
Set(const Set&);
Set& operator=(const Set&);
std::string fragmentName(unsigned id)
{
std::stringstream ss;
ss << basename << '.' << std::setw(5) << std::setfill('0') << id;
return ss.str();
}
Fragment* getFragment(const std::string& file, bool allowCreate)
{
if(openFragments.count(file) )
return openFragments[file];
Fragment* fragment = new Fragment(file, allowCreate);
openFragments[file] = fragment;
return fragment;
}
void acceptFragment(Fragment* foreign)
{
const unsigned newID = nextID++;
const std::string& file = fragmentName(newID);
foreign->rename(file);
openFragments[file] = foreign;
saveConfig();
}
void removeFragment(const std::string& file)
{
Fragment* fragment = openFragments[file];
openFragments.erase(file);
fragment->drop();
delete fragment;
}
void loadConfig(bool allowCreate)
{
std::ifstream in( (basename + ".t").c_str() );
if(!in)
{
if(allowCreate)
return;
throw std::runtime_error("could not read set description file " + basename);
}
{
char code;
unsigned version;
in >> code >> version;
if (code != 'v' || version != VERSION) {
throw std::runtime_error(str(
boost::format("cannot read set %1% with version %2%")
% basename
% version));
}
}
in >> nextID;
unsigned count = 0;
in >> count;
in.ignore(1, '\n');
while(count > 0)
{
std::string file;
std::getline(in, file);
if(!in)
break;
getFragment(basename + file, false);
count--;
}
if(!in || in.peek() != std::ifstream::traits_type::eof() )
throw std::runtime_error("bad set description for " + basename);
}
void saveConfig()
{
std::ofstream out((basename + ".t").c_str(), std::ostream::trunc);
out << 'v' << VERSION << '\n';
out << nextID << '\n';
out << openFragments.size() << '\n';
for (FragmentIter it = openFragments.begin(), end = openFragments.end(); it != end; ++it) {
if (it->second->filename().find(basename) != 0)
throw std::runtime_error("set corrupted: " + basename);
out << it->second->filename().substr(basename.size()) << '\n';
}
}
static std::string makeAbsolute(const std::string& path)
{
std::string::size_type spos = path.find_last_of('/');
if (spos == path.npos)
spos = 0;
else
spos += 1;
// can't/don't want to use ::basename because it may return static buffers
const std::string basename = path.substr(spos);
if (basename.empty() || basename == "." || basename == "..")
throw std::runtime_error("bad path");
if (path[0] == '/')
return path;
char cwd[PATH_MAX];
if (::getcwd(cwd, sizeof(cwd)) == NULL)
throw std::runtime_error("bad path");
return cwd + ('/' + path);
}
public:
Set(const std::string& basename, bool allowCreate = true)
: basename(makeAbsolute(basename) ), nextID(0), dropped(false)
{
loadConfig(allowCreate);
}
~Set()
{
if(dropped)
return;
saveConfig();
for(FragmentIter it = openFragments.begin(), end = openFragments.end(); it != end; ++it)
delete it->second;
}
size_t size()
{
size_t result = 0;
for(FragmentIter it = openFragments.begin(), end = openFragments.end(); it != end; ++it)
result += it->second->size();
return result;
}
Fragment* newFragment()
{
const std::lock_guard<Mutex> lock(mutex);
Fragment* result = getFragment(fragmentName(nextID++), true);
saveConfig();
return result;
}
void sort()
{
if(openFragments.empty() )
newFragment();
std::multimap<size_t, Fragment*> sortedFragments;
for(FragmentIter it = openFragments.begin(), end = openFragments.end(); it != end; ++it)
it->second->flush();
for(FragmentIter it = openFragments.begin(), end = openFragments.end(); it != end; ++it)
{
it->second->sort();
sortedFragments.insert(std::make_pair(it->second->size(), it->second) );
}
static const unsigned MERGE_WIDTH = 4;
if(sortedFragments.size() == 1)
return;
saveConfig();
while(sortedFragments.size() > 1)
{
Fragment* inputs[MERGE_WIDTH] = {};
unsigned inputCount = 0;
for(inputCount = 0; inputCount < MERGE_WIDTH; inputCount++)
{
if(sortedFragments.empty() )
break;
inputs[inputCount] = sortedFragments.begin()->second;
sortedFragments.erase(sortedFragments.begin());
}
SetFragment<Data>* merged = getFragment(fragmentName(nextID++), true);
struct op
{
static typename Data::KeyType key(const Data& d) { return d.pkey(); }
};
typedef typename Data::KeyType (*key_t)(const Data&);
typedef Union<Cursor, Cursor, key_t> L1Union;
switch(inputCount)
{
case 2: {
L1Union u(Cursor(*inputs[0]), (Cursor(*inputs[1]) ), op::key);
while(u.step() )
merged->append(*u.get() );
break;
}
case 3: {
Union<L1Union, SetFragmentCursor<Data>, key_t> u(
L1Union(Cursor(*inputs[0]), (Cursor(*inputs[1]) ), op::key),
Cursor(*inputs[2]),
op::key);
while(u.step() )
merged->append(*u.get() );
break;
}
case 4: {
Union<L1Union, L1Union, key_t> u(
L1Union(Cursor(*inputs[0]), (Cursor(*inputs[1]) ), op::key),
L1Union(Cursor(*inputs[2]), (Cursor(*inputs[3]) ), op::key),
op::key);
while(u.step() )
merged->append(*u.get() );
break;
}
default:
throw std::runtime_error("");
}
merged->flush();
sortedFragments.insert(std::make_pair(merged->size(), merged) );
for (unsigned i = 0; i < inputCount; i++)
removeFragment(inputs[i]->filename() );
saveConfig();
}
}
Cursor cursor()
{
sort();
return Cursor(*openFragments.begin()->second);
}
template<typename Key, typename Fn>
std::pair<bool, Data> getByKeyProjection(const Key& key, Fn fn)
{
sort();
if(openFragments.empty() )
return std::make_pair(false, Data() );
return openFragments.begin()->second->getByKeyProjection(key, fn);
}
std::pair<bool, Data> getByKey(const typename Data::KeyType& key)
{
struct ops
{
static typename Data::KeyType key(const typename Data::KeyType& key) { return key; }
};
return getByKeyProjection(key, ops::key);
}
void clear()
{
while(!openFragments.empty() )
removeFragment(openFragments.begin()->first);
nextID = 0;
saveConfig();
}
void drop()
{
clear();
if(::unlink( (basename + ".t").c_str() ) )
throw std::runtime_error("could not unlink set file " + basename + ": " + std::string(strerror(errno)));
dropped = true;
}
void mergeInto(Set& other)
{
for(FragmentIter it = openFragments.begin(), end = openFragments.end(); it != end; ++it)
other.acceptFragment(it->second);
openFragments.clear();
saveConfig();
}
void makeUnique()
{
sort();
Fragment* input = openFragments.begin()->second;
Cursor cursor(*input);
typename Data::KeyType key;
if(!cursor.step() )
return;
Fragment* unique = newFragment();
unique->append(*cursor.get() );
key = cursor.get()->pkey();
while(cursor.step() )
{
if(key == cursor.get()->pkey() )
continue;
unique->append(*cursor.get() );
key = cursor.get()->pkey();
}
unique->flush();
removeFragment(input->filename() );
saveConfig();
}
};
#endif

View File

@@ -0,0 +1,325 @@
#ifndef SETFRAGMENT_H_
#define SETFRAGMENT_H_
#include <algorithm>
#include <cerrno>
#include <stdexcept>
#include <string>
#include <vector>
#include <boost/scoped_array.hpp>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
struct FragmentDoesNotExist : public std::runtime_error
{
FragmentDoesNotExist(const std::string& file)
: runtime_error(file)
{}
};
template<typename Data>
class SetFragment {
public:
static const unsigned CONFIG_AREA_SIZE = 4096;
static const size_t BUFFER_SIZE = 4ULL * 1024 * 1024;
private:
std::string file;
int fd;
size_t itemCount;
bool sorted;
std::vector<Data> buffer;
size_t firstBufferedItem;
ssize_t firstDirtyItem;
ssize_t lastDirtyItem;
typename Data::KeyType lastItemKey;
SetFragment(const SetFragment&);
SetFragment& operator=(const SetFragment&);
size_t readBlock(Data* into, size_t count, size_t from)
{
size_t total = 0;
count *= sizeof(Data);
from *= sizeof(Data);
from += CONFIG_AREA_SIZE;
char* buf = (char*) into;
while(total < count)
{
ssize_t current = ::pread(fd, buf + total, count - total, from + total);
total += current;
if (current < 0)
throw std::runtime_error("read failed: " + std::string(strerror(errno)));
if (current == 0)
break;
}
return total / sizeof(Data);
}
size_t writeBlock(const Data* source, size_t count, size_t from)
{
size_t total = 0;
count *= sizeof(Data);
from *= sizeof(Data);
from += CONFIG_AREA_SIZE;
const char* buf = (const char*) source;
while(total < count)
{
ssize_t current = ::pwrite(fd, buf + total, count - total, from + total);
total += current;
if (current < 0)
throw std::runtime_error("write failed: " + std::string(strerror(errno)));
if (current == 0)
break;
}
return total / sizeof(Data);
}
void flushBuffer()
{
if(firstDirtyItem < 0)
return;
const Data* first = &buffer[firstDirtyItem - firstBufferedItem];
writeBlock(first, lastDirtyItem - firstDirtyItem + 1, firstDirtyItem);
buffer.clear();
firstDirtyItem = -1;
}
void bufferFileRange(size_t begin, size_t end)
{
flushBuffer();
const size_t firstAddress = begin;
const size_t windowSize = end == size_t(-1) ? end : (end - begin + 1);
const size_t totalRead = std::min<size_t>(windowSize, BUFFER_SIZE / sizeof(Data) );
buffer.resize(totalRead);
size_t read = readBlock(&buffer[0], totalRead, firstAddress);
firstBufferedItem = begin;
buffer.resize(read);
}
public:
SetFragment(const std::string& file, bool allowCreate = true)
: file(file),
itemCount(0),
firstBufferedItem(0), firstDirtyItem(-1), lastDirtyItem(0)
{
fd = ::open(file.c_str(), O_RDWR | (allowCreate ? O_CREAT : 0), 0660);
if(fd < 0)
{
int eno = errno;
if(!allowCreate && errno == ENOENT)
throw FragmentDoesNotExist(file);
else
throw std::runtime_error("could not open fragment file " + file + ": " + strerror(eno));
}
off_t totalSize = ::lseek(fd, 0, SEEK_END);
if(totalSize > 0)
{
int eno = errno;
if (totalSize < (off_t) CONFIG_AREA_SIZE)
throw std::runtime_error("error while opening fragment file " + file + ": " + strerror(eno));
else
if ( (totalSize - CONFIG_AREA_SIZE) % sizeof(Data) )
throw std::runtime_error("error while opening fragment file " + file + ": " + strerror(eno));
}
if(totalSize == 0)
{
sorted = true;
itemCount = 0;
}
else
{
if (::pread(fd, &sorted, sizeof(sorted), 0) != sizeof(sorted)) {
int eno = errno;
throw std::runtime_error("error while opening fragment file " + file + ": " + strerror(eno));
}
itemCount = (totalSize - CONFIG_AREA_SIZE) / sizeof(Data);
}
if(itemCount > 0)
lastItemKey = (*this)[itemCount - 1].pkey();
}
~SetFragment()
{
if(fd >= 0)
{
flush();
close(fd);
}
}
const std::string filename() const { return file; }
size_t size() const { return itemCount; }
void append(const Data& data)
{
if(itemCount > firstBufferedItem + buffer.size() )
{
flushBuffer();
firstBufferedItem = itemCount;
}
if(firstDirtyItem < 0)
firstDirtyItem = itemCount;
lastDirtyItem = itemCount;
if(itemCount > 0 && sorted)
sorted = lastItemKey < data.pkey();
buffer.push_back(data);
itemCount++;
lastItemKey = data.pkey();
if(buffer.size() * sizeof(Data) >= BUFFER_SIZE)
flushBuffer();
}
Data& operator[](size_t offset)
{
if(offset < firstBufferedItem || offset >= firstBufferedItem + buffer.size() )
bufferFileRange(offset == 0 ? 0 : offset - 1, -1);
return buffer[offset - firstBufferedItem];
}
void flush()
{
if(firstDirtyItem < 0)
return;
flushBuffer();
buffer = std::vector<Data>();
if (::pwrite(fd, &sorted, sizeof(sorted), 0) < 0) {
int eno = errno;
throw std::runtime_error("error in flush of " + file + ": " + strerror(eno));
}
// truncate to CONFIG_AREA_SIZE (for reopen)
if (itemCount == 0 && ::ftruncate(fd, CONFIG_AREA_SIZE) < 0) {
int eno = errno;
throw std::runtime_error("error in flush of " + file + ": " + strerror(eno));
}
}
void sort()
{
flush();
if(sorted)
return;
boost::scoped_array<Data> data(new Data[size()]);
if(readBlock(data.get(), size(), 0) < size() )
throw 42;
struct ops
{
static bool compare(const Data& l, const Data& r)
{
return l.pkey() < r.pkey();
}
};
std::sort(data.get(), data.get() + size(), ops::compare);
writeBlock(data.get(), size(), 0);
sorted = true;
flush();
}
void drop()
{
flush();
if (::unlink(file.c_str()) < 0) {
int eno = errno;
throw std::runtime_error("could not unlink fragment file " + file + ": " + strerror(eno));
}
close(fd);
fd = -1;
}
void rename(const std::string& to)
{
if (::rename(file.c_str(), to.c_str()) < 0) {
int eno = errno;
throw std::runtime_error("could not rename fragment file " + file + ": " + strerror(eno));
}
file = to;
}
template<typename Key, typename Fn>
std::pair<bool, Data> getByKeyProjection(const Key& key, Fn fn)
{
if(size() == 0)
return std::make_pair(false, Data() );
size_t first = 0;
size_t last = size() - 1;
while(first != last)
{
size_t midIdx = first + (last - first) / 2;
bufferFileRange(midIdx, midIdx + 1);
Data& mid = (*this)[midIdx];
if (fn(mid.pkey() ) < key)
first = midIdx + 1;
else
last = midIdx;
}
if(fn( (*this)[first].pkey() ) == key)
return std::make_pair(true, (*this)[first]);
else
return std::make_pair(false, Data() );
}
std::pair<bool, Data> getByKey(const typename Data::KeyType& key)
{
struct ops
{
static typename Data::KeyType key(const typename Data::KeyType& key) { return key; }
};
return getByKeyProjection(key, ops::key);
}
};
#endif

View File

@@ -0,0 +1,47 @@
#ifndef SETFRAGMENTCURSOR_H_
#define SETFRAGMENTCURSOR_H_
#include <database/SetFragment.h>
template<typename Data>
class SetFragmentCursor {
public:
typedef Data ElementType;
typedef size_t MarkerType;
private:
SetFragment<Data>* fragment;
size_t currentGetIndex;
public:
explicit SetFragmentCursor(SetFragment<Data>& fragment)
: fragment(&fragment), currentGetIndex(-1)
{}
bool step()
{
if (currentGetIndex + 1 >= fragment->size() )
return false;
currentGetIndex++;
return true;
}
Data* get()
{
return &(*fragment)[currentGetIndex];
}
MarkerType mark() const
{
return currentGetIndex;
}
void restore(MarkerType mark)
{
currentGetIndex = mark - 1;
step();
}
};
#endif

View File

@@ -0,0 +1,45 @@
#ifndef STRIPETARGETS_H_
#define STRIPETARGETS_H_
#include <database/EntryID.h>
#include <inttypes.h>
namespace db {
struct StripeTargets
{
EntryID id; /* 12 */
enum {
NTARGETS = 7
};
uint16_t targets[NTARGETS]; /* 26 */
uint16_t firstTargetIndex; /* 28 */
typedef EntryID KeyType;
EntryID pkey() const { return id; }
friend std::ostream& operator<<(std::ostream& os, StripeTargets const& obj)
{
os << "-------------" << "\n";
os << "StripeTargets" << "\n";
os << "-------------" << "\n";
os << "EntryID: " << obj.id.str() << "\n";
os << "Stripe targets: [ ";
for (int i=0; i<obj.NTARGETS; i++)
{
os << obj.targets[i] << " ";
}
os << "]\n";
os << "FirstTargetsIndex: " << obj.firstTargetIndex << "\n";
return os;
}
};
}
#endif

View File

@@ -0,0 +1,430 @@
#ifndef TABLE_H_
#define TABLE_H_
#include <database/Buffer.h>
#include <database/Filter.h>
#include <database/LeftJoinEq.h>
#include <database/Select.h>
#include <database/Set.h>
#include <boost/make_shared.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/ref.hpp>
#include <boost/weak_ptr.hpp>
template<typename Data, bool AllowDuplicateKeys = false>
class Table {
private:
struct Key
{
typedef typename Data::KeyType KeyType;
KeyType value;
Key() {}
Key(const KeyType& key) : value(key) {}
KeyType pkey() const { return value; }
bool operator<(const Key& r) const { return value < r.value; }
bool operator==(const Key& r) const { return value == r.value; }
};
struct GetKey
{
typedef typename Data::KeyType result_type;
result_type operator()(const Data& data) const { return data.pkey(); }
result_type operator()(const Key& data) const { return data.value; }
};
struct SecondIsNull
{
typedef bool result_type;
bool operator()(std::pair<Data, Key*>& p) const { return p.second == NULL; }
};
struct First
{
typedef Data result_type;
Data operator()(std::pair<Data, Key*>& p) const { return p.first; }
};
struct HasMatchingKey
{
HasMatchingKey(const typename Data::KeyType& k) : key(k) {}
typename Data::KeyType key;
bool operator()(std::pair<Data, Key*>& p) const
{
return (p.first.pkey() == key);
}
};
private:
std::string basename;
size_t fragmentSize;
Set<Data> base;
Set<Data> inserts;
Set<Key> deletes;
typename Data::KeyType lastChangeKey;
Set<Data> insertsPending;
Set<Key> deletesPending;
boost::scoped_ptr<Buffer<Data> > insertBuffer;
boost::scoped_ptr<Buffer<Key> > deleteBuffer;
enum {
ms_none,
ms_ordered_only_insert,
ms_ordered_only_delete,
ms_ordered_at_insert,
ms_ordered_at_delete,
ms_unordered_insert,
ms_unordered_delete,
ms_bulk_insert,
} modificationState;
bool dropped;
bool insertsIntoBase;
std::vector<boost::weak_ptr<Buffer<Data> > > bulkInserters;
void assertNoBulkInsert()
{
for(size_t i = 0; i < bulkInserters.size(); i++)
{
if(!bulkInserters[i].expired() )
throw std::runtime_error("bulk insert still running");
}
bulkInserters.clear();
}
void collapseChanges()
{
deleteBuffer.reset();
insertBuffer.reset();
insertsIntoBase = false;
if(deletesPending.size() )
{
Set<Data> insertsTemp(basename + ".it");
LeftJoinEq<
SetFragmentCursor<Data>,
SetFragmentCursor<Key>,
GetKey>
cleanedInserts = db::leftJoinBy(
GetKey(),
inserts.cursor(),
deletesPending.cursor() );
// sequence will always be sorted, no need to fragment and sort again
SetFragment<Data>* fragment = insertsTemp.newFragment();
while(cleanedInserts.step() )
{
if (cleanedInserts.get()->second == nullptr)
fragment->append(cleanedInserts.get()->first);
}
inserts.clear();
insertsTemp.mergeInto(inserts);
insertsTemp.drop();
deletesPending.mergeInto(deletes);
deletes.makeUnique();
}
if(insertsPending.size() )
insertsPending.mergeInto(inserts);
modificationState = ms_none;
}
void setupBaseBuffer()
{
insertBuffer.reset(new Buffer<Data>(base, fragmentSize));
insertsIntoBase = true;
}
public:
Table(const std::string& basename, size_t fragmentSize, bool allowCreate = true)
: basename(basename), fragmentSize(fragmentSize),
base(basename + ".b", allowCreate),
inserts(basename + ".i", allowCreate),
deletes(basename + ".d", allowCreate),
insertsPending(basename + ".ip", true),
deletesPending(basename + ".dp", true),
modificationState(ms_none),
dropped(false), insertsIntoBase(false)
{
if (base.size() == 0)
setupBaseBuffer();
}
~Table()
{
if(dropped)
return;
collapseChanges();
deletesPending.drop();
insertsPending.drop();
}
boost::shared_ptr<Buffer<Data> > bulkInsert()
{
if(inserts.size() || deletes.size() || insertsPending.size() || deletesPending.size() )
throw std::runtime_error("unordered operation");
switch(modificationState)
{
case ms_none:
case ms_ordered_only_insert:
case ms_bulk_insert:
break;
default:
throw std::runtime_error("unordered operation");
}
modificationState = ms_bulk_insert;
boost::shared_ptr<Buffer<Data> > buffer = boost::make_shared<Buffer<Data> >(
boost::ref(base), fragmentSize);
bulkInserters.push_back(buffer);
return buffer;
}
void insert(const Data& data)
{
using namespace std::rel_ops;
switch(modificationState)
{
case ms_bulk_insert:
assertNoBulkInsert();
BEEGFS_FALLTHROUGH;
case ms_none:
modificationState = ms_ordered_only_insert;
break;
case ms_ordered_only_insert:
if(lastChangeKey >= data.pkey() )
modificationState = ms_unordered_insert;
break;
case ms_unordered_delete:
throw std::runtime_error("unordered operation");
case ms_unordered_insert:
break;
case ms_ordered_at_delete:
case ms_ordered_only_delete:
if(lastChangeKey <= data.pkey() )
modificationState = ms_ordered_at_insert;
else
throw std::runtime_error("unordered operation");
break;
case ms_ordered_at_insert:
if(lastChangeKey < data.pkey()
|| (!AllowDuplicateKeys && lastChangeKey == data.pkey() ) )
throw std::runtime_error("unordered operation");
break;
}
lastChangeKey = data.pkey();
if(!insertBuffer)
insertBuffer.reset(new Buffer<Data>(insertsPending, fragmentSize) );
insertBuffer->append(data);
}
void remove(const typename Data::KeyType& key)
{
using namespace std::rel_ops;
if(modificationState == ms_bulk_insert)
assertNoBulkInsert();
// if deletes happen during initial insert, complete the base set. delete tracking
// cannot cope otherwise
if(insertsIntoBase)
{
insertBuffer.reset();
insertsIntoBase = false;
modificationState = ms_none;
}
switch(modificationState)
{
case ms_none:
case ms_bulk_insert:
modificationState = ms_ordered_only_delete;
break;
case ms_ordered_only_delete:
if(lastChangeKey >= key)
modificationState = ms_unordered_delete;
break;
case ms_unordered_delete:
break;
case ms_unordered_insert:
throw std::runtime_error("unordered operation");
case ms_ordered_at_delete:
case ms_ordered_at_insert:
case ms_ordered_only_insert:
if(key <= lastChangeKey)
throw std::runtime_error("unordered operation");
modificationState = ms_ordered_at_delete;
break;
}
lastChangeKey = key;
if(!deleteBuffer)
deleteBuffer.reset(new Buffer<Key>(deletesPending, fragmentSize) );
deleteBuffer->append(key);
}
void commitChanges()
{
collapseChanges();
base.sort();
inserts.sort();
deletes.sort();
}
typedef Union<
Select<
Filter<
LeftJoinEq<
SetFragmentCursor<Data>,
SetFragmentCursor<Key>,
GetKey>,
SecondIsNull>,
First>,
SetFragmentCursor<Data>,
GetKey> QueryType;
QueryType cursor()
{
return db::unionBy(
GetKey(),
db::leftJoinBy(
GetKey(),
base.cursor(),
deletes.cursor() )
| db::where(SecondIsNull() )
| db::select(First() ),
inserts.cursor() );
}
typedef Union<
Select<
Filter<
Filter<
LeftJoinEq<
SetFragmentCursor<Data>,
SetFragmentCursor<Key>,
GetKey>,
SecondIsNull>,
HasMatchingKey>,
First>,
SetFragmentCursor<Data>,
GetKey> MultiRowResultType;
MultiRowResultType getAllByKey(const typename Data::KeyType& key)
{
return db::unionBy(
GetKey(),
db::leftJoinBy(
GetKey(),
base.cursor(),
deletes.cursor() )
| db::where(SecondIsNull() )
| db::where(HasMatchingKey(key) )
| db::select(First() ),
inserts.cursor() );
}
template<typename Key, typename Fn>
std::pair<bool, Data> getByKeyProjection(const Key& key, Fn fn)
{
Data dummy = {};
std::pair<bool, Data> insIdx = inserts.getByKeyProjection(key, fn);
if(insIdx.first)
return insIdx;
std::pair<bool, Data> baseIdx = base.getByKeyProjection(key, fn);
if(!baseIdx.first)
return std::make_pair(false, dummy);
std::pair<bool, typename Table::Key> delIdx = deletes.getByKey(baseIdx.second.pkey() );
if(delIdx.first)
return std::make_pair(false, dummy);
return baseIdx;
}
std::pair<bool, Data> getByKey(const typename Data::KeyType& key)
{
struct ops
{
static typename Data::KeyType key(const typename Data::KeyType& key) { return key; }
};
return getByKeyProjection(key, ops::key);
}
void drop()
{
insertBuffer.reset();
deleteBuffer.reset();
base.drop();
inserts.drop();
deletes.drop();
insertsPending.drop();
deletesPending.drop();
dropped = true;
}
void clear()
{
insertBuffer.reset();
deleteBuffer.reset();
base.clear();
inserts.clear();
deletes.clear();
insertsPending.clear();
deletesPending.clear();
setupBaseBuffer();
}
};
#endif

View File

@@ -0,0 +1,130 @@
#ifndef UNION_H_
#define UNION_H_
#include <utility>
template<typename Left, typename Right, typename KeyExtract>
class Union
{
public:
typedef typename Left::ElementType ElementType;
struct MarkerType
{
typename Left::MarkerType leftMark;
typename Right::MarkerType rightMark;
bool leftEnded;
bool rightEnded;
bool nextStepLeft;
};
public:
Union(Left left, Right right, KeyExtract keyExtract):
left(left), right(right), keyExtract(keyExtract), leftEnded(false), nextStepLeft(true),
currentAtLeft(false)
{
this->rightEnded = !this->right.step();
}
bool step()
{
if(this->leftEnded && this->rightEnded)
return false;
if(this->leftEnded)
{
this->rightEnded = !this->right.step();
return resetCurrent();
}
if(this->rightEnded)
{
this->leftEnded = !this->left.step();
return resetCurrent();
}
if(this->nextStepLeft)
this->leftEnded = !this->left.step();
else
this->rightEnded = !this->right.step();
return resetCurrent();
}
ElementType* get()
{
if(this->currentAtLeft)
return this->left.get();
else
return this->right.get();
}
MarkerType mark() const
{
MarkerType result = { this->left.mark(), this->right.mark(), this->leftEnded,
this->rightEnded, this->nextStepLeft };
return result;
}
void restore(MarkerType mark)
{
this->leftEnded = mark.leftEnded;
this->rightEnded = mark.rightEnded;
this->nextStepLeft = mark.nextStepLeft;
if(!this->leftEnded)
this->left.restore(mark.leftMark);
if(!this->rightEnded)
this->right.restore(mark.rightMark);
resetCurrent();
}
private:
bool resetCurrent()
{
if(this->leftEnded && this->rightEnded)
return false;
if(this->leftEnded)
this->currentAtLeft = false;
else
if(this->rightEnded)
this->currentAtLeft = true;
else
if(this->keyExtract(*this->left.get() ) < this->keyExtract(*this->right.get() ) )
{
this->currentAtLeft = true;
this->nextStepLeft = true;
}
else
{
this->currentAtLeft = false;
this->nextStepLeft = false;
}
return true;
}
private:
Left left;
Right right;
KeyExtract keyExtract;
bool leftEnded;
bool rightEnded;
bool nextStepLeft;
bool currentAtLeft;
};
namespace db {
template<typename KeyExtract, typename Left, typename Right>
inline Union<Left, Right, KeyExtract> unionBy(KeyExtract keyExtract, Left left, Right right)
{
return Union<Left, Right, KeyExtract>(left, right, keyExtract);
}
}
#endif

View File

@@ -0,0 +1,25 @@
#ifndef USEDTARGET_H_
#define USEDTARGET_H_
#include <common/fsck/FsckTargetID.h>
namespace db {
struct UsedTarget
{
uint32_t id;
uint32_t type;
typedef std::pair<uint32_t, uint32_t> KeyType;
KeyType pkey() const { return std::make_pair(id, type); }
operator FsckTargetID() const
{
return FsckTargetID(id, FsckTargetIDType(type) );
}
};
}
#endif

View File

@@ -0,0 +1,52 @@
#ifndef VECTORSOURCE_H_
#define VECTORSOURCE_H_
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
template<typename Obj>
class VectorSource
{
public:
typedef Obj ElementType;
typedef typename std::vector<Obj>::const_iterator MarkerType;
public:
explicit VectorSource(std::vector<Obj>& data)
: content(boost::make_shared<std::vector<Obj> >() ), index(-1)
{
data.swap(*content);
}
bool step()
{
if(this->index == this->content->size() - 1)
return false;
this->index += 1;
return true;
}
ElementType* get()
{
return &(*this->content)[this->index];
}
MarkerType mark() const
{
return this->content->begin() + this->index;
}
void restore(MarkerType mark)
{
this->index = mark - this->content->begin();
}
private:
boost::shared_ptr<std::vector<Obj> > content;
size_t index;
};
#endif

View File

@@ -0,0 +1,23 @@
#include "Mode.h"
/**
* Check given config for a remaining arg and print it as invalid.
*
* @return true if a remaining invalid argument was found in the given config.
*/
bool Mode::checkInvalidArgs(const StringMap* cfg)
{
if(cfg->empty() )
return false;
if(cfg->size() == 1)
std::cerr << "Invalid argument: " << cfg->begin()->first << std::endl;
else
{ // multiple invalid args
std::cerr << "Found " << cfg->size() << " invalid arguments. One of them is: " <<
cfg->begin()->first << std::endl;
}
return true;
}

24
fsck/source/modes/Mode.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef MODE_H_
#define MODE_H_
#include <common/nodes/Node.h>
#include <common/Common.h>
class Mode
{
public:
virtual ~Mode() {}
/**
* Executes the mode inside the calling thread.
*
* @return APPCODE_...
*/
virtual int execute() = 0;
protected:
Mode() {}
bool checkInvalidArgs(const StringMap* cfg);
};
#endif /*MODE_H_*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,170 @@
#ifndef MODECHECKFS_H
#define MODECHECKFS_H
#include <boost/scoped_ptr.hpp>
#include <vector>
#include <database/FsckDB.h>
#include <database/FsckDBException.h>
#include <modes/Mode.h>
class UserPrompter
{
public:
template<unsigned Actions>
UserPrompter(const FsckRepairAction (&possibleActions)[Actions],
FsckRepairAction defaultRepairAction);
FsckRepairAction chooseAction(const std::string& prompt);
private:
bool askForAction;
std::vector<FsckRepairAction> possibleActions;
FsckRepairAction repairAction;
};
struct RepairChunkState
{
UserPrompter* prompt;
std::string lastID;
FsckRepairAction lastChunkAction;
};
struct FsckErrCount
{
FsckErrCount() : unfixableErrors(0), fixableErrors(0)
{
}
FsckErrCount operator+(const FsckErrCount& other)
{
unfixableErrors += other.unfixableErrors;
fixableErrors += other.fixableErrors;
return *this;
}
void operator+=(const FsckErrCount& other)
{
*this = *this + other;
}
uint64_t getTotalErrors() const
{
return unfixableErrors + fixableErrors;
}
uint64_t unfixableErrors;
uint64_t fixableErrors;
};
class ModeCheckFS : public Mode
{
public:
ModeCheckFS();
virtual ~ModeCheckFS();
static void printHelp();
virtual int execute();
private:
boost::scoped_ptr<FsckDB> database;
LogContext log;
NodeHandle lostAndFoundNode;
EntryInfo lostAndFoundInfo;
boost::shared_ptr<FsckDirInode> lostAndFoundInode;
std::set<NumNodeID> secondariesSetBad;
int initDatabase();
void printHeaderInformation();
void disposeUnusedFiles();
FhgfsOpsErr gatherData(bool forceRestart);
template<typename Obj, typename State>
FsckErrCount checkAndRepairGeneric(Cursor<Obj> cursor,
void (ModeCheckFS::*repair)(Obj&, FsckErrCount&, State&), State& state);
FsckErrCount checkAndRepairDanglingDentry();
FsckErrCount checkAndRepairWrongInodeOwner();
FsckErrCount checkAndRepairWrongOwnerInDentry();
FsckErrCount checkAndRepairOrphanedContDir();
FsckErrCount checkAndRepairOrphanedDirInode();
FsckErrCount checkAndRepairOrphanedFileInode();
FsckErrCount checkAndRepairDuplicateInodes();
FsckErrCount checkAndRepairOrphanedChunk();
FsckErrCount checkAndRepairMissingContDir();
FsckErrCount checkAndRepairWrongFileAttribs();
FsckErrCount checkAndRepairWrongDirAttribs();
FsckErrCount checkAndRepairFilesWithMissingTargets();
FsckErrCount checkAndRepairDirEntriesWithBrokeByIDFile();
FsckErrCount checkAndRepairOrphanedDentryByIDFiles();
FsckErrCount checkAndRepairChunksWithWrongPermissions();
FsckErrCount checkMissingMirrorChunks();
FsckErrCount checkMissingPrimaryChunks();
FsckErrCount checkDifferingChunkAttribs();
FsckErrCount checkAndRepairChunksInWrongPath();
FsckErrCount checkAndUpdateOldStyledHardlinks();
void logDuplicateInodeID(checks::DuplicatedInode& dups, int&);
FsckErrCount checkDuplicateChunks();
void logDuplicateChunk(std::list<FsckChunk>& dups, FsckErrCount& errCount, int&);
FsckErrCount checkDuplicateContDirs();
void logDuplicateContDir(std::list<db::ContDir>& dups, FsckErrCount& errCount, int&);
FsckErrCount checkMismirroredDentries();
void logMismirroredDentry(db::DirEntry& entry, FsckErrCount& errCount, int&);
FsckErrCount checkMismirroredDirectories();
void logMismirroredDirectory(db::DirInode& dir, FsckErrCount& errCount, int&);
FsckErrCount checkMismirroredFiles();
void logMismirroredFile(db::FileInode& file, FsckErrCount& errCount, int&);
FsckErrCount checkAndRepairMalformedChunk();
void repairMalformedChunk(FsckChunk& chunk, FsckErrCount& errCount, UserPrompter& prompt);
void checkAndRepair();
void repairDanglingDirEntry(db::DirEntry& entry, FsckErrCount& errCount,
std::pair<UserPrompter*, UserPrompter*>& prompt);
void repairWrongInodeOwner(FsckDirInode& inode, FsckErrCount& errCount,
UserPrompter& prompt);
void repairWrongInodeOwnerInDentry(std::pair<db::DirEntry, NumNodeID>& error,
FsckErrCount& errCount, UserPrompter& prompt);
void repairOrphanedDirInode(FsckDirInode& inode, FsckErrCount& errCount,
UserPrompter& prompt);
void repairOrphanedFileInode(FsckFileInode& inode, FsckErrCount& errCount,
UserPrompter& prompt);
void repairDuplicateInode(checks::DuplicatedInode& dupInode, FsckErrCount& errCount,
UserPrompter& prompt);
void repairOrphanedChunk(FsckChunk& chunk, FsckErrCount& errCount, RepairChunkState& state);
void repairMissingContDir(FsckDirInode& inode, FsckErrCount& errCount, UserPrompter& prompt);
void repairOrphanedContDir(FsckContDir& dir, FsckErrCount& errCount, UserPrompter& prompt);
void repairWrongFileAttribs(std::pair<FsckFileInode, checks::OptionalInodeAttribs>& error,
FsckErrCount& errCount, UserPrompter& prompt);
void repairWrongDirAttribs(std::pair<FsckDirInode, checks::InodeAttribs>& error,
FsckErrCount& errCount, UserPrompter& prompt);
void repairFileWithMissingTargets(db::DirEntry& entry, FsckErrCount& errCount,
UserPrompter& prompt);
void repairDirEntryWithBrokenByIDFile(db::DirEntry& entry, FsckErrCount& errCount,
UserPrompter& prompt);
void repairOrphanedDentryByIDFile(FsckFsID& id, FsckErrCount& errCount, UserPrompter& prompt);
void repairChunkWithWrongPermissions(std::pair<FsckChunk, FsckFileInode>& error,
FsckErrCount& errCount, UserPrompter& prompt);
void repairWrongChunkPath(std::pair<FsckChunk, FsckFileInode>& error, FsckErrCount& errCount,
UserPrompter& prompt);
void updateOldStyledHardlinks(db::FileInode& inode, FsckErrCount& errCount, UserPrompter& prompt);
void deleteFsIDsFromDB(FsckDirEntryList& dentries);
void deleteFilesFromDB(FsckDirEntryList& dentries);
bool ensureLostAndFoundExists();
void releaseLostAndFound();
};
#endif /* MODECHECKFS_H */

View File

@@ -0,0 +1,116 @@
#include "ModeEnableQuota.h"
#include <common/toolkit/ListTk.h>
#include <common/toolkit/UnitTk.h>
#include <components/worker/AdjustChunkPermissionsWork.h>
#include <net/msghelpers/MsgHelperRepair.h>
#include <toolkit/FsckTkEx.h>
#include <program/Program.h>
ModeEnableQuota::ModeEnableQuota()
: log("ModeEnableQuota")
{
}
void ModeEnableQuota::printHelp()
{
std::cout << "MODE ARGUMENTS:" << std::endl;
std::cout << " Optional:" << std::endl;
std::cout << " --databasePath=<path> Path to store the database files." << std::endl;
std::cout << " (Default: " << CONFIG_DEFAULT_DBPATH << ")" << std::endl;
std::cout << " --overwriteDbFile Overwrite an existing database file without prompt." << std::endl;
std::cout << " --logOutFile=<path> Path to the fsck output file, which contains a copy of" << std::endl;
std::cout << " the console output." << std::endl;
std::cout << " (Default: " << CONFIG_DEFAULT_OUTFILE << ")" << std::endl;
std::cout << " --logStdFile=<path> Path to the program error log file, which contains e.g." << std::endl;
std::cout << " network error messages." << std::endl;
std::cout << " (Default: " << CONFIG_DEFAULT_LOGFILE << ")" << std::endl;
std::cout << std::endl;
std::cout << "USAGE:" << std::endl;
std::cout << " This mode sets quota information on an existing file system." << std::endl;
std::cout << std::endl;
std::cout << " This is useful when quota support is being enabled on a file system instance" << std::endl;
std::cout << " that was previously used without quota support." << std::endl;
std::cout << std::endl;
std::cout << " Example: Set quota information" << std::endl;
std::cout << " $ beegfs-fsck --enablequota" << std::endl;
}
int ModeEnableQuota::execute()
{
App* app = Program::getApp();
Config *cfg = app->getConfig();
if(this->checkInvalidArgs(cfg->getUnknownConfigArgs()))
return APPCODE_INVALID_CONFIG;
FsckTkEx::printVersionHeader(false);
printHeaderInformation();
if ( !FsckTkEx::checkReachability() )
return APPCODE_COMMUNICATION_ERROR;
fixPermissions();
return APPCODE_NO_ERROR;
}
void ModeEnableQuota::printHeaderInformation()
{
Config* cfg = Program::getApp()->getConfig();
// get the current time and create some nice output, so the user can see at which time the run
// was started (especially nice to find older runs in the log file)
time_t t;
time(&t);
std::string timeStr = std::string(ctime(&t));
FsckTkEx::fsckOutput(
"Started BeeGFS fsck in enableQuota mode [" + timeStr.substr(0, timeStr.length() - 1)
+ "]\nLog will be written to " + cfg->getLogStdFile()
+ "\nDatabase files will be saved in " + cfg->getDatabasePath(),
OutputOptions_LINEBREAK | OutputOptions_HEADLINE);
}
void ModeEnableQuota::fixPermissions()
{
SynchronizedCounter finishedWork;
unsigned generatedWork = 0;
AtomicUInt64 fileCount;
AtomicUInt64 errorCount;
NodeStore* metaNodes = Program::getApp()->getMetaNodes();
auto nodes = metaNodes->referenceAllNodes();
for (auto iter = nodes.begin(); iter != nodes.end(); iter++)
{
generatedWork++;
Program::getApp()->getWorkQueue()->addIndirectWork(new AdjustChunkPermissionsWork(**iter,
&finishedWork, &fileCount, &errorCount));
}
FsckTkEx::fsckOutput("Processed 0 entries", OutputOptions_ADDLINEBREAKBEFORE |
OutputOptions_LINEDELETE | OutputOptions_NOLOG);
// wait for all packages to finish, because we cannot proceed if not all data was fetched
// BUT : update output each OUTPUT_INTERVAL_MS ms
while (! finishedWork.timedWaitForCount(generatedWork, MODEENABLEQUOTA_OUTPUT_INTERVAL_MS) )
{
FsckTkEx::fsckOutput("Processed " + StringTk::uint64ToStr(fileCount.read()) + " entries",
OutputOptions_LINEDELETE | OutputOptions_NOLOG);
}
FsckTkEx::fsckOutput("Processed " + StringTk::uint64ToStr(fileCount.read()) + " entries",
OutputOptions_LINEDELETE | OutputOptions_NOLOG | OutputOptions_LINEBREAK);
if ( errorCount.read() > 0 )
{
FsckTkEx::fsckOutput("", OutputOptions_LINEBREAK);
FsckTkEx::fsckOutput(
StringTk::uint64ToStr(errorCount.read())
+ " errors occurred. Please see Metadata server logs for more details.",
OutputOptions_ADDLINEBREAKBEFORE | OutputOptions_LINEBREAK);
}
}

View File

@@ -0,0 +1,27 @@
#ifndef MODEENABLEQUOTA_H
#define MODEENABLEQUOTA_H
#include <app/config/Config.h>
#include <database/FsckDB.h>
#include <modes/Mode.h>
#define MODEENABLEQUOTA_OUTPUT_INTERVAL_MS 5000
class ModeEnableQuota : public Mode
{
public:
ModeEnableQuota();
static void printHelp();
virtual int execute();
private:
LogContext log;
void printHeaderInformation();
void fixPermissions();
};
#endif /* MODEENABLEQUOTA_H */

View File

@@ -0,0 +1,99 @@
#include "ModeHelp.h"
#include <program/Program.h>
#include <modes/ModeCheckFS.h>
#include <modes/ModeEnableQuota.h>
int ModeHelp::execute()
{
App* app = Program::getApp();
Config* cfg = app->getConfig();
RunMode runMode = cfg->determineRunMode();
if(runMode == RunMode_INVALID)
printGeneralHelp();
else
printSpecificHelp(runMode);
std::cout << std::endl;
return APPCODE_NO_ERROR;
}
void ModeHelp::printGeneralHelp()
{
std::cout << "BeeGFS File System Check (http://www.beegfs.com)" << std::endl;
std::cout << "Version: " << BEEGFS_VERSION << std::endl;
std::cout << std::endl;
std::cout << "GENERAL USAGE:" << std::endl;
std::cout << " $ beegfs-fsck --<modename> --help" << std::endl;
std::cout << " $ beegfs-fsck --<modename> [mode_arguments] [client_arguments]" << std::endl;
std::cout << std::endl;
std::cout << "MODES:" << std::endl;
std::cout << " --checkfs => Perform a full check and optional repair of " << std::endl;
std::cout << " a BeeGFS instance." << std::endl;
std::cout << " --enablequota => Set attributes needed for quota support in FhGFS." << std::endl;
std::cout << " Can be used to enable quota support on an existing" << std::endl;
std::cout << " system." << std::endl;
std::cout << std::endl;
std::cout << "USAGE:" << std::endl;
std::cout << " This is the BeeGFS file system consistency check and repair tool." << std::endl;
std::cout << std::endl;
std::cout << " Choose a mode from the list above and use the parameter \"--help\"" << std::endl;
std::cout << " to show arguments and usage examples for that particular mode." << std::endl;
std::cout << std::endl;
std::cout << " (Running beegfs-fsck requires root privileges.)" << std::endl;
std::cout << std::endl;
std::cout << " Example: Show help for mode \"--checkfs\"" << std::endl;
std::cout << " $ beegfs-fsck --checkfs --help" << std::endl;
}
/**
* Note: don't call this with RunMode_INVALID.
*/
void ModeHelp::printSpecificHelp(RunMode runMode)
{
printSpecificHelpHeader(); // print general usage and client options info
switch(runMode)
{
case RunMode_CHECKFS:
{
ModeCheckFS::printHelp();
} break;
case RunMode_ENABLEQUOTA:
{
ModeEnableQuota::printHelp();
} break;
default:
{
std::cerr << "Error: Unhandled mode specified. Mode number: " << runMode << std::endl;
std::cout << std::endl;
printGeneralHelp();
} break;
}
}
/**
* Print the help message header that applies to any specific mode help. Contains general usage
* and client options info.
*/
void ModeHelp::printSpecificHelpHeader()
{
std::cout << "GENERAL USAGE:" << std::endl;
std::cout << " $ beegfs-fsck --<modename> [mode_arguments] [client_arguments]" << std::endl;
std::cout << std::endl;
std::cout << "CLIENT ARGUMENTS:" << std::endl;
std::cout << " --cfgFile=<path> Path to BeeGFS client config file." << std::endl;
std::cout << " (Default: " CONFIG_DEFAULT_CFGFILENAME ")" << std::endl;
std::cout << " --<key>=<value> Any setting from the client config file to override" << std::endl;
std::cout << " the config file values (e.g. \"--logLevel=5\")." << std::endl;
std::cout << std::endl;
}

View File

@@ -0,0 +1,24 @@
#ifndef MODEHELP_H_
#define MODEHELP_H_
#include <app/config/Config.h>
#include <common/Common.h>
#include "Mode.h"
class ModeHelp : public Mode
{
public:
ModeHelp() {};
virtual int execute();
private:
void printGeneralHelp();
void printSpecificHelp(RunMode runMode);
void printSpecificHelpHeader();
};
#endif /*MODEHELP_H_*/

View File

@@ -0,0 +1,125 @@
// control messages
#include <common/net/message/control/GenericResponseMsg.h>
// fsck messages
#include <common/net/message/fsck/AdjustChunkPermissionsRespMsg.h>
#include <common/net/message/fsck/CreateDefDirInodesRespMsg.h>
#include <common/net/message/fsck/CreateEmptyContDirsRespMsg.h>
#include <common/net/message/fsck/DeleteChunksRespMsg.h>
#include <common/net/message/fsck/DeleteDirEntriesRespMsg.h>
#include <common/net/message/fsck/FetchFsckChunkListRespMsg.h>
#include <common/net/message/fsck/FsckSetEventLoggingRespMsg.h>
#include <common/net/message/fsck/FixInodeOwnersInDentryRespMsg.h>
#include <common/net/message/fsck/FixInodeOwnersRespMsg.h>
#include <common/net/message/fsck/FsckSetEventLoggingRespMsg.h>
#include <common/net/message/fsck/LinkToLostAndFoundRespMsg.h>
#include <common/net/message/fsck/MoveChunkFileRespMsg.h>
#include <common/net/message/fsck/RecreateDentriesRespMsg.h>
#include <common/net/message/fsck/RecreateFsIDsRespMsg.h>
#include <common/net/message/fsck/RemoveInodesRespMsg.h>
#include <common/net/message/fsck/RetrieveDirEntriesRespMsg.h>
#include <common/net/message/fsck/RetrieveFsIDsRespMsg.h>
#include <common/net/message/fsck/RetrieveInodesRespMsg.h>
#include <common/net/message/fsck/UpdateDirAttribsRespMsg.h>
#include <common/net/message/fsck/UpdateFileAttribsRespMsg.h>
#include <net/message/fsck/FsckModificationEventMsgEx.h>
#include <common/net/message/fsck/CheckAndRepairDupInodeRespMsg.h>
// nodes messages
#include <common/net/message/nodes/GetMirrorBuddyGroupsRespMsg.h>
#include <common/net/message/nodes/GetNodesRespMsg.h>
#include <common/net/message/nodes/GetTargetMappingsRespMsg.h>
#include <common/net/message/nodes/GetTargetStatesRespMsg.h>
#include <common/net/message/nodes/SetTargetConsistencyStatesRespMsg.h>
#include <net/message/nodes/HeartbeatMsgEx.h>
// storage messages
#include <common/net/message/storage/attribs/GetEntryInfoRespMsg.h>
#include <common/net/message/storage/attribs/SetLocalAttrRespMsg.h>
#include <common/net/message/storage/attribs/StatRespMsg.h>
#include <common/net/message/storage/creating/MkDirRespMsg.h>
#include <common/net/message/storage/creating/RmDirEntryRespMsg.h>
#include <common/net/message/storage/creating/UnlinkFileRespMsg.h>
#include <common/net/message/storage/listing/ListDirFromOffsetRespMsg.h>
#include <common/net/message/storage/lookup/FindOwnerRespMsg.h>
#include <common/net/message/storage/StatStoragePathRespMsg.h>
#include <common/net/message/storage/creating/MoveFileInodeRespMsg.h>
// general includes
#include <common/net/message/SimpleMsg.h>
#include <net/message/testing/DummyMsgEx.h>
#include "NetMessageFactory.h"
/**
* @return NetMessage that must be deleted by the caller
* (msg->msgType is NETMSGTYPE_Invalid on error)
*/
std::unique_ptr<NetMessage> NetMessageFactory::createFromMsgType(unsigned short msgType) const
{
NetMessage* msg;
switch(msgType)
{
// The following lines are grouped by "type of the message" and ordered alphabetically inside
// the groups. There should always be one message per line to keep a clear layout (although
// this might lead to lines that are longer than usual)
// control messages
case NETMSGTYPE_GenericResponse: { msg = new GenericResponseMsg(); } break;
// nodes messages
case NETMSGTYPE_GetNodesResp: { msg = new GetNodesRespMsg(); } break;
case NETMSGTYPE_Heartbeat: { msg = new HeartbeatMsgEx(); } break;
case NETMSGTYPE_GetMirrorBuddyGroupsResp: { msg = new GetMirrorBuddyGroupsRespMsg(); } break;
case NETMSGTYPE_GetTargetMappingsResp: { msg = new GetTargetMappingsRespMsg(); } break;
case NETMSGTYPE_GetTargetStatesResp: { msg = new GetTargetStatesRespMsg(); } break;
case NETMSGTYPE_SetTargetConsistencyStatesResp: { msg = new SetTargetConsistencyStatesRespMsg(); } break;
// storage messages
case NETMSGTYPE_FindOwnerResp: { msg = new FindOwnerRespMsg(); } break;
case NETMSGTYPE_ListDirFromOffsetResp: { msg = new ListDirFromOffsetRespMsg(); } break;
case NETMSGTYPE_RmDirEntryResp: { msg = new RmDirEntryRespMsg(); } break;
case NETMSGTYPE_GetEntryInfoResp: { msg = new GetEntryInfoRespMsg(); } break;
case NETMSGTYPE_StatResp: { msg = new StatRespMsg(); } break;
case NETMSGTYPE_StatStoragePathResp: { msg = new StatStoragePathRespMsg(); } break;
case NETMSGTYPE_MkDirResp: { msg = new MkDirRespMsg(); } break;
case NETMSGTYPE_SetLocalAttrResp: { msg = new SetLocalAttrRespMsg(); } break;
case NETMSGTYPE_UnlinkFileResp: { msg = new UnlinkFileRespMsg(); } break;
case NETMSGTYPE_MoveFileInodeResp: {msg = new MoveFileInodeRespMsg(); } break;
//fsck
case NETMSGTYPE_RetrieveDirEntriesResp: { msg = new RetrieveDirEntriesRespMsg(); } break;
case NETMSGTYPE_RetrieveInodesResp: { msg = new RetrieveInodesRespMsg(); } break;
case NETMSGTYPE_RetrieveFsIDsResp: { msg = new RetrieveFsIDsRespMsg(); } break;
case NETMSGTYPE_DeleteDirEntriesResp: { msg = new DeleteDirEntriesRespMsg(); } break;
case NETMSGTYPE_CreateDefDirInodesResp: { msg = new CreateDefDirInodesRespMsg(); } break;
case NETMSGTYPE_FixInodeOwnersResp: { msg = new FixInodeOwnersRespMsg(); } break;
case NETMSGTYPE_FixInodeOwnersInDentryResp: { msg = new FixInodeOwnersInDentryRespMsg(); } break;
case NETMSGTYPE_LinkToLostAndFoundResp : { msg = new LinkToLostAndFoundRespMsg(); } break;
case NETMSGTYPE_DeleteChunksResp: { msg = new DeleteChunksRespMsg(); } break;
case NETMSGTYPE_CreateEmptyContDirsResp: { msg = new CreateEmptyContDirsRespMsg(); } break;
case NETMSGTYPE_UpdateFileAttribsResp: { msg = new UpdateFileAttribsRespMsg(); } break;
case NETMSGTYPE_UpdateDirAttribsResp: { msg = new UpdateDirAttribsRespMsg(); } break;
case NETMSGTYPE_RecreateFsIDsResp: { msg = new RecreateFsIDsRespMsg(); } break;
case NETMSGTYPE_RecreateDentriesResp: { msg = new RecreateDentriesRespMsg(); } break;
case NETMSGTYPE_FsckSetEventLoggingResp: { msg = new FsckSetEventLoggingRespMsg(); } break;
case NETMSGTYPE_FsckModificationEvent: { msg = new FsckModificationEventMsgEx(); } break;
case NETMSGTYPE_AdjustChunkPermissionsResp: { msg = new AdjustChunkPermissionsRespMsg(); } break;
case NETMSGTYPE_FetchFsckChunkListResp: { msg = new FetchFsckChunkListRespMsg(); } break;
case NETMSGTYPE_MoveChunkFileResp: { msg = new MoveChunkFileRespMsg(); } break;
case NETMSGTYPE_RemoveInodesResp: { msg = new RemoveInodesRespMsg(); } break;
case NETMSGTYPE_CheckAndRepairDupInodeResp: { msg = new CheckAndRepairDupInodeRespMsg(); } break;
//testing
case NETMSGTYPE_Dummy: { msg = new DummyMsgEx(); } break;
default:
{
msg = new SimpleMsg(NETMSGTYPE_Invalid);
} break;
}
return std::unique_ptr<NetMessage>(msg);
}

View File

@@ -0,0 +1,16 @@
#ifndef NETMESSAGEFACTORY_H_
#define NETMESSAGEFACTORY_H_
#include <common/Common.h>
#include <common/net/message/AbstractNetMessageFactory.h>
class NetMessageFactory : public AbstractNetMessageFactory
{
public:
NetMessageFactory() {}
protected:
virtual std::unique_ptr<NetMessage> createFromMsgType(unsigned short msgType) const override;
} ;
#endif /*NETMESSAGEFACTORY_H_*/

View File

@@ -0,0 +1,26 @@
#include <program/Program.h>
#include "FsckModificationEventMsgEx.h"
bool FsckModificationEventMsgEx::processIncoming(ResponseContext& ctx)
{
LogContext log("FsckModificationEventMsg incoming");
App* app = Program::getApp();
ModificationEventHandler *eventHandler = app->getModificationEventHandler();
StringList& entryIDList = this->getEntryIDList();
bool addRes = eventHandler->add(getModificationEventTypeList(), entryIDList);
if (!addRes)
{
log.logErr("Unable to add modification event to database");
return false;
}
bool ackRes = acknowledge(ctx);
if (!ackRes)
log.logErr("Unable to send ack to metadata server");
return true;
}

View File

@@ -0,0 +1,12 @@
#ifndef FSCKMODIFICATIONEVENTMSGEX_H
#define FSCKMODIFICATIONEVENTMSGEX_H
#include <common/net/message/fsck/FsckModificationEventMsg.h>
class FsckModificationEventMsgEx : public FsckModificationEventMsg
{
public:
virtual bool processIncoming(ResponseContext& ctx);
};
#endif /*FSCKMODIFICATIONEVENTMSGEX_H*/

View File

@@ -0,0 +1,102 @@
#include <common/net/sock/NetworkInterfaceCard.h>
#include <program/Program.h>
#include "HeartbeatMsgEx.h"
#include <boost/lexical_cast.hpp>
bool HeartbeatMsgEx::processIncoming(ResponseContext& ctx)
{
LogContext log("Heartbeat incoming");
App* app = Program::getApp();
bool isNodeNew;
// construct node
NicAddressList& nicList = getNicList();
auto node = std::make_shared<Node>(getNodeType(), getNodeID(), getNodeNumID(), getPortUDP(),
getPortTCP(), nicList);
// set local nic capabilities
Node& localNode = app->getLocalNode();
NicAddressList localNicList(localNode.getNicList());
NicListCapabilities localNicCaps;
NetworkInterfaceCard::supportedCapabilities(&localNicList, &localNicCaps);
node->getConnPool()->setLocalNicList(localNicList, localNicCaps);
std::string nodeIDWithTypeStr = node->getNodeIDWithTypeStr();
// add/update node in store
NodeStore* nodes = NULL;
switch(getNodeType() )
{
case NODETYPE_Meta:
nodes = app->getMetaNodes(); break;
case NODETYPE_Storage:
nodes = app->getStorageNodes(); break;
case NODETYPE_Mgmt:
nodes = app->getMgmtNodes(); break;
default:
{
LOG(GENERAL, ERR, "Invalid node type.",
("Node Type", getNodeType()),
("Sender", ctx.peerName()),
("NodeID", getNodeNumID()),
("Port (UDP)", getPortUDP()),
("Port (TCP)", getPortTCP())
);
goto ack_resp;
} break;
}
isNodeNew = (nodes->addOrUpdateNode(std::move(node)) == NodeStoreResult::Added);
if(isNodeNew)
{
bool supportsRDMA = NetworkInterfaceCard::supportsRDMA(&nicList);
log.log(Log_WARNING, std::string("New node: ") +
nodeIDWithTypeStr + "; " +
std::string(supportsRDMA ? "RDMA; " : "") +
std::string("Source: ") + ctx.peerName() );
log.log(Log_DEBUG, std::string("Number of nodes in the system: ") +
StringTk::intToStr(nodes->getSize() ) +
std::string(" (Type: ") + boost::lexical_cast<std::string>(getNodeType()) + ")");
}
processIncomingRoot();
ack_resp:
acknowledge(ctx);
return true;
}
/**
* Handles the contained root information.
*/
void HeartbeatMsgEx::processIncomingRoot()
{
LogContext log("Heartbeat incoming");
// check whether root info is defined
if( (getNodeType() != NODETYPE_Meta) || !getRootNumID() )
return;
// try to apply the contained root info
if(Program::getApp()->getMetaRoot().setIfDefault(getRootNumID(), getRootIsBuddyMirrored()))
{
log.log(Log_CRITICAL, "Root (by Heartbeat): " + getRootNumID().str() );
}
}

View File

@@ -0,0 +1,15 @@
#ifndef HEARTBEATMSGEX_H_
#define HEARTBEATMSGEX_H_
#include <common/net/message/nodes/HeartbeatMsg.h>
class HeartbeatMsgEx : public HeartbeatMsg
{
public:
virtual bool processIncoming(ResponseContext& ctx);
private:
void processIncomingRoot();
};
#endif /*HEARTBEATMSGEX_H_*/

View File

@@ -0,0 +1,9 @@
#include <program/Program.h>
#include "DummyMsgEx.h"
bool DummyMsgEx::processIncoming(ResponseContext& ctx)
{
ctx.sendResponse(DummyMsg() );
return true;
}

View File

@@ -0,0 +1,20 @@
#ifndef DUMMYMSGEX_H
#define DUMMYMSGEX_H
#include <common/net/message/control/DummyMsg.h>
/*
* this is intended for testing purposes only
* just sends another DummyMsg back to the sender
*/
class DummyMsgEx : public DummyMsg
{
public:
virtual bool processIncoming(ResponseContext& ctx);
private:
void processIncomingRoot();
};
#endif /*DUMMYMSGEX_H*/

View File

@@ -0,0 +1,951 @@
#include <common/net/message/fsck/CreateDefDirInodesMsg.h>
#include <common/net/message/fsck/CreateDefDirInodesRespMsg.h>
#include <common/net/message/fsck/CreateEmptyContDirsMsg.h>
#include <common/net/message/fsck/CreateEmptyContDirsRespMsg.h>
#include <common/net/message/fsck/DeleteDirEntriesMsg.h>
#include <common/net/message/fsck/DeleteDirEntriesRespMsg.h>
#include <common/net/message/fsck/DeleteChunksMsg.h>
#include <common/net/message/fsck/DeleteChunksRespMsg.h>
#include <common/net/message/fsck/FixInodeOwnersMsg.h>
#include <common/net/message/fsck/FixInodeOwnersRespMsg.h>
#include <common/net/message/fsck/FixInodeOwnersInDentryMsg.h>
#include <common/net/message/fsck/FixInodeOwnersInDentryRespMsg.h>
#include <common/net/message/fsck/LinkToLostAndFoundMsg.h>
#include <common/net/message/fsck/LinkToLostAndFoundRespMsg.h>
#include <common/net/message/fsck/MoveChunkFileMsg.h>
#include <common/net/message/fsck/MoveChunkFileRespMsg.h>
#include <common/net/message/fsck/RecreateFsIDsMsg.h>
#include <common/net/message/fsck/RecreateFsIDsRespMsg.h>
#include <common/net/message/fsck/RecreateDentriesMsg.h>
#include <common/net/message/fsck/RecreateDentriesRespMsg.h>
#include <common/net/message/fsck/RemoveInodesMsg.h>
#include <common/net/message/fsck/RemoveInodesRespMsg.h>
#include <common/net/message/fsck/CheckAndRepairDupInodeMsg.h>
#include <common/net/message/fsck/CheckAndRepairDupInodeRespMsg.h>
#include <common/net/message/fsck/UpdateFileAttribsMsg.h>
#include <common/net/message/fsck/UpdateFileAttribsRespMsg.h>
#include <common/net/message/fsck/UpdateDirAttribsMsg.h>
#include <common/net/message/fsck/UpdateDirAttribsRespMsg.h>
#include <common/net/message/nodes/SetTargetConsistencyStatesMsg.h>
#include <common/net/message/nodes/SetTargetConsistencyStatesRespMsg.h>
#include <common/net/message/storage/creating/MkDirMsg.h>
#include <common/net/message/storage/creating/MkDirRespMsg.h>
#include <common/net/message/storage/creating/UnlinkFileMsg.h>
#include <common/net/message/storage/creating/UnlinkFileRespMsg.h>
#include <common/net/message/storage/attribs/SetLocalAttrMsg.h>
#include <common/net/message/storage/attribs/SetLocalAttrRespMsg.h>
#include <common/net/message/storage/creating/MoveFileInodeMsg.h>
#include <common/net/message/storage/creating/MoveFileInodeRespMsg.h>
#include <common/toolkit/MessagingTk.h>
#include <common/toolkit/MetadataTk.h>
#include <program/Program.h>
#include "MsgHelperRepair.h"
#include <boost/lexical_cast.hpp>
template<typename RepairItemT>
static bool setSecondaryBad(uint16_t groupID, std::set<NumNodeID>& secondariesWithRepair,
const std::list<RepairItemT>& args, std::list<RepairItemT>& failed)
{
auto* bgm = Program::getApp()->getMetaMirrorBuddyGroupMapper();
NumNodeID secondary(bgm->getSecondaryTargetID(groupID));
if (secondariesWithRepair.count(secondary))
return true;
auto setRes = MsgHelperRepair::setNodeState(secondary, TargetConsistencyState_BAD);
if (setRes == FhgfsOpsErr_SUCCESS)
{
secondariesWithRepair.insert(NumNodeID(bgm->getSecondaryTargetID(groupID)));
return true;
}
LOG(GENERAL, ERR, "Failed to set secondary consistency state, not attempting repair action.",
groupID, setRes);
failed = args;
return false;
}
FhgfsOpsErr MsgHelperRepair::setNodeState(NumNodeID node, TargetConsistencyState state)
{
std::list<uint16_t> targets(1, node.val());
std::list<uint8_t> states(1, state);
SetTargetConsistencyStatesMsg msg(NODETYPE_Meta, &targets, &states, false);
{
auto secondary = Program::getApp()->getMetaNodes()->referenceNode(node);
MessagingTk::requestResponse(*secondary, msg, NETMSGTYPE_SetTargetConsistencyStatesResp);
}
{
auto mgmt = Program::getApp()->getMgmtNodes()->referenceFirstNode();
const auto respMsg = MessagingTk::requestResponse(*mgmt, msg,
NETMSGTYPE_SetTargetConsistencyStatesResp);
if (!respMsg)
return FhgfsOpsErr_COMMUNICATION;
return static_cast<SetTargetConsistencyStatesRespMsg&>(*respMsg).getResult();
}
}
void MsgHelperRepair::deleteDanglingDirEntries(NumNodeID node, bool isBuddyMirrored,
FsckDirEntryList* dentries, FsckDirEntryList* failedDeletes,
std::set<NumNodeID>& secondariesWithRepair)
{
DeleteDirEntriesMsg deleteDirEntriesMsg(dentries);
RequestResponseNode rrNode(node, Program::getApp()->getMetaNodes());
RequestResponseArgs rrArgs(nullptr, &deleteDirEntriesMsg, NETMSGTYPE_DeleteDirEntriesResp);
if (isBuddyMirrored)
{
rrNode.setMirrorInfo(Program::getApp()->getMetaMirrorBuddyGroupMapper(), false);
if (!setSecondaryBad(node.val(), secondariesWithRepair, *dentries, *failedDeletes))
return;
}
FhgfsOpsErr commRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if (commRes == FhgfsOpsErr_SUCCESS)
{
auto* deleteDirEntriesRespMsg = (DeleteDirEntriesRespMsg*) rrArgs.outRespMsg.get();
deleteDirEntriesRespMsg->getFailedEntries().swap(*failedDeletes);
if (! failedDeletes->empty())
{
for (auto iter = failedDeletes->begin(); iter != failedDeletes->end(); iter++)
LOG(GENERAL, CRITICAL, "Failed to delete directory entry from metadata node.",
node, isBuddyMirrored, ("entryID", iter->getID()));
}
}
else
{
*failedDeletes = *dentries;
LOG(GENERAL, ERR, "Communication error occured.", node, isBuddyMirrored, commRes);
}
}
void MsgHelperRepair::createDefDirInodes(NumNodeID node, bool isBuddyMirrored,
const std::vector<std::tuple<std::string, bool>>& entries, FsckDirInodeList* createdInodes,
std::set<NumNodeID>& secondariesWithRepair)
{
StringList failedInodeIDs;
CreateDefDirInodesMsg createDefDirInodesMsgEx(entries);
RequestResponseNode rrNode(node, Program::getApp()->getMetaNodes());
RequestResponseArgs rrArgs(nullptr, &createDefDirInodesMsgEx, NETMSGTYPE_CreateDefDirInodesResp);
if (isBuddyMirrored)
{
rrNode.setMirrorInfo(Program::getApp()->getMetaMirrorBuddyGroupMapper(), false);
if (!setSecondaryBad(node.val(), secondariesWithRepair, {}, *createdInodes))
{
for (auto it = entries.begin(); it != entries.end(); ++it)
failedInodeIDs.push_back(std::get<0>(*it));
return;
}
}
FhgfsOpsErr commRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if (commRes == FhgfsOpsErr_SUCCESS)
{
auto* createDefDirInodesRespMsg = (CreateDefDirInodesRespMsg*) rrArgs.outRespMsg.get();
createDefDirInodesRespMsg->getFailedInodeIDs().swap(failedInodeIDs);
createDefDirInodesRespMsg->getCreatedInodes().swap(*createdInodes);
}
else
{
for (auto it = entries.begin(); it != entries.end(); ++it)
failedInodeIDs.push_back(std::get<0>(*it));
LOG(GENERAL, ERR, "Communication error occured.", node, isBuddyMirrored, commRes);
}
if (! failedInodeIDs.empty())
{
for (StringListIter iter = failedInodeIDs.begin(); iter != failedInodeIDs.end(); iter++)
LOG(GENERAL, CRITICAL, "Failed to create default directory inode.", node, isBuddyMirrored,
("entryID", *iter));
}
}
void MsgHelperRepair::correctInodeOwnersInDentry(NumNodeID node, bool isBuddyMirrored,
FsckDirEntryList* dentries, NumNodeIDList* owners, FsckDirEntryList* failedCorrections,
std::set<NumNodeID>& secondariesWithRepair)
{
FixInodeOwnersInDentryMsg fixInodeOwnersMsg(*dentries, *owners);
RequestResponseNode rrNode(node, Program::getApp()->getMetaNodes());
RequestResponseArgs rrArgs(nullptr, &fixInodeOwnersMsg, NETMSGTYPE_FixInodeOwnersInDentryResp);
if (isBuddyMirrored)
{
rrNode.setMirrorInfo(Program::getApp()->getMetaMirrorBuddyGroupMapper(), false);
if (!setSecondaryBad(node.val(), secondariesWithRepair, *dentries, *failedCorrections))
return;
}
FhgfsOpsErr commRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if (commRes == FhgfsOpsErr_SUCCESS)
{
auto* fixInodeOwnersRespMsg = (FixInodeOwnersInDentryRespMsg*) rrArgs.outRespMsg.get();
fixInodeOwnersRespMsg->getFailedEntries().swap(*failedCorrections);
}
else
{
*failedCorrections = *dentries;
LOG(GENERAL, ERR, "Communication error occured.", node, isBuddyMirrored, commRes);
}
if (! failedCorrections->empty())
{
for (auto iter = failedCorrections->begin(); iter != failedCorrections->end(); iter++)
LOG(GENERAL, CRITICAL, "Failed to correct inode owner information in dentry.",
node, isBuddyMirrored, ("entryID", iter->getID()));
}
}
void MsgHelperRepair::correctInodeOwners(NumNodeID node, bool isBuddyMirrored,
FsckDirInodeList* dirInodes, FsckDirInodeList* failedCorrections,
std::set<NumNodeID>& secondariesWithRepair)
{
FixInodeOwnersMsg fixInodeOwnersMsg(dirInodes);
RequestResponseNode rrNode(node, Program::getApp()->getMetaNodes());
RequestResponseArgs rrArgs(nullptr, &fixInodeOwnersMsg, NETMSGTYPE_FixInodeOwnersResp);
if (isBuddyMirrored)
{
rrNode.setMirrorInfo(Program::getApp()->getMetaMirrorBuddyGroupMapper(), false);
if (!setSecondaryBad(node.val(), secondariesWithRepair, *dirInodes, *failedCorrections))
return;
}
FhgfsOpsErr commRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if (commRes == FhgfsOpsErr_SUCCESS)
{
auto* fixInodeOwnersRespMsg = (FixInodeOwnersRespMsg*) rrArgs.outRespMsg.get();
fixInodeOwnersRespMsg->getFailedInodes().swap(*failedCorrections);
}
else
{
*failedCorrections = *dirInodes;
LOG(GENERAL, ERR, "Communication error occured.", node, isBuddyMirrored, commRes);
}
if (! failedCorrections->empty())
{
for (auto iter = failedCorrections->begin(); iter != failedCorrections->end(); iter++)
LOG(GENERAL, CRITICAL, "Failed to correct inode owner information.", node, isBuddyMirrored,
("entryID", iter->getID()));
}
}
void MsgHelperRepair::deleteFiles(NumNodeID node, bool isBuddyMirrored, FsckDirEntryList* dentries,
FsckDirEntryList* failedDeletes)
{
const char* logContext = "MsgHelperRepair (deleteFiles)";
for ( FsckDirEntryListIter iter = dentries->begin(); iter != dentries->end(); iter++ )
{
EntryInfo parentInfo(node, "", iter->getParentDirID(), "", DirEntryType_DIRECTORY, 0);
std::string entryName = iter->getName();
UnlinkFileMsg unlinkFileMsg(&parentInfo, entryName);
RequestResponseNode rrNode(node, Program::getApp()->getMetaNodes());
RequestResponseArgs rrArgs(nullptr, &unlinkFileMsg, NETMSGTYPE_UnlinkFileResp);
if (isBuddyMirrored)
rrNode.setMirrorInfo(Program::getApp()->getMetaMirrorBuddyGroupMapper(), false);
FhgfsOpsErr commRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if (commRes == FhgfsOpsErr_SUCCESS)
{
const auto unlinkFileRespMsg = (const UnlinkFileRespMsg*) rrArgs.outRespMsg.get();
// get result
int unlinkRes = unlinkFileRespMsg->getValue();
if ( unlinkRes )
{
LogContext(logContext).log(Log_CRITICAL,
"Failed to delete file; entryID: " + iter->getID());
failedDeletes->push_back(*iter);
}
}
else
{
failedDeletes->push_back(*iter);
LOG(GENERAL, ERR, "Communication error occured.", node, isBuddyMirrored, commRes);
}
}
}
void MsgHelperRepair::deleteChunks(Node& node, FsckChunkList* chunks, FsckChunkList* failedDeletes)
{
const char* logContext = "MsgHelperRepair (deleteChunks)";
DeleteChunksMsg deleteChunksMsg(chunks);
const auto respMsg = MessagingTk::requestResponse(node, deleteChunksMsg,
NETMSGTYPE_DeleteChunksResp);
if (respMsg)
{
DeleteChunksRespMsg* deleteChunksRespMsg = (DeleteChunksRespMsg*) respMsg.get();
deleteChunksRespMsg->getFailedChunks().swap(*failedDeletes);
if (! failedDeletes->empty())
{
for (FsckChunkListIter iter = failedDeletes->begin(); iter != failedDeletes->end();
iter++)
{
LogContext(logContext).log(Log_CRITICAL, "Failed to delete chunk entry. targetID: " +
StringTk::uintToStr(iter->getTargetID()) + " chunkID: " + iter->getID());
}
}
}
else
{
*failedDeletes = *chunks;
LogContext(logContext).logErr("Communication error occured with node: " + node.getAlias());
}
}
NodeHandle MsgHelperRepair::referenceLostAndFoundOwner(EntryInfo* outLostAndFoundEntryInfo)
{
const char* logContext = "MsgHelperRepair (referenceLostAndFoundOwner)";
App* app = Program::getApp();
// find owner node
Path path("/" + std::string(META_LOSTANDFOUND_PATH));
NodeHandle ownerNode;
FhgfsOpsErr findRes = MetadataTk::referenceOwner(&path, app->getMetaNodes(), ownerNode,
outLostAndFoundEntryInfo, app->getMetaRoot(), app->getMetaMirrorBuddyGroupMapper());
if ( findRes != FhgfsOpsErr_SUCCESS )
{
LogContext(logContext).log(Log_DEBUG, "No owner node found for lost+found. Directory does not"
" seem to exist (yet)");
}
return ownerNode;
}
bool MsgHelperRepair::createLostAndFound(NodeHandle& outReferencedNode,
EntryInfo& outLostAndFoundEntryInfo)
{
const char* logContext = "MsgHelperRepair (createLostAndFound)";
App* app = Program::getApp();
NodeStore* metaNodes = app->getMetaNodes();
bool retVal = false;
// get root owner node and entryInfo
NodeHandle rootNode;
Path rootPath("");
// rootPath.setAbsolute(true);
EntryInfo rootEntryInfo;
FhgfsOpsErr findRes = MetadataTk::referenceOwner(&rootPath, metaNodes,
rootNode, &rootEntryInfo, app->getMetaRoot(), app->getMetaMirrorBuddyGroupMapper());
if ( findRes != FhgfsOpsErr_SUCCESS )
{
LogContext(logContext).log(Log_CRITICAL, "Unable to reference metadata node for root "
"directory");
return false;
}
// create the directory
std::string lostFoundPathStr = META_LOSTANDFOUND_PATH;
UInt16List preferredNodes;
MkDirMsg mkDirMsg(&rootEntryInfo, lostFoundPathStr , 0, 0, S_IFDIR | S_IRWXU | S_IRWXG, 0000,
&preferredNodes);
RequestResponseNode rrNode(rootEntryInfo.getOwnerNodeID(), Program::getApp()->getMetaNodes());
RequestResponseArgs rrArgs(nullptr, &mkDirMsg, NETMSGTYPE_MkDirResp);
if (rootEntryInfo.getIsBuddyMirrored())
rrNode.setMirrorInfo(Program::getApp()->getMetaMirrorBuddyGroupMapper(), false);
FhgfsOpsErr commRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if (commRes != FhgfsOpsErr_SUCCESS)
return false;
auto* mkDirRespMsg = (MkDirRespMsg*) rrArgs.outRespMsg.get();
FhgfsOpsErr mkDirRes = (FhgfsOpsErr) mkDirRespMsg->getResult();
if ( mkDirRes != FhgfsOpsErr_SUCCESS )
{
LogContext(logContext).log(Log_CRITICAL,
"Node encountered an error: " + boost::lexical_cast<std::string>(mkDirRes));
return false;
}
// create seems to have succeeded
// copy is created because delete is called on mkDirRespMsg, but we still need this object
outLostAndFoundEntryInfo = *(mkDirRespMsg->getEntryInfo());
outReferencedNode = outLostAndFoundEntryInfo.getIsBuddyMirrored()
? metaNodes->referenceNode(
NumNodeID(
app->getMetaMirrorBuddyGroupMapper()->getPrimaryTargetID(
outLostAndFoundEntryInfo.getOwnerNodeID().val())))
: metaNodes->referenceNode(outLostAndFoundEntryInfo.getOwnerNodeID());
retVal = true;
return retVal;
}
void MsgHelperRepair::linkToLostAndFound(Node& lostAndFoundNode, EntryInfo* lostAndFoundInfo,
FsckDirInodeList* dirInodes, FsckDirInodeList* failedInodes, FsckDirEntryList* createdDentries,
std::set<NumNodeID>& secondariesWithRepair)
{
const char* logContext = "MsgHelperRepair (linkToLostAndFound)";
if (lostAndFoundInfo->getIsBuddyMirrored() &&
!setSecondaryBad(lostAndFoundInfo->getOwnerNodeID().val(), secondariesWithRepair,
{}, *createdDentries))
{
return;
}
LinkToLostAndFoundMsg linkToLostAndFoundMsg(lostAndFoundInfo, dirInodes);
const auto respMsg = MessagingTk::requestResponse(lostAndFoundNode, linkToLostAndFoundMsg,
NETMSGTYPE_LinkToLostAndFoundResp);
if (respMsg)
{
auto* linkToLostAndFoundRespMsg = (LinkToLostAndFoundRespMsg*) respMsg.get();
linkToLostAndFoundRespMsg->getFailedDirInodes().swap(*failedInodes);
linkToLostAndFoundRespMsg->getCreatedDirEntries().swap(*createdDentries);
}
else
{
*failedInodes = *dirInodes;
LogContext(logContext).logErr("Communication error occured with node: " +
lostAndFoundNode.getAlias());
}
if (! failedInodes->empty())
{
for (FsckDirInodeListIter iter = failedInodes->begin(); iter != failedInodes->end();
iter++)
{
LogContext(logContext).log(Log_CRITICAL, "Failed to link directory inode to lost+found. "
"entryID: " + iter->getID());
}
}
}
void MsgHelperRepair::linkToLostAndFound(Node& lostAndFoundNode, EntryInfo* lostAndFoundInfo,
FsckFileInodeList* fileInodes, FsckFileInodeList* failedInodes,
FsckDirEntryList* createdDentries, std::set<NumNodeID>& secondariesWithRepair)
{
const char* logContext = "MsgHelperRepair (linkToLostAndFound)";
if (lostAndFoundInfo->getIsBuddyMirrored() &&
!setSecondaryBad(lostAndFoundInfo->getOwnerNodeID().val(), secondariesWithRepair,
*fileInodes, *failedInodes))
{
return;
}
LinkToLostAndFoundMsg linkToLostAndFoundMsg(lostAndFoundInfo, fileInodes);
const auto respMsg = MessagingTk::requestResponse(lostAndFoundNode, linkToLostAndFoundMsg,
NETMSGTYPE_LinkToLostAndFoundResp);
if (respMsg)
{
auto* linkToLostAndFoundRespMsg = (LinkToLostAndFoundRespMsg*) respMsg.get();
linkToLostAndFoundRespMsg->getFailedFileInodes().swap(*failedInodes);
linkToLostAndFoundRespMsg->getCreatedDirEntries().swap(*createdDentries);
}
else
{
*failedInodes = *fileInodes;
LogContext(logContext).logErr("Communication error occured with node: " +
lostAndFoundNode.getAlias());
}
if (! failedInodes->empty())
{
for (FsckFileInodeListIter iter = failedInodes->begin(); iter != failedInodes->end();
iter++)
{
LogContext(logContext).log(Log_CRITICAL, "Failed to link file inode to lost+found. "
"entryID: " + iter->getID());
}
}
}
void MsgHelperRepair::createContDirs(NumNodeID node, bool isBuddyMirrored, FsckDirInodeList* inodes,
StringList* failedCreates, std::set<NumNodeID>& secondariesWithRepair)
{
// create a string list with the IDs
std::vector<CreateEmptyContDirsMsg::Item> items;
for (FsckDirInodeListIter iter = inodes->begin(); iter != inodes->end(); iter++)
items.emplace_back(iter->getID(), iter->getIsBuddyMirrored());
CreateEmptyContDirsMsg createContDirsMsg(std::move(items));
RequestResponseNode rrNode(node, Program::getApp()->getMetaNodes());
RequestResponseArgs rrArgs(nullptr, &createContDirsMsg, NETMSGTYPE_CreateEmptyContDirsResp);
if (isBuddyMirrored)
{
rrNode.setMirrorInfo(Program::getApp()->getMetaMirrorBuddyGroupMapper(), false);
if (!setSecondaryBad(node.val(), secondariesWithRepair, {}, *failedCreates))
{
for (auto it = inodes->begin(); it != inodes->end(); ++it)
failedCreates->push_back(it->getID());
return;
}
}
FhgfsOpsErr commRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if (commRes == FhgfsOpsErr_SUCCESS)
{
auto* createContDirsRespMsg = (CreateEmptyContDirsRespMsg*) rrArgs.outRespMsg.get();
createContDirsRespMsg->getFailedIDs().swap(*failedCreates);
}
else
{
failedCreates->clear();
for (auto it = inodes->begin(); it != inodes->end(); ++it)
failedCreates->push_back(it->getID());
LOG(GENERAL, ERR, "Communication error occured.", node, isBuddyMirrored, commRes);
}
if ( !failedCreates->empty() )
{
for ( StringListIter iter = failedCreates->begin(); iter != failedCreates->end(); iter++ )
LOG(GENERAL, CRITICAL, "Failed to create empty content directory.", ("dirID", *iter));
}
}
void MsgHelperRepair::updateFileAttribs(NumNodeID node, bool isBuddyMirrored, FsckFileInodeList* inodes,
FsckFileInodeList* failedUpdates, std::set<NumNodeID>& secondariesWithRepair)
{
UpdateFileAttribsMsg updateFileAttribsMsg(inodes);
RequestResponseNode rrNode(node, Program::getApp()->getMetaNodes());
RequestResponseArgs rrArgs(nullptr, &updateFileAttribsMsg, NETMSGTYPE_UpdateFileAttribsResp);
if (isBuddyMirrored)
{
rrNode.setMirrorInfo(Program::getApp()->getMetaMirrorBuddyGroupMapper(), false);
if (!setSecondaryBad(node.val(), secondariesWithRepair, *inodes, *failedUpdates))
return;
}
FhgfsOpsErr commRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if (commRes == FhgfsOpsErr_SUCCESS)
{
auto* updateFileAttribsRespMsg = (UpdateFileAttribsRespMsg*) rrArgs.outRespMsg.get();
updateFileAttribsRespMsg->getFailedInodes().swap(*failedUpdates);
}
else
{
*failedUpdates = *inodes;
LOG(GENERAL, ERR, "Communication error occured.", node, isBuddyMirrored, commRes);
}
if ( !failedUpdates->empty() )
{
for (auto iter = failedUpdates->begin(); iter != failedUpdates->end(); iter++)
LOG(GENERAL, CRITICAL, "Failed to update attributes of file inode.",
("entryID", iter->getID()));
}
}
void MsgHelperRepair::updateDirAttribs(NumNodeID node, bool isBuddyMirrored,
FsckDirInodeList* inodes, FsckDirInodeList* failedUpdates,
std::set<NumNodeID>& secondariesWithRepair)
{
UpdateDirAttribsMsg updateDirAttribsMsg(inodes);
RequestResponseNode rrNode(node, Program::getApp()->getMetaNodes());
RequestResponseArgs rrArgs(nullptr, &updateDirAttribsMsg, NETMSGTYPE_UpdateDirAttribsResp);
if (isBuddyMirrored)
{
rrNode.setMirrorInfo(Program::getApp()->getMetaMirrorBuddyGroupMapper(), false);
if (!setSecondaryBad(node.val(), secondariesWithRepair, *inodes, *failedUpdates))
return;
}
FhgfsOpsErr commRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if (commRes == FhgfsOpsErr_SUCCESS)
{
auto* updateDirAttribsRespMsg = (UpdateDirAttribsRespMsg*) rrArgs.outRespMsg.get();
updateDirAttribsRespMsg->getFailedInodes().swap(*failedUpdates);
}
else
{
*failedUpdates = *inodes;
LOG(GENERAL, ERR, "Communication error occured.", node, isBuddyMirrored, commRes);
}
if ( !failedUpdates->empty() )
{
for (auto iter = failedUpdates->begin(); iter != failedUpdates->end(); iter++)
LOG(GENERAL, CRITICAL, "Failed to update attributes of directory inode.",
("entryID", iter->getID()));
}
}
void MsgHelperRepair::recreateFsIDs(NumNodeID node, bool isBuddyMirrored,
FsckDirEntryList* dentries, FsckDirEntryList* failedEntries,
std::set<NumNodeID>& secondariesWithRepair)
{
const char* logContext = "MsgHelperRepair (recreateFsIDs)";
RecreateFsIDsMsg recreateFsIDsMsg(dentries);
RequestResponseNode rrNode(node, Program::getApp()->getMetaNodes());
RequestResponseArgs rrArgs(nullptr, &recreateFsIDsMsg, NETMSGTYPE_RecreateFsIDsResp);
if (isBuddyMirrored)
{
rrNode.setMirrorInfo(Program::getApp()->getMetaMirrorBuddyGroupMapper(), false);
if (!setSecondaryBad(node.val(), secondariesWithRepair, *dentries, *failedEntries))
return;
}
FhgfsOpsErr commRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if (commRes == FhgfsOpsErr_SUCCESS)
{
RecreateFsIDsRespMsg* recreateFsIDsRespMsg = (RecreateFsIDsRespMsg*) rrArgs.outRespMsg.get();
recreateFsIDsRespMsg->getFailedEntries().swap(*failedEntries);
}
else
{
*failedEntries = *dentries;
LOG(GENERAL, ERR, "Communication error occured.", node, isBuddyMirrored, commRes);
}
if ( !failedEntries->empty() )
{
for ( FsckDirEntryListIter iter = failedEntries->begin(); iter != failedEntries->end();
iter++ )
{
LogContext(logContext).log(Log_CRITICAL, "Failed to recreate dentry-by-ID file link."
" entryID: " + iter->getID());
}
}
}
void MsgHelperRepair::recreateDentries(NumNodeID node, bool isBuddyMirrored, FsckFsIDList* fsIDs,
FsckFsIDList* failedCreates, FsckDirEntryList* createdDentries, FsckFileInodeList* createdInodes,
std::set<NumNodeID>& secondariesWithRepair)
{
const char* logContext = "MsgHelperRepair (recreateDentries)";
RecreateDentriesMsg recreateDentriesMsg(fsIDs);
RequestResponseNode rrNode(node, Program::getApp()->getMetaNodes());
RequestResponseArgs rrArgs(nullptr, &recreateDentriesMsg, NETMSGTYPE_RecreateDentriesResp);
if (isBuddyMirrored)
{
rrNode.setMirrorInfo(Program::getApp()->getMetaMirrorBuddyGroupMapper(), false);
if (!setSecondaryBad(node.val(), secondariesWithRepair, *fsIDs, *failedCreates))
return;
}
FhgfsOpsErr commRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if (commRes == FhgfsOpsErr_SUCCESS)
{
auto* recreateDentriesMsg = (RecreateDentriesRespMsg*) rrArgs.outRespMsg.get();
recreateDentriesMsg->getFailedCreates().swap(*failedCreates);
recreateDentriesMsg->getCreatedDentries().swap(*createdDentries);
recreateDentriesMsg->getCreatedInodes().swap(*createdInodes);
}
else
{
*failedCreates = *fsIDs;
LOG(GENERAL, ERR, "Communication error occured.", node, isBuddyMirrored, commRes);
}
if ( !failedCreates->empty() )
{
for ( FsckFsIDListIter iter = failedCreates->begin(); iter != failedCreates->end();
iter++ )
{
LogContext(logContext).log(Log_CRITICAL, "Failed to recreate dentry."
" entryID: " + iter->getID());
}
}
}
void MsgHelperRepair::fixChunkPermissions(Node& node, FsckChunkList& chunkList,
PathInfoList& pathInfoList, FsckChunkList& failedChunks)
{
const char* logContext = "MsgHelperRepair (fixChunkPermissions)";
if ( chunkList.size() != pathInfoList.size() )
{
LogContext(logContext).logErr(
"Failed to set uid/gid for chunks. Size of lists does not match.");
return;
}
FsckChunkListIter chunksIter = chunkList.begin();
PathInfoListIter pathInfoIter = pathInfoList.begin();
for ( ; chunksIter != chunkList.end(); chunksIter++, pathInfoIter++ )
{
std::string chunkID = chunksIter->getID();
uint16_t targetID = chunksIter->getTargetID();
int validAttribs = SETATTR_CHANGE_USERID | SETATTR_CHANGE_GROUPID; // only interested in these
SettableFileAttribs attribs = {0, 0, 0, 0, 0};
attribs.userID = chunksIter->getUserID();
attribs.groupID = chunksIter->getGroupID();
bool enableCreation = false;
PathInfo pathInfo = *pathInfoIter;
SetLocalAttrMsg setLocalAttrMsg(chunkID, targetID, &pathInfo, validAttribs, &attribs,
enableCreation);
setLocalAttrMsg.addMsgHeaderFeatureFlag(SETLOCALATTRMSG_FLAG_USE_QUOTA);
const auto respMsg = MessagingTk::requestResponse(node, setLocalAttrMsg,
NETMSGTYPE_SetLocalAttrResp);
if (respMsg)
{
SetLocalAttrRespMsg* setLocalAttrRespMsg = (SetLocalAttrRespMsg*) respMsg.get();
if ( setLocalAttrRespMsg->getResult() != FhgfsOpsErr_SUCCESS )
{
LogContext(logContext).logErr(
"Failed to set uid/gid for chunk. chunkID: " + chunkID + "; targetID: "
+ StringTk::uintToStr(targetID));
failedChunks.push_back(*chunksIter);
}
}
else
{
LogContext(logContext).logErr("Communication error occured with node: " + node.getAlias());
failedChunks.push_back(*chunksIter);
}
}
}
/*
* NOTE: chunk gets modified! (new savedPath is set)
*/
bool MsgHelperRepair::moveChunk(Node& node, FsckChunk& chunk, const std::string& moveTo,
bool allowOverwrite)
{
const char* logContext = "MsgHelperRepair (moveChunks)";
bool result;
MoveChunkFileMsg moveChunkFileMsg(chunk.getID(), chunk.getTargetID(),
chunk.getBuddyGroupID() != 0, chunk.getSavedPath()->str(), moveTo, allowOverwrite);
const auto respMsg = MessagingTk::requestResponse(node, moveChunkFileMsg,
NETMSGTYPE_MoveChunkFileResp);
if (respMsg)
{
MoveChunkFileRespMsg* moveChunkFileRespMsg = (MoveChunkFileRespMsg*) respMsg.get();
result = moveChunkFileRespMsg->getValue() == FhgfsOpsErr_SUCCESS;
if(!result)
{
LogContext(logContext).logErr(
"Failed to move chunk. chunkID: " + chunk.getID() + "; targetID: "
+ StringTk::uintToStr(chunk.getTargetID() ) + "; fromPath: "
+ chunk.getSavedPath()->str() + "; toPath: "
+ moveTo);
}
else
{
// set newPath in chunk
chunk.setSavedPath(Path(moveTo));
}
}
else
{
LogContext(logContext).logErr("Communication error occured with node: " + node.getAlias());
result = false;
}
return result;
}
void MsgHelperRepair::deleteFileInodes(NumNodeID node, bool isBuddyMirrored,
FsckFileInodeList& inodes, StringList& failedDeletes, std::set<NumNodeID>& secondariesWithRepair)
{
std::vector<RemoveInodesMsg::Item> items;
for (auto it = inodes.begin(); it != inodes.end(); ++it)
items.emplace_back(it->getID(), DirEntryType_REGULARFILE, it->getIsBuddyMirrored());
RemoveInodesMsg removeInodesMsg(std::move(items));
RequestResponseNode rrNode(node, Program::getApp()->getMetaNodes());
RequestResponseArgs rrArgs(nullptr, &removeInodesMsg, NETMSGTYPE_RemoveInodesResp);
if (isBuddyMirrored)
{
rrNode.setMirrorInfo(Program::getApp()->getMetaMirrorBuddyGroupMapper(), false);
if (!setSecondaryBad(node.val(), secondariesWithRepair, {}, failedDeletes))
{
for (auto it = inodes.begin(); it != inodes.end(); ++it)
failedDeletes.push_back(it->getID());
return;
}
}
FhgfsOpsErr commRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if (commRes == FhgfsOpsErr_SUCCESS)
{
RemoveInodesRespMsg* removeInodesRespMsg = (RemoveInodesRespMsg*) rrArgs.outRespMsg.get();
failedDeletes = removeInodesRespMsg->releaseFailedEntryIDList();
for ( StringListIter iter = failedDeletes.begin(); iter != failedDeletes.end(); iter++ )
LOG(GENERAL, ERR, "Failed to delete file inode.", node, isBuddyMirrored,
("entryID", *iter));
}
else
{
LOG(GENERAL, ERR, "Communication error occured.", node, isBuddyMirrored, commRes);
failedDeletes.clear();
for (auto it = inodes.begin(); it != inodes.end(); ++it)
failedDeletes.push_back(it->getID());
}
}
void MsgHelperRepair::deleteDuplicateFileInodes(NumNodeID node, bool isBuddyMirrored,
FsckDuplicateInodeInfoVector& dupInodes, StringList& failedEntries, std::set<NumNodeID>& secondariesWithRepair)
{
const char* logContext = "MsgHelperRepair (check and repair duplicate inode)";
CheckAndRepairDupInodeMsg repairDupInodeMsg(&dupInodes);
RequestResponseNode rrNode(node, Program::getApp()->getMetaNodes());
RequestResponseArgs rrArgs(nullptr, &repairDupInodeMsg, NETMSGTYPE_CheckAndRepairDupInodeResp);
if (isBuddyMirrored)
{
rrNode.setMirrorInfo(Program::getApp()->getMetaMirrorBuddyGroupMapper(), false);
if (!setSecondaryBad(node.val(), secondariesWithRepair, {}, failedEntries))
{
for (auto it = dupInodes.begin(); it != dupInodes.end(); ++it)
failedEntries.push_back(it->getID());
return;
}
}
FhgfsOpsErr commRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if (commRes == FhgfsOpsErr_SUCCESS)
{
CheckAndRepairDupInodeRespMsg* repairDupInodeRespMsg = (CheckAndRepairDupInodeRespMsg*) rrArgs.outRespMsg.get();
failedEntries = repairDupInodeRespMsg->releaseFailedEntryIDList();
for (StringListIter iter = failedEntries.begin(); iter != failedEntries.end(); ++iter)
{
LogContext(logContext).logErr("Failed to repair duplicate inode, entryID: " + *iter);
}
}
else
{
LogContext(logContext).logErr("Communication error occured with node: " + std::to_string(node.val()));
}
}
void MsgHelperRepair::deinlineFileInode(NumNodeID node, EntryInfo* entryInfo, StringList& failedEntries,
std::set<NumNodeID>& secondariesWithRepair)
{
const char* logContext = "MsgHelperRepair (Deinline File Inode)";
MoveFileInodeMsg msg(entryInfo, MODE_DEINLINE);
RequestResponseNode rrNode(node, Program::getApp()->getMetaNodes());
RequestResponseArgs rrArgs(nullptr, &msg, NETMSGTYPE_MoveFileInodeResp);
if (entryInfo->getIsBuddyMirrored())
{
rrNode.setMirrorInfo(Program::getApp()->getMetaMirrorBuddyGroupMapper(), false);
if (!setSecondaryBad(node.val(), secondariesWithRepair, {}, failedEntries))
{
failedEntries.push_back(entryInfo->getEntryID());
return;
}
}
FhgfsOpsErr commRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if (commRes == FhgfsOpsErr_SUCCESS)
{
auto respMsg = (MoveFileInodeRespMsg*) rrArgs.outRespMsg.get();
if (respMsg->getResult() != FhgfsOpsErr_SUCCESS)
{
LogContext(logContext).logErr("Failed to migrate hardlink metadata to new version, entryID: " + entryInfo->getEntryID());
failedEntries.push_back(entryInfo->getEntryID());
}
}
else
{
failedEntries.push_back(entryInfo->getEntryID());
LogContext(logContext).logErr("Communication error occured with node: " + std::to_string(node.val()));
}
}

View File

@@ -0,0 +1,77 @@
#ifndef MSGHELPERREPAIR_H_
#define MSGHELPERREPAIR_H_
#include <common/Common.h>
#include <common/nodes/Node.h>
#include <common/nodes/TargetStateInfo.h>
#include <common/fsck/FsckChunk.h>
#include <common/fsck/FsckDirEntry.h>
#include <common/fsck/FsckDirInode.h>
#include <common/fsck/FsckFsID.h>
#include <common/fsck/FsckDuplicateInodeInfo.h>
class MsgHelperRepair
{
public:
static FhgfsOpsErr setNodeState(NumNodeID node, TargetConsistencyState state);
static void deleteDanglingDirEntries(NumNodeID node, bool isBuddyMirrored,
FsckDirEntryList* dentries, FsckDirEntryList* failedDeletes,
std::set<NumNodeID>& secondariesWithRepair);
static void createDefDirInodes(NumNodeID node, bool isBuddyMirrored,
const std::vector<std::tuple<std::string, bool>>& entries,
FsckDirInodeList* createdInodes,
std::set<NumNodeID>& secondariesWithRepair);
static void correctInodeOwnersInDentry(NumNodeID node, bool isBuddyMirrored,
FsckDirEntryList* dentries, NumNodeIDList* owners, FsckDirEntryList* failedCorrections,
std::set<NumNodeID>& secondariesWithRepair);
static void correctInodeOwners(NumNodeID node, bool isBuddyMirrored,
FsckDirInodeList* dirInodes, FsckDirInodeList* failedCorrections,
std::set<NumNodeID>& secondariesWithRepair);
static void deleteFiles(NumNodeID node, bool isBuddyMirrored, FsckDirEntryList* dentries,
FsckDirEntryList* failedDeletes);
static void deleteChunks(Node& node, FsckChunkList* chunks, FsckChunkList* failedDeletes);
static NodeHandle referenceLostAndFoundOwner(EntryInfo* outLostAndFoundEntryInfo);
static bool createLostAndFound(NodeHandle& outReferencedNode,
EntryInfo& outLostAndFoundEntryInfo);
static void linkToLostAndFound(Node& lostAndFoundNode, EntryInfo* lostAndFoundInfo,
FsckDirInodeList* dirInodes, FsckDirInodeList* failedInodes,
FsckDirEntryList* createdDentries, std::set<NumNodeID>& secondariesWithRepair);
static void linkToLostAndFound(Node& lostAndFoundNode, EntryInfo* lostAndFoundInfo,
FsckFileInodeList* fileInodes, FsckFileInodeList* failedInodes,
FsckDirEntryList* createdDentries, std::set<NumNodeID>& secondariesWithRepair);
static void createContDirs(NumNodeID node, bool isBuddyMirrored, FsckDirInodeList* inodes,
StringList* failedCreates, std::set<NumNodeID>& secondariesWithRepair);
static void updateFileAttribs(NumNodeID node, bool isBuddyMirrored, FsckFileInodeList* inodes,
FsckFileInodeList* failedUpdates, std::set<NumNodeID>& secondariesWithRepair);
static void updateDirAttribs(NumNodeID node, bool isBuddyMirrored, FsckDirInodeList* inodes,
FsckDirInodeList* failedUpdates, std::set<NumNodeID>& secondariesWithRepair);
static void recreateFsIDs(NumNodeID node, bool isBuddyMirrored, FsckDirEntryList* dentries,
FsckDirEntryList* failedEntries, std::set<NumNodeID>& secondariesWithRepair);
static void recreateDentries(NumNodeID node, bool isBuddyMirrored, FsckFsIDList* fsIDs,
FsckFsIDList* failedCreates, FsckDirEntryList* createdDentries,
FsckFileInodeList* createdInodes, std::set<NumNodeID>& secondariesWithRepair);
static void fixChunkPermissions(Node& node, FsckChunkList& chunkList,
PathInfoList& pathInfoList, FsckChunkList& failedChunks);
static bool moveChunk(Node& node, FsckChunk& chunk, const std::string& moveTo,
bool allowOverwrite);
static void deleteFileInodes(NumNodeID node, bool isBuddyMirrored, FsckFileInodeList& inodes,
StringList& failedDeletes, std::set<NumNodeID>& secondariesWithRepair);
static void deleteDuplicateFileInodes(NumNodeID node, bool isBuddyMirrored,
FsckDuplicateInodeInfoVector& dupInodes, StringList& failedEntries, std::set<NumNodeID>& secondariesWithRepair);
static void deinlineFileInode(NumNodeID node, EntryInfo* entryInfo, StringList& failedEntries,
std::set<NumNodeID>& secondariesWithRepair);
private:
MsgHelperRepair() {}
public:
// inliners
};
#endif /* MSGHELPERREPAIR_H_ */

View File

@@ -0,0 +1,7 @@
#include "Program.h"
int main(int argc, char** argv)
{
return Program::main(argc, argv);
}

View File

@@ -0,0 +1,23 @@
#include <common/toolkit/BuildTypeTk.h>
#include "Program.h"
App* Program::app = NULL;
int Program::main(int argc, char** argv)
{
BuildTypeTk::checkDebugBuildTypes();
AbstractApp::runTimeInitsAndChecks(); // must be called before creating a new App
app = new App(argc, argv);
app->startInCurrentThread();
int appRes = app->getAppResult();
delete app;
return appRes;
}

View File

@@ -0,0 +1,27 @@
#ifndef PROGRAM_H_
#define PROGRAM_H_
#include <app/App.h>
class Program
{
public:
static int main(int argc, char** argv);
private:
Program() {}
static App* app;
public:
// getters & setters
static App* getApp()
{
return app;
}
};
#endif /*PROGRAM_H_*/

View File

@@ -0,0 +1,273 @@
#include "DatabaseTk.h"
#include <common/app/log/LogContext.h>
#include <common/storage/striping/Raid0Pattern.h>
#include <common/toolkit/FsckTk.h>
#include <common/toolkit/StringTk.h>
#include <database/FsckDB.h>
#include <database/FsckDBTable.h>
#include <common/storage/Path.h>
#include <common/toolkit/StorageTk.h>
DatabaseTk::DatabaseTk()
{
}
DatabaseTk::~DatabaseTk()
{
}
FsckDirEntry DatabaseTk::createDummyFsckDirEntry(FsckDirEntryType entryType)
{
FsckDirEntryList list;
createDummyFsckDirEntries(7, 1, &list, entryType);
return list.front();
}
void DatabaseTk::createDummyFsckDirEntries(uint amount, FsckDirEntryList* outList,
FsckDirEntryType entryType)
{
createDummyFsckDirEntries(0,amount, outList, entryType);
}
void DatabaseTk::createDummyFsckDirEntries(uint from, uint amount, FsckDirEntryList* outList,
FsckDirEntryType entryType)
{
for ( uint i = from; i < (from+amount); i++ )
{
std::string index = StringTk::uintToHexStr(i);
std::string id = index + "-1-1";
std::string name = "dentryName" + index;
std::string parentID = index + "-2-1";
NumNodeID entryOwnerNodeID(i + 1000);
NumNodeID inodeOwnerNodeID(i + 2000);
NumNodeID saveNodeID(i + 3000);
int saveDevice = i;
uint64_t saveInode = i;
FsckDirEntry dentry(id, name, parentID, entryOwnerNodeID, inodeOwnerNodeID, entryType, true,
saveNodeID, saveDevice, saveInode, false);
outList->push_back(dentry);
}
}
FsckFileInode DatabaseTk::createDummyFsckFileInode()
{
FsckFileInodeList list;
createDummyFsckFileInodes(7, 1, &list);
return list.front();
}
void DatabaseTk::createDummyFsckFileInodes(uint amount, FsckFileInodeList* outList)
{
createDummyFsckFileInodes(0,amount,outList);
}
void DatabaseTk::createDummyFsckFileInodes(uint from, uint amount, FsckFileInodeList* outList)
{
for ( uint i = from; i < (from + amount); i++ )
{
std::string index = StringTk::uintToHexStr(i);
UInt16Vector targetIDs;
while (targetIDs.size() < i)
targetIDs.push_back(i + 1000 * (targetIDs.size() + 1) );
Raid0Pattern stripePatternIn(1024, targetIDs);
std::string id = index + "-1-1";
std::string parentDirID = index + "-2-1";
NumNodeID parentNodeID(i + 1000);
unsigned userID = 1000;
unsigned groupID = 1000;
uint64_t usedBlocks = 0;
int64_t fileSize = usedBlocks*512;
unsigned numHardLinks = 1;
PathInfo pathInfo(userID, parentDirID, PATHINFO_FEATURE_ORIG);
FsckStripePatternType stripePatternType = FsckTk::stripePatternToFsckStripePattern(
&stripePatternIn);
unsigned chunkSize = 524288;
NumNodeID saveNodeID(i + 2000);
FsckFileInode fileInode(id, parentDirID, parentNodeID, pathInfo, userID, groupID, fileSize,
numHardLinks, usedBlocks, targetIDs, stripePatternType, chunkSize, saveNodeID,
1000 + i, 42, true, false, true, false);
outList->push_back(fileInode);
}
}
FsckDirInode DatabaseTk::createDummyFsckDirInode()
{
FsckDirInodeList list;
createDummyFsckDirInodes(7, 1, &list);
return list.front();
}
void DatabaseTk::createDummyFsckDirInodes(uint amount, FsckDirInodeList* outList)
{
createDummyFsckDirInodes(0, amount, outList);
}
void DatabaseTk::createDummyFsckDirInodes(uint from, uint amount, FsckDirInodeList* outList)
{
for ( uint i = from; i < (from + amount); i++ )
{
std::string index = StringTk::uintToHexStr(i);
UInt16Vector targetIDs;
Raid0Pattern stripePatternIn(1024, targetIDs);
std::string id = index + "-1-1";
std::string parentDirID = index + "-2-1";
NumNodeID parentNodeID(i + 1000);
NumNodeID ownerNodeID(i + 2000);
int64_t size = 10;
unsigned numHardLinks = 12;
FsckStripePatternType stripePatternType = FsckTk::stripePatternToFsckStripePattern(
&stripePatternIn);
NumNodeID saveNodeID(i + 3000);
FsckDirInode dirInode(id, parentDirID, parentNodeID, ownerNodeID, size, numHardLinks,
targetIDs, stripePatternType, saveNodeID, false, true, false);
outList->push_back(dirInode);
}
}
FsckChunk DatabaseTk::createDummyFsckChunk()
{
FsckChunkList list;
createDummyFsckChunks(7, 1, &list);
return list.front();
}
void DatabaseTk::createDummyFsckChunks(uint amount, FsckChunkList* outList)
{
createDummyFsckChunks(amount, 1, outList);
}
void DatabaseTk::createDummyFsckChunks(uint amount, uint numTargets, FsckChunkList* outList)
{
createDummyFsckChunks(0, amount, numTargets, outList);
}
void DatabaseTk::createDummyFsckChunks(uint from, uint amount, uint numTargets,
FsckChunkList* outList)
{
for ( uint i = from; i < (from + amount); i++ )
{
std::string index = StringTk::uintToHexStr(i);
for ( uint j = 0; j < numTargets; j++ )
{
std::string id = index + "-1-1";
uint16_t targetID = j;
PathInfo pathInfo(0, META_ROOTDIR_ID_STR, PATHINFO_FEATURE_ORIG);
Path chunkDirPath; // ignored!
std::string chunkFilePathStr;
StorageTk::getChunkDirChunkFilePath(&pathInfo, id, true, chunkDirPath,
chunkFilePathStr);
Path savedPath(chunkFilePathStr);
uint64_t usedBlocks = 300;
int64_t fileSize = usedBlocks*512;
FsckChunk chunk(id, targetID, savedPath, fileSize, usedBlocks, 0, 0, 0, 0, 0);
outList->push_back(chunk);
}
}
}
FsckContDir DatabaseTk::createDummyFsckContDir()
{
FsckContDirList list;
createDummyFsckContDirs(7, 1, &list);
return list.front();
}
void DatabaseTk::createDummyFsckContDirs(uint amount, FsckContDirList* outList)
{
createDummyFsckContDirs(0, amount, outList);
}
void DatabaseTk::createDummyFsckContDirs(uint from, uint amount, FsckContDirList* outList)
{
for ( uint i = from; i < (from + amount); i++ )
{
std::string index = StringTk::uintToHexStr(i);
std::string id = index + "-1-1";
NumNodeID saveNodeID(i);
FsckContDir contDir(id, saveNodeID, false);
outList->push_back(contDir);
}
}
FsckFsID DatabaseTk::createDummyFsckFsID()
{
FsckFsIDList list;
createDummyFsckFsIDs(7, 1, &list);
return list.front();
}
void DatabaseTk::createDummyFsckFsIDs(uint amount, FsckFsIDList* outList)
{
createDummyFsckFsIDs(0, amount, outList);
}
void DatabaseTk::createDummyFsckFsIDs(uint from, uint amount, FsckFsIDList* outList)
{
for ( uint i = from; i < (from + amount); i++ )
{
std::string index = StringTk::uintToHexStr(i);
std::string id = index + "-1-1";
std::string parentDirID = index + "-2-1";
NumNodeID saveNodeID(i);
int saveDevice = i;
uint64_t saveInode = i;
FsckFsID fsID(id, parentDirID, saveNodeID, saveDevice, saveInode, false);
outList->push_back(fsID);
}
}
std::string DatabaseTk::calculateExpectedChunkPath(std::string entryID, unsigned origParentUID,
std::string origParentEntryID, int pathInfoFlags)
{
std::string resStr;
bool hasOrigFeature;
hasOrigFeature = pathInfoFlags == PATHINFO_FEATURE_ORIG;
PathInfo pathInfo(origParentUID, origParentEntryID, pathInfoFlags);
Path chunkDirPath;
std::string chunkFilePath;
StorageTk::getChunkDirChunkFilePath(&pathInfo, entryID, hasOrigFeature, chunkDirPath,
chunkFilePath);
if (hasOrigFeature) // we can use the Path chunkDirPath
resStr = chunkDirPath.str();
else
{
/* chunk in old format => Path chunkDirPath is not set in getChunkDirChunkFilePath(),
* so it is a bit more tricky */
// we need to strip the filename itself (including the '/')
size_t startPos = 0;
size_t len = chunkFilePath.find_last_of('/');
// generate a substring as result
resStr = chunkFilePath.substr(startPos, len);
}
return resStr;
}

View File

@@ -0,0 +1,62 @@
#ifndef DATABASETK_H_
#define DATABASETK_H_
#include <common/fsck/FsckChunk.h>
#include <common/fsck/FsckContDir.h>
#include <common/fsck/FsckDirEntry.h>
#include <common/fsck/FsckDirInode.h>
#include <common/fsck/FsckFileInode.h>
#include <common/fsck/FsckFsID.h>
#include <common/fsck/FsckModificationEvent.h>
#include <common/fsck/FsckTargetID.h>
#include <database/Chunk.h>
#include <database/ContDir.h>
#include <database/DirEntry.h>
#include <database/DirInode.h>
#include <database/FileInode.h>
#include <database/FsID.h>
class FsckDB;
class DatabaseTk
{
public:
DatabaseTk();
virtual ~DatabaseTk();
static FsckDirEntry createDummyFsckDirEntry(
FsckDirEntryType entryType = FsckDirEntryType_REGULARFILE);
static void createDummyFsckDirEntries(uint amount, FsckDirEntryList* outList,
FsckDirEntryType entryType = FsckDirEntryType_REGULARFILE);
static void createDummyFsckDirEntries(uint from, uint amount, FsckDirEntryList* outList,
FsckDirEntryType entryType = FsckDirEntryType_REGULARFILE);
static FsckFileInode createDummyFsckFileInode();
static void createDummyFsckFileInodes(uint amount, FsckFileInodeList* outList);
static void createDummyFsckFileInodes(uint from, uint amount, FsckFileInodeList* outList);
static FsckDirInode createDummyFsckDirInode();
static void createDummyFsckDirInodes(uint amount, FsckDirInodeList* outList);
static void createDummyFsckDirInodes(uint from, uint amount, FsckDirInodeList* outList);
static FsckChunk createDummyFsckChunk();
static void createDummyFsckChunks(uint amount, FsckChunkList* outList);
static void createDummyFsckChunks(uint amount, uint numTargets, FsckChunkList* outList);
static void createDummyFsckChunks(uint from, uint amount, uint numTargets,
FsckChunkList* outList);
static FsckContDir createDummyFsckContDir();
static void createDummyFsckContDirs(uint amount, FsckContDirList* outList);
static void createDummyFsckContDirs(uint from, uint amount, FsckContDirList* outList);
static FsckFsID createDummyFsckFsID();
static void createDummyFsckFsIDs(uint amount, FsckFsIDList* outList);
static void createDummyFsckFsIDs(uint from, uint amount, FsckFsIDList* outList);
static std::string calculateExpectedChunkPath(std::string entryID, unsigned origParentUID,
std::string origParentEntryID, int pathInfoFlags);
};
#endif /* DATABASETK_H_ */

View File

@@ -0,0 +1,2 @@
#include "FsckDefinitions.h"

View File

@@ -0,0 +1,75 @@
#ifndef FSCKDBTYPES_H_
#define FSCKDBTYPES_H_
#include <common/Common.h>
#include <common/storage/striping/StripePattern.h>
#include <common/storage/StorageDefinitions.h>
#define TABLE_NAME_DIR_ENTRIES "dirEntries"
#define TABLE_NAME_FILE_INODES "fileInodes"
#define TABLE_NAME_DIR_INODES "dirInodes"
#define TABLE_NAME_CHUNKS "chunks"
#define TABLE_NAME_STRIPE_PATTERNS "stripePatterns"
#define TABLE_NAME_CONT_DIRS "contDirs"
#define TABLE_NAME_FSIDS "fsIDs"
#define TABLE_NAME_USED_TARGETS "usedTargets"
#define TABLE_NAME_MODIFICATION_EVENTS "modificationEvents"
enum FsckRepairAction
{
FsckRepairAction_DELETEDENTRY = 0,
FsckRepairAction_DELETEFILE = 1,
FsckRepairAction_CREATEDEFAULTDIRINODE = 2,
FsckRepairAction_CORRECTOWNER = 3,
FsckRepairAction_LOSTANDFOUND = 4,
FsckRepairAction_CREATECONTDIR = 5,
FsckRepairAction_DELETEINODE = 7,
FsckRepairAction_DELETECHUNK = 8,
FsckRepairAction_DELETECONTDIR = 9,
FsckRepairAction_UPDATEATTRIBS = 10,
FsckRepairAction_CHANGETARGET = 11,
FsckRepairAction_RECREATEFSID = 12,
FsckRepairAction_RECREATEDENTRY = 13,
FsckRepairAction_FIXPERMISSIONS = 14,
FsckRepairAction_MOVECHUNK = 15,
FsckRepairAction_REPAIRDUPLICATEINODE = 16,
FsckRepairAction_UPDATEOLDTYLEDHARDLINKS = 17,
FsckRepairAction_NOTHING = 18,
FsckRepairAction_UNDEFINED = 19
};
struct FsckRepairActionElem
{
const char* actionShortDesc;
const char* actionDesc;
enum FsckRepairAction action;
};
// Note: Keep in sync with enum FsckErrorCode
FsckRepairActionElem const __FsckRepairActions[] =
{
{"DeleteDentry", "Delete directory entry", FsckRepairAction_DELETEDENTRY},
{"DeleteFile", "Delete file", FsckRepairAction_DELETEFILE},
{"CreateDefDirInode", "Create a directory inode with default values",
FsckRepairAction_CREATEDEFAULTDIRINODE},
{"CorrectOwner", "Correct owner node", FsckRepairAction_CORRECTOWNER},
{"LostAndFound", "Link to lost+found", FsckRepairAction_LOSTANDFOUND},
{"CreateContDir", "Create an empty content directory", FsckRepairAction_CREATECONTDIR},
{"DeleteInode", "Delete inodes", FsckRepairAction_DELETEINODE},
{"DeleteChunk", "Delete chunk", FsckRepairAction_DELETECHUNK},
{"DeleteContDir", "Delete content directory", FsckRepairAction_DELETECONTDIR},
{"UpdateAttribs", "Update attributes", FsckRepairAction_UPDATEATTRIBS},
{"ChangeTarget", "Change target ID in stripe patterns", FsckRepairAction_CHANGETARGET},
{"RecreateFsID", "Recreate dentry-by-id file", FsckRepairAction_RECREATEFSID},
{"RecreateDentry", "Recreate directory entry file", FsckRepairAction_RECREATEDENTRY},
{"FixPermissions", "Fix permissions", FsckRepairAction_FIXPERMISSIONS},
{"MoveChunk", "Move chunk", FsckRepairAction_MOVECHUNK},
{"RepairDuplicateInode", "Repair duplicate inode", FsckRepairAction_REPAIRDUPLICATEINODE},
{"UpdateOldStyledHardlinks", "Update metadata of old styled hardlinks to new format",
FsckRepairAction_UPDATEOLDTYLEDHARDLINKS},
{"Nothing", "Do nothing", FsckRepairAction_NOTHING},
{0, 0, FsckRepairAction_UNDEFINED}
};
#endif /* FSCKDBTYPES_H_ */

View File

@@ -0,0 +1,16 @@
/*
* FsckException.h
*
* This exception is intended for things that can happen in fsck, that prevents it from running
* further, e.g. if a server goes down fsck should abort
*
*/
#ifndef FSCKEXCEPTION_H
#define FSCKEXCEPTION_H
#include <common/toolkit/NamedException.h>
DECLARE_NAMEDEXCEPTION(FsckException, "FsckException")
#endif /* FSCKEXCEPTION_H */

View File

@@ -0,0 +1,604 @@
#include "FsckTkEx.h"
#include <common/net/message/fsck/FsckSetEventLoggingMsg.h>
#include <common/net/message/fsck/FsckSetEventLoggingRespMsg.h>
#include <common/net/message/storage/StatStoragePathMsg.h>
#include <common/net/message/storage/StatStoragePathRespMsg.h>
#include <common/toolkit/ListTk.h>
#include <common/toolkit/FsckTk.h>
#include <common/toolkit/UnitTk.h>
#include <program/Program.h>
#include <mutex>
char FsckTkEx::progressChar = '-';
Mutex FsckTkEx::outputMutex;
/*
* check the reachability of all nodes in the system
*/
bool FsckTkEx::checkReachability()
{
NodeStore* metaNodes = Program::getApp()->getMetaNodes();
NodeStore* storageNodes = Program::getApp()->getStorageNodes();
StringList errors;
bool commSuccess = true;
FsckTkEx::fsckOutput("Step 1: Check reachability of nodes: ", OutputOptions_FLUSH);
if ( metaNodes->getSize() == 0 )
{
errors.push_back("No metadata nodes found");
commSuccess = false;
}
if ( storageNodes->getSize() == 0 )
{
errors.push_back("No storage nodes found");
commSuccess = false;
}
for (const auto& node : metaNodes->referenceAllNodes())
{
if ( !FsckTkEx::checkReachability(*node, NODETYPE_Meta) )
{
errors.push_back("Communication with metadata node failed: " + node->getAlias());
commSuccess = false;
}
}
for (const auto& node : storageNodes->referenceAllNodes())
{
if ( !FsckTkEx::checkReachability(*node, NODETYPE_Storage) )
{
errors.push_back("Communication with storage node failed: " + node->getAlias());
commSuccess = false;
}
}
if ( commSuccess )
FsckTkEx::fsckOutput("Finished", OutputOptions_LINEBREAK);
else
{
for ( StringListIter iter = errors.begin(); iter != errors.end(); iter++ )
{
FsckTkEx::fsckOutput(*iter,
OutputOptions_NONE | OutputOptions_LINEBREAK | OutputOptions_STDERR);
}
}
return commSuccess;
}
/*
* check reachability of a single node
*/
bool FsckTkEx::checkReachability(Node& node, NodeType nodetype)
{
bool retVal = false;
HeartbeatRequestMsg heartbeatRequestMsg;
std::string realNodeID = node.getAlias();
const auto respMsg = MessagingTk::requestResponse(node, heartbeatRequestMsg,
NETMSGTYPE_Heartbeat);
if (respMsg)
{
HeartbeatMsg *heartbeatMsg = (HeartbeatMsg *) respMsg.get();
std::string receivedNodeID = heartbeatMsg->getNodeID();
retVal = receivedNodeID.compare(realNodeID) == 0;
}
return retVal;
}
void FsckTkEx::fsckOutput(std::string text, int optionFlags)
{
const std::lock_guard<Mutex> lock(outputMutex);
static bool fileErrLogged = false; // to make sure we print logfile open err only once
Config* cfg = Program::getApp()->getConfig(); // might be NULL on app init failure
bool toLog = cfg && (!(OutputOptions_NOLOG & optionFlags)); // true if write to log file
FILE *logFile = NULL;
if (likely(toLog))
{
std::string logFilePath = cfg->getLogOutFile();
logFile = fopen(logFilePath.c_str(),"a+");
if (logFile == NULL)
{
toLog = false;
if(!fileErrLogged)
{
std::cerr << "Cannot open output file for writing: '" << logFilePath << "'"
<< std::endl;
fileErrLogged = true;
}
}
}
const char* colorNormal = OutputColor_NORMAL;
const char* color = OutputColor_NORMAL;
FILE *outFile = stdout;
if (OutputOptions_STDERR & optionFlags)
{
outFile = stderr;
}
else if (OutputOptions_NOSTDOUT & optionFlags)
{
outFile = NULL;
}
bool outFileIsTty;
if (outFile)
outFileIsTty = isatty(fileno(outFile));
else
outFileIsTty = false;
if (OutputOptions_COLORGREEN & optionFlags)
{
color = OutputColor_GREEN;
}
else if (OutputOptions_COLORRED & optionFlags)
{
color = OutputColor_RED;
}
else
{
color = OutputColor_NORMAL;
}
if (OutputOptions_LINEDELETE & optionFlags)
{
optionFlags = optionFlags | OutputOptions_FLUSH;
SAFE_FPRINTF(outFile, "\r");
SAFE_FPRINTF(outFile, " ");
SAFE_FPRINTF(outFile, "\r");
}
if (OutputOptions_ADDLINEBREAKBEFORE & optionFlags)
{
SAFE_FPRINTF(outFile, "\n");
if (likely(toLog)) SAFE_FPRINTF(logFile,"\n");
}
if (OutputOptions_HEADLINE & optionFlags)
{
SAFE_FPRINTF(outFile, "\n--------------------------------------------------------------------\n");
if (likely(toLog))
SAFE_FPRINTF(logFile,"\n--------------------------------------------------------------------\n");
if (likely(outFileIsTty))
SAFE_FPRINTF(outFile, "%s%s%s",color,text.c_str(),colorNormal);
else
SAFE_FPRINTF(outFile, "%s",text.c_str());
if (likely(toLog))
SAFE_FPRINTF(logFile,"%s",text.c_str());
SAFE_FPRINTF(outFile, "\n--------------------------------------------------------------------\n") ;
if (likely(toLog))
SAFE_FPRINTF(logFile,"\n--------------------------------------------------------------------\n");
}
else
{
if (likely(outFileIsTty))
SAFE_FPRINTF(outFile, "%s%s%s",color,text.c_str(),colorNormal);
else
SAFE_FPRINTF(outFile, "%s",text.c_str());
if (likely(toLog)) SAFE_FPRINTF(logFile,"%s",text.c_str());
}
if (OutputOptions_LINEBREAK & optionFlags)
{
SAFE_FPRINTF(outFile, "\n");
if (likely(toLog))
SAFE_FPRINTF(logFile,"\n");
}
if (OutputOptions_DOUBLELINEBREAK & optionFlags)
{
SAFE_FPRINTF(outFile, "\n\n");
if (likely(toLog))
SAFE_FPRINTF(logFile,"\n\n");
}
if (OutputOptions_FLUSH & optionFlags)
{
fflush(outFile);
if (likely(toLog))
fflush(logFile);
}
if (logFile != NULL)
{
fclose(logFile);
}
}
void FsckTkEx::printVersionHeader(bool toStdErr, bool noLogFile)
{
int optionFlags = OutputOptions_LINEBREAK;
if (toStdErr)
{
optionFlags = OutputOptions_LINEBREAK | OutputOptions_STDERR;
}
if (noLogFile)
{
optionFlags = optionFlags | OutputOptions_NOLOG;
}
FsckTkEx::fsckOutput("\n", optionFlags);
FsckTkEx::fsckOutput("BeeGFS File System Check Version : " + std::string(BEEGFS_VERSION),
optionFlags);
FsckTkEx::fsckOutput("----", optionFlags);
}
void FsckTkEx::progressMeter()
{
const std::lock_guard<Mutex> lock(outputMutex);
printf("\b%c",progressChar);
fflush(stdout);
switch(progressChar)
{
case '-' :
{
progressChar = '\\';
break;
}
case '\\' :
{
progressChar = '|';
break;
}
case '|' :
{
progressChar = '/';
break;
}
case '/' :
{
progressChar = '-';
break;
}
default:
{
progressChar = '-';
break;
}
}
}
/*
* this is only a rough approximation
*/
int64_t FsckTkEx::calcNeededSpace()
{
const char* logContext = "FsckTkEx (calcNeededSpace)";
int64_t neededSpace = 0;
// get used inodes from all meta data servers and sum them up
NodeStore* metaNodes = Program::getApp()->getMetaNodes();
for (const auto& metaNode : metaNodes->referenceAllNodes())
{
NumNodeID nodeID = metaNode->getNumID();
StatStoragePathMsg statStoragePathMsg(0);
const auto respMsg = MessagingTk::requestResponse(*metaNode, statStoragePathMsg,
NETMSGTYPE_StatStoragePathResp);
if (respMsg)
{
StatStoragePathRespMsg* statStoragePathRespMsg = (StatStoragePathRespMsg *) respMsg.get();
int64_t usedInodes = statStoragePathRespMsg->getInodesTotal()
- statStoragePathRespMsg->getInodesFree();
neededSpace += usedInodes * NEEDED_DISKSPACE_META_INODE;
}
else
{
LogContext(logContext).logErr(
"Unable to calculate needed disk space; Communication error with node: "
+ nodeID.str());
return 0;
}
}
// get used inodes from all storage servers and sum them up
NodeStore* storageNodes = Program::getApp()->getStorageNodes();
TargetMapper* targetMapper = Program::getApp()->getTargetMapper();
for (const auto& storageNode : storageNodes->referenceAllNodes())
{
NumNodeID nodeID = storageNode->getNumID();
UInt16List targetIDs;
targetMapper->getTargetsByNode(nodeID, targetIDs);
for ( UInt16ListIter targetIDIter = targetIDs.begin(); targetIDIter != targetIDs.end();
targetIDIter++ )
{
uint16_t targetID = *targetIDIter;
StatStoragePathMsg statStoragePathMsg(targetID);
const auto respMsg = MessagingTk::requestResponse(*storageNode, statStoragePathMsg,
NETMSGTYPE_StatStoragePathResp);
if (respMsg)
{
auto* statStoragePathRespMsg = (StatStoragePathRespMsg *) respMsg.get();
int64_t usedInodes = statStoragePathRespMsg->getInodesTotal()
- statStoragePathRespMsg->getInodesFree();
neededSpace += usedInodes * NEEDED_DISKSPACE_STORAGE_INODE;
}
else
{
LogContext(logContext).logErr(
"Unable to calculate needed disk space; Communication error with node: "
+ nodeID.str());
return -1;
}
}
}
// now we take the calculated approximation and double it to have a lot of space for errors
return neededSpace*2;
}
bool FsckTkEx::checkDiskSpace(Path& dbPath)
{
int64_t neededDiskSpace = FsckTkEx::calcNeededSpace();
if ( unlikely(neededDiskSpace < 0) )
{
FsckTkEx::fsckOutput("Could not determine needed disk space. Aborting now.",
OutputOptions_LINEBREAK | OutputOptions_ADDLINEBREAKBEFORE);
return false;
}
int64_t sizeTotal;
int64_t sizeFree;
int64_t inodesTotal;
int64_t inodesFree;
bool statRes = StorageTk::statStoragePath(dbPath, true, &sizeTotal, &sizeFree,
&inodesTotal, &inodesFree);
if (!statRes)
{
FsckTkEx::fsckOutput(
"Could not stat database file path to determine free space; database file: "
+ dbPath.str());
return false;
}
if ( neededDiskSpace >= sizeFree )
{
std::string neededDiskSpaceUnit;
double neededDiskSpaceValue = UnitTk::byteToXbyte(neededDiskSpace, &neededDiskSpaceUnit);
std::string sizeFreeUnit;
double sizeFreeValue = UnitTk::byteToXbyte(sizeFree, &sizeFreeUnit);
FsckTkEx::fsckOutput(
"Not enough disk space to create database file: " + dbPath.str()
+ "; Recommended free space: " + StringTk::doubleToStr(neededDiskSpaceValue)
+ neededDiskSpaceUnit + "; Free space: " + StringTk::doubleToStr(sizeFreeValue)
+ sizeFreeUnit, OutputOptions_LINEBREAK | OutputOptions_ADDLINEBREAKBEFORE);
bool ignoreDBDiskSpace = Program::getApp()->getConfig()->getIgnoreDBDiskSpace();
if(!ignoreDBDiskSpace)
return false;
}
return true;
}
std::string FsckTkEx::getRepairActionDesc(FsckRepairAction repairAction, bool shortDesc)
{
for (size_t i = 0; __FsckRepairActions[i].actionDesc != nullptr; i++)
{
if( repairAction == __FsckRepairActions[i].action )
{ // we have a match
if (shortDesc)
return __FsckRepairActions[i].actionShortDesc;
else
return __FsckRepairActions[i].actionDesc;
}
}
return "";
}
FhgfsOpsErr FsckTkEx::startModificationLogging(NodeStore* metaNodes, Node& localNode,
bool forceRestart)
{
const char* logContext = "FsckTkEx (startModificationLogging)";
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
NicAddressList localNicList = localNode.getNicList();
unsigned localPortUDP = localNode.getPortUDP();
FsckTkEx::fsckOutput("-----",
OutputOptions_ADDLINEBREAKBEFORE | OutputOptions_FLUSH | OutputOptions_LINEBREAK);
FsckTkEx::fsckOutput(
"Waiting for metadata servers to start modification logging. This might take some time.",
OutputOptions_FLUSH | OutputOptions_LINEBREAK);
FsckTkEx::fsckOutput("-----", OutputOptions_FLUSH | OutputOptions_DOUBLELINEBREAK);
NumNodeIDList nodeIDs;
auto metaNodeList = metaNodes->referenceAllNodes();
for (auto iter = metaNodeList.begin(); iter != metaNodeList.end(); iter++)
{
auto node = metaNodes->referenceNode((*iter)->getNumID());
NicAddressList nicList;
FsckSetEventLoggingMsg fsckSetEventLoggingMsg(true, localPortUDP,
&localNicList, forceRestart);
const auto respMsg = MessagingTk::requestResponse(*node, fsckSetEventLoggingMsg,
NETMSGTYPE_FsckSetEventLoggingResp);
if (respMsg)
{
auto* fsckSetEventLoggingRespMsg = (FsckSetEventLoggingRespMsg*) respMsg.get();
bool started = fsckSetEventLoggingRespMsg->getLoggingEnabled();
if (!started) // EventFlusher was already running on this node!
{
LogContext(logContext).logErr("Modification logging already running on node: "
+ node->getAlias());
retVal = FhgfsOpsErr_INUSE;
break;
}
}
else
{
LogContext(logContext).logErr("Communication error occured with node: " + node->getAlias());
retVal = FhgfsOpsErr_COMMUNICATION;
}
}
return retVal;
}
bool FsckTkEx::stopModificationLogging(NodeStore* metaNodes)
{
const char* logContext = "FsckTkEx (stopModificationLogging)";
bool retVal = true;
FsckTkEx::fsckOutput("-----",
OutputOptions_ADDLINEBREAKBEFORE | OutputOptions_FLUSH | OutputOptions_LINEBREAK);
FsckTkEx::fsckOutput(
"Waiting for metadata servers to stop modification logging. This might take some time.",
OutputOptions_FLUSH | OutputOptions_LINEBREAK);
FsckTkEx::fsckOutput("-----", OutputOptions_FLUSH | OutputOptions_DOUBLELINEBREAK);
NumNodeIDList nodeIDs;
auto metaNodeList = metaNodes->referenceAllNodes();
for (auto iter = metaNodeList.begin(); iter != metaNodeList.end(); iter++)
nodeIDs.push_back((*iter)->getNumID());
NumNodeIDListIter nodeIdIter = nodeIDs.begin();
while (! nodeIDs.empty())
{
NumNodeID nodeID = *nodeIdIter;
auto node = metaNodes->referenceNode(nodeID);
NicAddressList nicList;
FsckSetEventLoggingMsg fsckSetEventLoggingMsg(false, 0, &nicList, false);
const auto respMsg = MessagingTk::requestResponse(*node, fsckSetEventLoggingMsg,
NETMSGTYPE_FsckSetEventLoggingResp);
if (respMsg)
{
auto* fsckSetEventLoggingRespMsg = (FsckSetEventLoggingRespMsg*) respMsg.get();
bool result = fsckSetEventLoggingRespMsg->getResult();
bool missedEvents = fsckSetEventLoggingRespMsg->getMissedEvents();
if ( result )
{
nodeIdIter = nodeIDs.erase(nodeIdIter);
if ( missedEvents )
{
retVal = false;
}
}
else
nodeIdIter++; // keep in list and try again later
}
else
{
LogContext(logContext).logErr("Communication error occurred with node: " + node->getAlias());
retVal = false;
}
if (nodeIdIter == nodeIDs.end())
{
nodeIdIter = nodeIDs.begin();
sleep(5);
}
}
return retVal;
}
bool FsckTkEx::checkConsistencyStates()
{
auto mgmtdNode = Program::getApp()->getMgmtNodes()->referenceFirstNode();
if (!mgmtdNode)
throw std::runtime_error("Management node not found");
std::list<uint8_t> storageReachabilityStates;
std::list<uint8_t> storageConsistencyStates;
std::list<uint16_t> storageTargetIDs;
std::list<uint8_t> metaReachabilityStates;
std::list<uint8_t> metaConsistencyStates;
std::list<uint16_t> metaTargetIDs;
if (!NodesTk::downloadTargetStates(*mgmtdNode, NODETYPE_Storage, &storageTargetIDs,
&storageReachabilityStates, &storageConsistencyStates, false)
|| !NodesTk::downloadTargetStates(*mgmtdNode, NODETYPE_Meta, &metaTargetIDs,
&metaReachabilityStates, &metaConsistencyStates, false))
{
throw std::runtime_error("Could not download target states from management.");
}
bool result = true;
{
auto idIt = storageTargetIDs.begin();
auto stateIt = storageConsistencyStates.begin();
for (; idIt != storageTargetIDs.end() && stateIt != storageConsistencyStates.end();
idIt++, stateIt++)
{
if (*stateIt == TargetConsistencyState_NEEDS_RESYNC)
{
FsckTkEx::fsckOutput("Storage target " + StringTk::uintToStr(*idIt) + " is set to "
"NEEDS_RESYNC.", OutputOptions_LINEBREAK);
result = false;
}
}
}
{
auto idIt = metaTargetIDs.begin();
auto stateIt = metaConsistencyStates.begin();
for (; idIt != metaTargetIDs.end() && stateIt != metaConsistencyStates.end();
idIt++, stateIt++)
{
if (*stateIt == TargetConsistencyState_NEEDS_RESYNC)
{
FsckTkEx::fsckOutput("Meta node " + StringTk::uintToStr(*idIt) + " is set to "
"NEEDS_RESYNC.", OutputOptions_LINEBREAK);
result = false;
}
}
}
return result;
}

View File

@@ -0,0 +1,103 @@
#ifndef FSCKTKEX_H_
#define FSCKTKEX_H_
#include <common/app/log/LogContext.h>
#include <common/fsck/FsckDirEntry.h>
#include <common/fsck/FsckChunk.h>
#include <common/fsck/FsckContDir.h>
#include <common/fsck/FsckDirEntry.h>
#include <common/fsck/FsckDirInode.h>
#include <common/fsck/FsckFileInode.h>
#include <common/fsck/FsckFsID.h>
#include <common/net/message/nodes/HeartbeatRequestMsg.h>
#include <common/net/message/nodes/HeartbeatMsg.h>
#include <common/nodes/NodeStore.h>
#include <common/threading/PThread.h>
#include <common/toolkit/NodesTk.h>
#include <common/toolkit/MessagingTk.h>
#include <toolkit/FsckDefinitions.h>
/* OutputOption Flags for fsckOutput */
#define OutputOptions_NONE 0
#define OutputOptions_LINEBREAK 1
#define OutputOptions_DOUBLELINEBREAK 1 << 1
#define OutputOptions_HEADLINE 1 << 2
#define OutputOptions_FLUSH 1 << 3
#define OutputOptions_ADDLINEBREAKBEFORE 1 << 4
#define OutputOptions_COLORGREEN 1 << 5
#define OutputOptions_COLORRED 1 << 6
#define OutputOptions_LINEDELETE 1 << 7
#define OutputOptions_NOSTDOUT 1 << 8
#define OutputOptions_NOLOG 1 << 9
#define OutputOptions_STDERR 1 << 10
#define OutputColor_NORMAL "\033[0m";
#define OutputColor_GREEN "\033[32m";
#define OutputColor_RED "\033[31m";
#define SAFE_FPRINTF(stream, fmt, args...) \
do{ if(stream) {fprintf(stream, fmt, ##args);} } while(0)
/*
* calculating with:
* DirEntry 76+256+28 Byte (space for dentry + longest name + one index)
* FileInode 96 Byte |
* DirInode 56 Byte # only the larger of these two counts, even though files are inlined
* ContDir 16 Byte
* FsID 40 Byte
* chunk 88 Byte
*
*/
#define NEEDED_DISKSPACE_META_INODE 512
#define NEEDED_DISKSPACE_STORAGE_INODE 88
class FsckTkEx
{
public:
// check the reachability of all nodes
static bool checkReachability();
// check the reachability of a given node by sending a heartbeat message
static bool checkReachability(Node& node, NodeType nodetype);
/*
* a formatted output for fsck
*
* @param text The text to be printed
* @param optionFlags OutputOptions_... flags (mainly for formatiing)
*/
static void fsckOutput(std::string text, int optionFlags);
// just print a formatted header with the version to the console
static void printVersionHeader(bool toStdErr = false, bool noLogFile = false);
// print the progress meter which goes round and round (-\|/-)
static void progressMeter();
static int64_t calcNeededSpace();
static bool checkDiskSpace(Path& dbPath);
static std::string getRepairActionDesc(FsckRepairAction repairAction, bool shortDesc = false);
static FhgfsOpsErr startModificationLogging(NodeStore* metaNodes, Node& localNode,
bool forceRestart);
static bool stopModificationLogging(NodeStore* metaNodes);
static bool checkConsistencyStates();
private:
FsckTkEx() {}
// saves the last char output by the progress meter
static char progressChar;
// a mutex that is locked by output functions to make sure the output does not get messed up
// by two threads doing output at the same time
static Mutex outputMutex;
public:
// inliners
static void fsckOutput(std::string text)
{
fsckOutput(text, OutputOptions_LINEBREAK);
}
};
#endif /* FSCKTKEX_H_ */