New upstream version 8.1.0
This commit is contained in:
153
fsck/CMakeLists.txt
Normal file
153
fsck/CMakeLists.txt
Normal file
@@ -0,0 +1,153 @@
|
||||
include_directories(
|
||||
source
|
||||
)
|
||||
|
||||
add_library(
|
||||
fsck STATIC
|
||||
./source/toolkit/DatabaseTk.cpp
|
||||
./source/toolkit/FsckDefinitions.h
|
||||
./source/toolkit/FsckException.h
|
||||
./source/toolkit/FsckDefinitions.cpp
|
||||
./source/toolkit/FsckTkEx.cpp
|
||||
./source/toolkit/DatabaseTk.h
|
||||
./source/toolkit/FsckTkEx.h
|
||||
./source/database/VectorSource.h
|
||||
./source/database/DirInode.h
|
||||
./source/database/FsckDBTable.cpp
|
||||
./source/database/Select.h
|
||||
./source/database/EntryID.h
|
||||
./source/database/Union.h
|
||||
./source/database/SetFragmentCursor.h
|
||||
./source/database/FsID.h
|
||||
./source/database/Set.h
|
||||
./source/database/FsckDB.cpp
|
||||
./source/database/SetFragment.h
|
||||
./source/database/Group.h
|
||||
./source/database/FsckDB.h
|
||||
./source/database/FsckDBChecks.cpp
|
||||
./source/database/Table.h
|
||||
./source/database/Cursor.h
|
||||
./source/database/UsedTarget.h
|
||||
./source/database/FsckDBException.cpp
|
||||
./source/database/FileInode.h
|
||||
./source/database/ModificationEvent.h
|
||||
./source/database/LeftJoinEq.h
|
||||
./source/database/Filter.h
|
||||
./source/database/FsckDBException.h
|
||||
./source/database/DirEntry.h
|
||||
./source/database/Chunk.h
|
||||
./source/database/FsckDBTable.h
|
||||
./source/database/ContDir.h
|
||||
./source/database/StripeTargets.h
|
||||
./source/database/Buffer.h
|
||||
./source/database/Distinct.h
|
||||
./source/database/DiskList.h
|
||||
./source/net/message/NetMessageFactory.h
|
||||
./source/net/message/NetMessageFactory.cpp
|
||||
./source/net/message/nodes/HeartbeatMsgEx.cpp
|
||||
./source/net/message/nodes/HeartbeatMsgEx.h
|
||||
./source/net/message/testing/DummyMsgEx.h
|
||||
./source/net/message/testing/DummyMsgEx.cpp
|
||||
./source/net/message/fsck/FsckModificationEventMsgEx.cpp
|
||||
./source/net/message/fsck/FsckModificationEventMsgEx.h
|
||||
./source/net/msghelpers/MsgHelperRepair.h
|
||||
./source/net/msghelpers/MsgHelperRepair.cpp
|
||||
./source/components/DatagramListener.h
|
||||
./source/components/ModificationEventHandler.h
|
||||
./source/components/InternodeSyncer.h
|
||||
./source/components/InternodeSyncer.cpp
|
||||
./source/components/DataFetcher.cpp
|
||||
./source/components/ModificationEventHandler.cpp
|
||||
./source/components/DatagramListener.cpp
|
||||
./source/components/worker/RetrieveDirEntriesWork.h
|
||||
./source/components/worker/RetrieveInodesWork.h
|
||||
./source/components/worker/AdjustChunkPermissionsWork.h
|
||||
./source/components/worker/AdjustChunkPermissionsWork.cpp
|
||||
./source/components/worker/RetrieveFsIDsWork.cpp
|
||||
./source/components/worker/RetrieveChunksWork.cpp
|
||||
./source/components/worker/RetrieveChunksWork.h
|
||||
./source/components/worker/RetrieveInodesWork.cpp
|
||||
./source/components/worker/RetrieveFsIDsWork.h
|
||||
./source/components/worker/RetrieveDirEntriesWork.cpp
|
||||
./source/components/DataFetcher.h
|
||||
./source/modes/ModeHelp.cpp
|
||||
./source/modes/Mode.h
|
||||
./source/modes/ModeCheckFS.cpp
|
||||
./source/modes/Mode.cpp
|
||||
./source/modes/ModeEnableQuota.cpp
|
||||
./source/modes/ModeHelp.h
|
||||
./source/modes/ModeEnableQuota.h
|
||||
./source/modes/ModeCheckFS.h
|
||||
./source/program/Program.h
|
||||
./source/program/Program.cpp
|
||||
./source/program/Main.cpp
|
||||
./source/app/App.h
|
||||
./source/app/App.cpp
|
||||
./source/app/config/Config.h
|
||||
./source/app/config/Config.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
fsck
|
||||
beegfs-common
|
||||
dl
|
||||
pthread
|
||||
)
|
||||
|
||||
add_executable(
|
||||
fsck.beegfs
|
||||
source/program/Main.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
fsck.beegfs
|
||||
fsck
|
||||
)
|
||||
|
||||
if(NOT BEEGFS_SKIP_TESTS)
|
||||
# This is a dirty workaround used for a hardcoded path in
|
||||
# "fsck/tests/TestConfig.cpp". Once the "defaultConfigFile" test does
|
||||
# not steal the config file from "client_module" anymore, this
|
||||
# workaround can be removed.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DTHIS_IS_A_CMAKE_BUILD=1")
|
||||
|
||||
add_executable(
|
||||
test-fsck
|
||||
./tests/TestFsckTk.cpp
|
||||
./tests/TestDatabase.cpp
|
||||
./tests/TestConfig.h
|
||||
./tests/TestSet.cpp
|
||||
./tests/TestCursors.cpp
|
||||
./tests/TestTable.cpp
|
||||
./tests/FlatTest.h
|
||||
./tests/TestSetFragment.cpp
|
||||
./tests/TestSerialization.cpp
|
||||
./tests/FlatTest.cpp
|
||||
./tests/TestTable.h
|
||||
./tests/TestConfig.cpp
|
||||
./tests/TestDatabase.h
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
test-fsck
|
||||
fsck
|
||||
gtest_main
|
||||
)
|
||||
|
||||
# required for a test
|
||||
file(
|
||||
COPY ${CMAKE_CURRENT_SOURCE_DIR}/../client_module/build/dist/etc/beegfs-client.conf
|
||||
DESTINATION dist/etc/
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME test-fsck
|
||||
COMMAND test-fsck --compiler
|
||||
)
|
||||
endif()
|
||||
|
||||
install(
|
||||
TARGETS fsck.beegfs
|
||||
DESTINATION "sbin"
|
||||
COMPONENT "utils"
|
||||
)
|
||||
28
fsck/build/Makefile
Executable file
28
fsck/build/Makefile
Executable file
@@ -0,0 +1,28 @@
|
||||
include ../../build/Makefile
|
||||
|
||||
main := ../source/program/Main.cpp
|
||||
sources := $(filter-out $(main), $(shell find ../source -iname '*.cpp'))
|
||||
|
||||
$(call build-static-library,\
|
||||
Fsck,\
|
||||
$(sources),\
|
||||
common dl nl3-route,\
|
||||
../source)
|
||||
|
||||
$(call define-dep-lib,\
|
||||
Fsck,\
|
||||
-I ../source,\
|
||||
$(build_dir)/libFsck.a)
|
||||
|
||||
$(call build-executable,\
|
||||
beegfs-fsck,\
|
||||
$(main),\
|
||||
Fsck common dl nl3-route)
|
||||
|
||||
$(call build-test,\
|
||||
test-runner,\
|
||||
$(shell find ../tests -name '*.cpp'),\
|
||||
Fsck common dl nl3-route,\
|
||||
../tests)
|
||||
|
||||
CXXFLAGS += -DBOOST_RESULT_OF_USE_DECLTYPE
|
||||
562
fsck/source/app/App.cpp
Normal file
562
fsck/source/app/App.cpp
Normal 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, ®DGramLis, 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
223
fsck/source/app/App.h
Normal 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_
|
||||
317
fsck/source/app/config/Config.cpp
Normal file
317
fsck/source/app/config/Config.cpp
Normal 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
|
||||
}
|
||||
|
||||
224
fsck/source/app/config/Config.h
Normal file
224
fsck/source/app/config/Config.h
Normal 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_*/
|
||||
203
fsck/source/components/DataFetcher.cpp
Normal file
203
fsck/source/components/DataFetcher.cpp
Normal 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);
|
||||
}
|
||||
43
fsck/source/components/DataFetcher.h
Normal file
43
fsck/source/components/DataFetcher.h
Normal 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_ */
|
||||
53
fsck/source/components/DatagramListener.cpp
Normal file
53
fsck/source/components/DatagramListener.cpp
Normal 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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
22
fsck/source/components/DatagramListener.h
Normal file
22
fsck/source/components/DatagramListener.h
Normal 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_*/
|
||||
491
fsck/source/components/InternodeSyncer.cpp
Normal file
491
fsck/source/components/InternodeSyncer.cpp
Normal 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;
|
||||
}
|
||||
95
fsck/source/components/InternodeSyncer.h
Normal file
95
fsck/source/components/InternodeSyncer.h
Normal 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_ */
|
||||
98
fsck/source/components/ModificationEventHandler.cpp
Normal file
98
fsck/source/components/ModificationEventHandler.cpp
Normal 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;
|
||||
}
|
||||
44
fsck/source/components/ModificationEventHandler.h
Normal file
44
fsck/source/components/ModificationEventHandler.h
Normal 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 */
|
||||
97
fsck/source/components/worker/AdjustChunkPermissionsWork.cpp
Normal file
97
fsck/source/components/worker/AdjustChunkPermissionsWork.cpp
Normal 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
32
fsck/source/components/worker/AdjustChunkPermissionsWork.h
Normal file
32
fsck/source/components/worker/AdjustChunkPermissionsWork.h
Normal 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 */
|
||||
152
fsck/source/components/worker/RetrieveChunksWork.cpp
Normal file
152
fsck/source/components/worker/RetrieveChunksWork.cpp
Normal 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();
|
||||
}
|
||||
57
fsck/source/components/worker/RetrieveChunksWork.h
Normal file
57
fsck/source/components/worker/RetrieveChunksWork.h
Normal 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 */
|
||||
222
fsck/source/components/worker/RetrieveDirEntriesWork.cpp
Normal file
222
fsck/source/components/worker/RetrieveDirEntriesWork.cpp
Normal 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
62
fsck/source/components/worker/RetrieveDirEntriesWork.h
Normal file
62
fsck/source/components/worker/RetrieveDirEntriesWork.h
Normal 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 */
|
||||
125
fsck/source/components/worker/RetrieveFsIDsWork.cpp
Normal file
125
fsck/source/components/worker/RetrieveFsIDsWork.cpp
Normal 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
49
fsck/source/components/worker/RetrieveFsIDsWork.h
Normal file
49
fsck/source/components/worker/RetrieveFsIDsWork.h
Normal 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 */
|
||||
187
fsck/source/components/worker/RetrieveInodesWork.cpp
Normal file
187
fsck/source/components/worker/RetrieveInodesWork.cpp
Normal 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
58
fsck/source/components/worker/RetrieveInodesWork.h
Normal file
58
fsck/source/components/worker/RetrieveInodesWork.h
Normal 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 */
|
||||
47
fsck/source/database/Buffer.h
Normal file
47
fsck/source/database/Buffer.h
Normal 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
|
||||
55
fsck/source/database/Chunk.h
Normal file
55
fsck/source/database/Chunk.h
Normal 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
|
||||
26
fsck/source/database/ContDir.h
Normal file
26
fsck/source/database/ContDir.h
Normal 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
|
||||
67
fsck/source/database/Cursor.h
Normal file
67
fsck/source/database/Cursor.h
Normal 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
|
||||
67
fsck/source/database/DirEntry.h
Normal file
67
fsck/source/database/DirEntry.h
Normal 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
|
||||
40
fsck/source/database/DirInode.h
Normal file
40
fsck/source/database/DirInode.h
Normal 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
|
||||
149
fsck/source/database/DiskList.h
Normal file
149
fsck/source/database/DiskList.h
Normal 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
|
||||
88
fsck/source/database/Distinct.h
Normal file
88
fsck/source/database/Distinct.h
Normal 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
|
||||
163
fsck/source/database/EntryID.h
Normal file
163
fsck/source/database/EntryID.h
Normal 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
|
||||
99
fsck/source/database/FileInode.h
Normal file
99
fsck/source/database/FileInode.h
Normal 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
|
||||
72
fsck/source/database/Filter.h
Normal file
72
fsck/source/database/Filter.h
Normal 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
|
||||
38
fsck/source/database/FsID.h
Normal file
38
fsck/source/database/FsID.h
Normal 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
|
||||
40
fsck/source/database/FsckDB.cpp
Normal file
40
fsck/source/database/FsckDB.cpp
Normal 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();
|
||||
}
|
||||
178
fsck/source/database/FsckDB.h
Normal file
178
fsck/source/database/FsckDB.h
Normal 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_ */
|
||||
1419
fsck/source/database/FsckDBChecks.cpp
Normal file
1419
fsck/source/database/FsckDBChecks.cpp
Normal file
File diff suppressed because it is too large
Load Diff
2
fsck/source/database/FsckDBException.cpp
Normal file
2
fsck/source/database/FsckDBException.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#include "FsckDBException.h"
|
||||
|
||||
8
fsck/source/database/FsckDBException.h
Normal file
8
fsck/source/database/FsckDBException.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef FSCKDBEXCEPTION_H
|
||||
#define FSCKDBEXCEPTION_H
|
||||
|
||||
#include <common/toolkit/NamedException.h>
|
||||
|
||||
DECLARE_NAMEDEXCEPTION(FsckDBException, "FsckDBException")
|
||||
|
||||
#endif /*FSCKDBEXCEPTION_H*/
|
||||
509
fsck/source/database/FsckDBTable.cpp
Normal file
509
fsck/source/database/FsckDBTable.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
641
fsck/source/database/FsckDBTable.h
Normal file
641
fsck/source/database/FsckDBTable.h
Normal 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
|
||||
131
fsck/source/database/Group.h
Normal file
131
fsck/source/database/Group.h
Normal 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 ¤t;
|
||||
}
|
||||
|
||||
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
|
||||
189
fsck/source/database/LeftJoinEq.h
Normal file
189
fsck/source/database/LeftJoinEq.h
Normal 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 ¤t;
|
||||
}
|
||||
|
||||
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
|
||||
18
fsck/source/database/ModificationEvent.h
Normal file
18
fsck/source/database/ModificationEvent.h
Normal 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
|
||||
74
fsck/source/database/Select.h
Normal file
74
fsck/source/database/Select.h
Normal 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 ¤t;
|
||||
}
|
||||
|
||||
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
380
fsck/source/database/Set.h
Normal 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
|
||||
325
fsck/source/database/SetFragment.h
Normal file
325
fsck/source/database/SetFragment.h
Normal 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
|
||||
47
fsck/source/database/SetFragmentCursor.h
Normal file
47
fsck/source/database/SetFragmentCursor.h
Normal 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
|
||||
45
fsck/source/database/StripeTargets.h
Normal file
45
fsck/source/database/StripeTargets.h
Normal 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
|
||||
430
fsck/source/database/Table.h
Normal file
430
fsck/source/database/Table.h
Normal 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
|
||||
130
fsck/source/database/Union.h
Normal file
130
fsck/source/database/Union.h
Normal 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
|
||||
25
fsck/source/database/UsedTarget.h
Normal file
25
fsck/source/database/UsedTarget.h
Normal 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
|
||||
52
fsck/source/database/VectorSource.h
Normal file
52
fsck/source/database/VectorSource.h
Normal 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
|
||||
23
fsck/source/modes/Mode.cpp
Normal file
23
fsck/source/modes/Mode.cpp
Normal 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
24
fsck/source/modes/Mode.h
Normal 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_*/
|
||||
2008
fsck/source/modes/ModeCheckFS.cpp
Normal file
2008
fsck/source/modes/ModeCheckFS.cpp
Normal file
File diff suppressed because it is too large
Load Diff
170
fsck/source/modes/ModeCheckFS.h
Normal file
170
fsck/source/modes/ModeCheckFS.h
Normal 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 */
|
||||
116
fsck/source/modes/ModeEnableQuota.cpp
Normal file
116
fsck/source/modes/ModeEnableQuota.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
27
fsck/source/modes/ModeEnableQuota.h
Normal file
27
fsck/source/modes/ModeEnableQuota.h
Normal 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 */
|
||||
99
fsck/source/modes/ModeHelp.cpp
Normal file
99
fsck/source/modes/ModeHelp.cpp
Normal 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;
|
||||
}
|
||||
24
fsck/source/modes/ModeHelp.h
Normal file
24
fsck/source/modes/ModeHelp.h
Normal 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_*/
|
||||
125
fsck/source/net/message/NetMessageFactory.cpp
Normal file
125
fsck/source/net/message/NetMessageFactory.cpp
Normal 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);
|
||||
}
|
||||
|
||||
16
fsck/source/net/message/NetMessageFactory.h
Normal file
16
fsck/source/net/message/NetMessageFactory.h
Normal 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_*/
|
||||
26
fsck/source/net/message/fsck/FsckModificationEventMsgEx.cpp
Normal file
26
fsck/source/net/message/fsck/FsckModificationEventMsgEx.cpp
Normal 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;
|
||||
}
|
||||
12
fsck/source/net/message/fsck/FsckModificationEventMsgEx.h
Normal file
12
fsck/source/net/message/fsck/FsckModificationEventMsgEx.h
Normal 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*/
|
||||
102
fsck/source/net/message/nodes/HeartbeatMsgEx.cpp
Normal file
102
fsck/source/net/message/nodes/HeartbeatMsgEx.cpp
Normal 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() );
|
||||
}
|
||||
}
|
||||
15
fsck/source/net/message/nodes/HeartbeatMsgEx.h
Normal file
15
fsck/source/net/message/nodes/HeartbeatMsgEx.h
Normal 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_*/
|
||||
9
fsck/source/net/message/testing/DummyMsgEx.cpp
Normal file
9
fsck/source/net/message/testing/DummyMsgEx.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include <program/Program.h>
|
||||
#include "DummyMsgEx.h"
|
||||
|
||||
bool DummyMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
ctx.sendResponse(DummyMsg() );
|
||||
|
||||
return true;
|
||||
}
|
||||
20
fsck/source/net/message/testing/DummyMsgEx.h
Normal file
20
fsck/source/net/message/testing/DummyMsgEx.h
Normal 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*/
|
||||
951
fsck/source/net/msghelpers/MsgHelperRepair.cpp
Normal file
951
fsck/source/net/msghelpers/MsgHelperRepair.cpp
Normal 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()));
|
||||
}
|
||||
}
|
||||
77
fsck/source/net/msghelpers/MsgHelperRepair.h
Normal file
77
fsck/source/net/msghelpers/MsgHelperRepair.h
Normal 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_ */
|
||||
7
fsck/source/program/Main.cpp
Normal file
7
fsck/source/program/Main.cpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#include "Program.h"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
return Program::main(argc, argv);
|
||||
}
|
||||
|
||||
23
fsck/source/program/Program.cpp
Normal file
23
fsck/source/program/Program.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
27
fsck/source/program/Program.h
Normal file
27
fsck/source/program/Program.h
Normal 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_*/
|
||||
273
fsck/source/toolkit/DatabaseTk.cpp
Normal file
273
fsck/source/toolkit/DatabaseTk.cpp
Normal 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;
|
||||
}
|
||||
62
fsck/source/toolkit/DatabaseTk.h
Normal file
62
fsck/source/toolkit/DatabaseTk.h
Normal 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_ */
|
||||
2
fsck/source/toolkit/FsckDefinitions.cpp
Normal file
2
fsck/source/toolkit/FsckDefinitions.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#include "FsckDefinitions.h"
|
||||
|
||||
75
fsck/source/toolkit/FsckDefinitions.h
Normal file
75
fsck/source/toolkit/FsckDefinitions.h
Normal 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_ */
|
||||
16
fsck/source/toolkit/FsckException.h
Normal file
16
fsck/source/toolkit/FsckException.h
Normal 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 */
|
||||
604
fsck/source/toolkit/FsckTkEx.cpp
Normal file
604
fsck/source/toolkit/FsckTkEx.cpp
Normal 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;
|
||||
}
|
||||
103
fsck/source/toolkit/FsckTkEx.h
Normal file
103
fsck/source/toolkit/FsckTkEx.h
Normal 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_ */
|
||||
21
fsck/tests/FlatTest.cpp
Normal file
21
fsck/tests/FlatTest.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "FlatTest.h"
|
||||
|
||||
#include <program/Program.h>
|
||||
#include <common/toolkit/StorageTk.h>
|
||||
|
||||
FlatTest::FlatTest()
|
||||
{
|
||||
dirName = "./fsck.test.dir/";
|
||||
this->fileName = this->dirName + "set";
|
||||
}
|
||||
|
||||
void FlatTest::SetUp()
|
||||
{
|
||||
if(::mkdir(this->dirName.c_str(), 0770) < 0 && errno != EEXIST)
|
||||
throw std::runtime_error("could not create dir");
|
||||
}
|
||||
|
||||
void FlatTest::TearDown()
|
||||
{
|
||||
StorageTk::removeDirRecursive(this->dirName);
|
||||
}
|
||||
31
fsck/tests/FlatTest.h
Normal file
31
fsck/tests/FlatTest.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef FLATTEST_H_
|
||||
#define FLATTEST_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
class FlatTest : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
FlatTest();
|
||||
|
||||
void SetUp();
|
||||
void TearDown();
|
||||
|
||||
struct Data {
|
||||
uint64_t id;
|
||||
char dummy[1024 - sizeof(uint64_t)];
|
||||
|
||||
typedef uint64_t KeyType;
|
||||
uint64_t pkey() const { return id; }
|
||||
};
|
||||
|
||||
protected:
|
||||
std::string dirName;
|
||||
std::string fileName;
|
||||
};
|
||||
|
||||
#endif
|
||||
64
fsck/tests/TestConfig.cpp
Normal file
64
fsck/tests/TestConfig.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "TestConfig.h"
|
||||
|
||||
void TestConfig::SetUp()
|
||||
{
|
||||
emptyConfigFile = DUMMY_EMPTY_CONFIG_FILE;
|
||||
this->dummyConfigFile = DUMMY_NOEXIST_CONFIG_FILE;
|
||||
}
|
||||
|
||||
void TestConfig::TearDown()
|
||||
{
|
||||
// delete generated config file
|
||||
if ( StorageTk::pathExists(this->emptyConfigFile) )
|
||||
{
|
||||
// return value of remove is ignored now
|
||||
remove(this->emptyConfigFile.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestConfig, defaultConfigFile)
|
||||
{
|
||||
log.log(Log_DEBUG, "testDefaultConfigFile started");
|
||||
|
||||
// get the path where the binary resides
|
||||
int BUFSIZE = 255;
|
||||
char exePathBuf[BUFSIZE];
|
||||
// read only BUFSIZE-1, as we need to terminate the string manually later
|
||||
ssize_t len = readlink("/proc/self/exe", exePathBuf, BUFSIZE-1);
|
||||
|
||||
/* In case of an error, failure will indicate the error */
|
||||
if (len < 0)
|
||||
FAIL() << "Internal error";
|
||||
|
||||
/* in case of insufficient buffer size, failure will indicate the error */
|
||||
if (len >= BUFSIZE)
|
||||
FAIL() << "Internal error";
|
||||
|
||||
// readlink does NOT null terminate the string, so we do it here to be safe
|
||||
exePathBuf[len] = '\0';
|
||||
|
||||
// construct the path to the default config file
|
||||
std::string defaultFileName = std::string(dirname(exePathBuf))
|
||||
+ "/" + DEFAULT_CONFIG_FILE_RELATIVE;
|
||||
|
||||
// create config with the default file and see what happens while parsing
|
||||
int argc = 2;
|
||||
char* argv[2];
|
||||
|
||||
std::string appNameStr = APP_NAME;
|
||||
appNameStr += '\0';
|
||||
|
||||
std::string cfgLineStr = "cfgFile=" + defaultFileName;
|
||||
cfgLineStr += '\0';
|
||||
|
||||
argv[0] = &appNameStr[0];
|
||||
argv[1] = &cfgLineStr[0];
|
||||
|
||||
try {
|
||||
Config config(argc, argv);
|
||||
} catch (ConnAuthFileException& e) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.log(Log_DEBUG, "testDefaultConfigFile finished");
|
||||
}
|
||||
29
fsck/tests/TestConfig.h
Normal file
29
fsck/tests/TestConfig.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef TESTCONFIG_H_
|
||||
#define TESTCONFIG_H_
|
||||
|
||||
#include <app/config/Config.h>
|
||||
|
||||
#include <libgen.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <common/app/config/ConnAuthFileException.h>
|
||||
|
||||
#define DUMMY_NOEXIST_CONFIG_FILE "/tmp/nonExistantConfigFile.fsck"
|
||||
#define DUMMY_EMPTY_CONFIG_FILE "/tmp/emptyConfigFile.fsck"
|
||||
#define DEFAULT_CONFIG_FILE_RELATIVE "dist/etc/beegfs-client.conf"
|
||||
#define APP_NAME "beegfs-fsck";
|
||||
|
||||
class TestConfig: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp();
|
||||
void TearDown();
|
||||
|
||||
LogContext log;
|
||||
|
||||
// we have these filenames as member variables because
|
||||
// we might need to delete them in tearDown function
|
||||
std::string dummyConfigFile;
|
||||
std::string emptyConfigFile;
|
||||
};
|
||||
|
||||
#endif /* TESTCONFIG_H_ */
|
||||
254
fsck/tests/TestCursors.cpp
Normal file
254
fsck/tests/TestCursors.cpp
Normal file
@@ -0,0 +1,254 @@
|
||||
#include <database/Distinct.h>
|
||||
#include <database/Filter.h>
|
||||
#include <database/Group.h>
|
||||
#include <database/LeftJoinEq.h>
|
||||
#include <database/Select.h>
|
||||
#include <database/Union.h>
|
||||
#include <database/VectorSource.h>
|
||||
|
||||
#include <boost/static_assert.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
template<typename Obj, size_t Size>
|
||||
static VectorSource<Obj> vectorSource(const Obj (&data)[Size])
|
||||
{
|
||||
std::vector<Obj> vector(data, data + Size);
|
||||
|
||||
return VectorSource<Obj>(vector);
|
||||
}
|
||||
|
||||
template<typename Source, typename Data, size_t Size, typename EqFn>
|
||||
static void expect(const Source& source, const Data (&data)[Size], EqFn eqFn)
|
||||
{
|
||||
BOOST_STATIC_ASSERT(Size > 2);
|
||||
|
||||
for(size_t markPos = 0; markPos < Size; markPos++)
|
||||
{
|
||||
boost::shared_ptr<typename Source::MarkerType> mark;
|
||||
Source current = source;
|
||||
|
||||
for(size_t i = 0; i < Size; i++)
|
||||
{
|
||||
ASSERT_TRUE(current.step());
|
||||
|
||||
if(i == markPos)
|
||||
mark = boost::make_shared<typename Source::MarkerType>(current.mark() );
|
||||
|
||||
ASSERT_TRUE(eqFn(*current.get(), data[i]));
|
||||
}
|
||||
|
||||
ASSERT_FALSE(current.step());
|
||||
|
||||
current.restore(*mark);
|
||||
|
||||
for(size_t i = markPos; i < Size; i++)
|
||||
{
|
||||
ASSERT_TRUE(eqFn(*current.get(), data[i]));
|
||||
|
||||
if(i < Size - 1) {
|
||||
ASSERT_TRUE(current.step());
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_FALSE(current.step());
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Source, typename Data, size_t Size>
|
||||
static void expect(Source source, const Data (&data)[Size])
|
||||
{
|
||||
expect(source, data, std::equal_to<Data>() );
|
||||
}
|
||||
|
||||
TEST(Cursors, vectorSource)
|
||||
{
|
||||
const int data[] = { 1, 2, 3 };
|
||||
|
||||
VectorSource<int> source = vectorSource(data);
|
||||
|
||||
expect(source, data);
|
||||
}
|
||||
|
||||
TEST(Cursors, union)
|
||||
{
|
||||
const std::pair<int, int> data1[] = {
|
||||
std::make_pair(1, 1),
|
||||
std::make_pair(1, 2),
|
||||
std::make_pair(2, 2)
|
||||
};
|
||||
|
||||
const std::pair<int, int> data2[] = {
|
||||
std::make_pair(1, 3),
|
||||
std::make_pair(2, 3)
|
||||
};
|
||||
|
||||
const std::pair<int, int> expected[] = {
|
||||
std::make_pair(1, 3),
|
||||
std::make_pair(1, 1),
|
||||
std::make_pair(1, 2),
|
||||
std::make_pair(2, 3),
|
||||
std::make_pair(2, 2)
|
||||
};
|
||||
|
||||
struct Fn
|
||||
{
|
||||
static int key(std::pair<int, int> pair)
|
||||
{
|
||||
return pair.first;
|
||||
}
|
||||
};
|
||||
|
||||
expect(
|
||||
db::unionBy(
|
||||
Fn::key,
|
||||
vectorSource(data1),
|
||||
vectorSource(data2) ),
|
||||
expected);
|
||||
}
|
||||
|
||||
TEST(Cursors, select)
|
||||
{
|
||||
const int data[] = { 1, 2, 3, 4 };
|
||||
|
||||
const int expected[] = { -1, -2, -3, -4 };
|
||||
|
||||
struct Fn
|
||||
{
|
||||
static int op(int i)
|
||||
{
|
||||
return -i;
|
||||
}
|
||||
};
|
||||
|
||||
expect(
|
||||
vectorSource(data)
|
||||
| db::select(Fn::op),
|
||||
expected);
|
||||
}
|
||||
|
||||
TEST(Cursors, leftJoinEq)
|
||||
{
|
||||
const int data1[] = { 1, 2, 2, 3, 4 };
|
||||
const int data2[] = { 1, 2, 3, 3, 5 };
|
||||
|
||||
const std::pair<int, int> expected[] = {
|
||||
std::make_pair(1, 1),
|
||||
std::make_pair(2, 2),
|
||||
std::make_pair(2, 2),
|
||||
std::make_pair(3, 3),
|
||||
std::make_pair(3, 3),
|
||||
std::make_pair(4, 0),
|
||||
};
|
||||
|
||||
struct Fn
|
||||
{
|
||||
static bool eq(std::pair<int, int*> left, std::pair<int, int> right)
|
||||
{
|
||||
return left.first == right.first &&
|
||||
( (right.second == 0 && left.second == NULL) ||
|
||||
(right.second != 0 && left.second != NULL && right.second == *left.second) );
|
||||
}
|
||||
|
||||
static int key(int i)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
expect(
|
||||
db::leftJoinBy(
|
||||
Fn::key,
|
||||
vectorSource(data1),
|
||||
vectorSource(data2) ),
|
||||
expected,
|
||||
Fn::eq);
|
||||
}
|
||||
|
||||
struct TestGroupOps
|
||||
{
|
||||
typedef int KeyType;
|
||||
typedef int ProjType;
|
||||
typedef int GroupType;
|
||||
|
||||
int count;
|
||||
|
||||
TestGroupOps()
|
||||
: count(0)
|
||||
{}
|
||||
|
||||
KeyType key(int i)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
|
||||
ProjType project(int i)
|
||||
{
|
||||
return -i;
|
||||
}
|
||||
|
||||
void step(int i)
|
||||
{
|
||||
this->count += 1;
|
||||
}
|
||||
|
||||
int finish()
|
||||
{
|
||||
int result = this->count;
|
||||
this->count = 0;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
TEST(Cursors, group)
|
||||
{
|
||||
const int data[] = { 1, 2, 2, 3 };
|
||||
const std::pair<int, int> expected[] = {
|
||||
std::make_pair(-1, 1),
|
||||
std::make_pair(-2, 2),
|
||||
std::make_pair(-3, 1)
|
||||
};
|
||||
|
||||
expect(
|
||||
vectorSource(data)
|
||||
| db::groupBy(TestGroupOps() ),
|
||||
expected);
|
||||
}
|
||||
|
||||
TEST(Cursors, filter)
|
||||
{
|
||||
const int data[] = { 1, 2, 2, 3 };
|
||||
const int expected[] = { 2, 2, 3 };
|
||||
|
||||
struct Fn
|
||||
{
|
||||
static bool fn(int i)
|
||||
{
|
||||
return i > 1;
|
||||
}
|
||||
};
|
||||
|
||||
expect(
|
||||
vectorSource(data)
|
||||
| db::where(Fn::fn),
|
||||
expected);
|
||||
}
|
||||
|
||||
TEST(Cursors, distinct)
|
||||
{
|
||||
const int data[] = { 1, 2, 2, 3 };
|
||||
const int expected[] = { 1, 2, 3 };
|
||||
|
||||
struct Fn
|
||||
{
|
||||
static int fn(int i)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
expect(
|
||||
vectorSource(data)
|
||||
| db::distinctBy(Fn::fn),
|
||||
expected);
|
||||
}
|
||||
1772
fsck/tests/TestDatabase.cpp
Normal file
1772
fsck/tests/TestDatabase.cpp
Normal file
File diff suppressed because it is too large
Load Diff
115
fsck/tests/TestDatabase.h
Normal file
115
fsck/tests/TestDatabase.h
Normal file
@@ -0,0 +1,115 @@
|
||||
#ifndef TESTDATABASE_H_
|
||||
#define TESTDATABASE_H_
|
||||
|
||||
#include <common/net/message/NetMessage.h>
|
||||
#include <database/FsckDB.h>
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
class TestDatabase: public ::testing::Test
|
||||
{
|
||||
public:
|
||||
TestDatabase();
|
||||
virtual ~TestDatabase();
|
||||
|
||||
void SetUp();
|
||||
void TearDown();
|
||||
|
||||
protected:
|
||||
std::string databasePath;
|
||||
boost::scoped_ptr<FsckDB> db;
|
||||
|
||||
void testInsertAndReadSingleDentry();
|
||||
void testInsertAndReadDentries();
|
||||
|
||||
void testUpdateDentries();
|
||||
void testUpdateDirInodes();
|
||||
void testUpdateFileInodes();
|
||||
void testUpdateChunks();
|
||||
|
||||
void testInsertAndReadSingleFileInode();
|
||||
|
||||
void testInsertAndReadSingleDirInode();
|
||||
void testInsertAndReadDirInodes();
|
||||
|
||||
void testInsertAndReadSingleChunk();
|
||||
void testInsertAndReadChunks();
|
||||
|
||||
void testInsertAndReadSingleContDir();
|
||||
void testInsertAndReadContDirs();
|
||||
|
||||
void testInsertAndReadSingleFsID();
|
||||
void testInsertAndReadFsIDs();
|
||||
|
||||
void testFindDuplicateInodeIDs();
|
||||
void testFindDuplicateChunks();
|
||||
void testFindDuplicateContDirs();
|
||||
void testFindMismirroredDentries();
|
||||
void testFindMismirroredDirectories();
|
||||
|
||||
void testCheckForAndInsertDanglingDirEntries();
|
||||
void testCheckForAndInsertInodesWithWrongOwner();
|
||||
void testCheckForAndInsertDirEntriesWithWrongOwner();
|
||||
void testCheckForAndInsertMissingDentryByIDFile();
|
||||
void testCheckForAndInsertOrphanedDentryByIDFiles();
|
||||
void testCheckForAndInsertOrphanedDirInodes();
|
||||
void testCheckForAndInsertOrphanedFileInodes();
|
||||
void testCheckForAndInsertOrphanedChunks();
|
||||
void testCheckForAndInsertInodesWithoutContDir();
|
||||
void testCheckForAndInsertOrphanedContDirs();
|
||||
void testCheckForAndInsertFileInodesWithWrongAttribs();
|
||||
void testCheckForAndInsertDirInodesWithWrongAttribs();
|
||||
void testCheckForAndInsertFilesWithMissingStripeTargets();
|
||||
void testCheckForAndInsertChunksWithWrongPermissions();
|
||||
void testCheckForAndInsertChunksInWrongPath();
|
||||
|
||||
void testDeleteDentries();
|
||||
void testDeleteChunks();
|
||||
void testDeleteFsIDs();
|
||||
|
||||
void testGetRowCount();
|
||||
|
||||
void testGetFullPath();
|
||||
|
||||
template<typename Table, typename Items>
|
||||
static bool setRepairActions(Table* table, std::list<Items>& items, FsckRepairAction action)
|
||||
{
|
||||
for (typename std::list<Items>::iterator it = items.begin(), end = items.end();
|
||||
it != end;
|
||||
++it)
|
||||
{
|
||||
if (!table->setRepairAction(*it, action) )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Item, typename Source>
|
||||
static void drainToList(Source source, std::list<Item>& list)
|
||||
{
|
||||
list.clear();
|
||||
while(source.step() )
|
||||
list.push_back(*source.get() );
|
||||
}
|
||||
|
||||
template<typename Item, typename Source>
|
||||
static std::list<Item> drain(Source source)
|
||||
{
|
||||
std::list<Item> list;
|
||||
drainToList(source, list);
|
||||
return list;
|
||||
}
|
||||
|
||||
template<typename Source>
|
||||
static size_t countCursor(Source source)
|
||||
{
|
||||
size_t result = 0;
|
||||
while(source.step() )
|
||||
result++;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* TESTDATABASE_H_ */
|
||||
35
fsck/tests/TestFsckTk.cpp
Normal file
35
fsck/tests/TestFsckTk.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include <common/toolkit/FsckTk.h>
|
||||
#include <common/toolkit/StringTk.h>
|
||||
#include <toolkit/DatabaseTk.h>
|
||||
#include <toolkit/FsckTkEx.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(FsckTk, dirEntryTypeConversion)
|
||||
{
|
||||
FsckDirEntryType entryTypeOut;
|
||||
|
||||
entryTypeOut = FsckTk::DirEntryTypeToFsckDirEntryType(DirEntryType_INVALID);
|
||||
ASSERT_EQ(entryTypeOut, FsckDirEntryType_INVALID);
|
||||
|
||||
entryTypeOut = FsckTk::DirEntryTypeToFsckDirEntryType(DirEntryType_REGULARFILE);
|
||||
ASSERT_EQ(entryTypeOut, FsckDirEntryType_REGULARFILE);
|
||||
|
||||
entryTypeOut = FsckTk::DirEntryTypeToFsckDirEntryType(DirEntryType_SYMLINK);
|
||||
ASSERT_EQ(entryTypeOut, FsckDirEntryType_SYMLINK);
|
||||
|
||||
entryTypeOut = FsckTk::DirEntryTypeToFsckDirEntryType(DirEntryType_DIRECTORY);
|
||||
ASSERT_EQ(entryTypeOut, FsckDirEntryType_DIRECTORY);
|
||||
|
||||
entryTypeOut = FsckTk::DirEntryTypeToFsckDirEntryType(DirEntryType_BLOCKDEV);
|
||||
ASSERT_EQ(entryTypeOut, FsckDirEntryType_SPECIAL);
|
||||
|
||||
entryTypeOut = FsckTk::DirEntryTypeToFsckDirEntryType(DirEntryType_CHARDEV);
|
||||
ASSERT_EQ(entryTypeOut, FsckDirEntryType_SPECIAL);
|
||||
|
||||
entryTypeOut = FsckTk::DirEntryTypeToFsckDirEntryType(DirEntryType_FIFO);
|
||||
ASSERT_EQ(entryTypeOut, FsckDirEntryType_SPECIAL);
|
||||
|
||||
entryTypeOut = FsckTk::DirEntryTypeToFsckDirEntryType(DirEntryType_SOCKET);
|
||||
ASSERT_EQ(entryTypeOut, FsckDirEntryType_SPECIAL);
|
||||
}
|
||||
77
fsck/tests/TestSerialization.cpp
Normal file
77
fsck/tests/TestSerialization.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include <common/net/message/nodes/HeartbeatRequestMsg.h>
|
||||
#include <common/net/message/nodes/HeartbeatMsg.h>
|
||||
#include <common/toolkit/ZipIterator.h>
|
||||
#include <net/message/NetMessageFactory.h>
|
||||
#include <toolkit/DatabaseTk.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
template<typename Obj>
|
||||
static void testObjectRoundTrip(Obj& data)
|
||||
{
|
||||
Serializer ser;
|
||||
ser % data;
|
||||
|
||||
std::vector<char> buffer(ser.size());
|
||||
|
||||
ser = Serializer(&buffer[0], buffer.size());
|
||||
ser % data;
|
||||
ASSERT_TRUE(ser.good());
|
||||
ASSERT_EQ(ser.size(), buffer.size());
|
||||
|
||||
Deserializer des(&buffer[0], buffer.size());
|
||||
|
||||
Obj read;
|
||||
|
||||
des % read;
|
||||
ASSERT_TRUE(des.good());
|
||||
ASSERT_EQ(des.size(), buffer.size());
|
||||
|
||||
ser = Serializer();
|
||||
ser % read;
|
||||
ASSERT_EQ(ser.size(), buffer.size());
|
||||
|
||||
std::vector<char> rtBuffer(buffer.size());
|
||||
|
||||
ser = Serializer(&rtBuffer[0], rtBuffer.size());
|
||||
ser % read;
|
||||
ASSERT_TRUE(ser.good());
|
||||
ASSERT_EQ(ser.size(), rtBuffer.size());
|
||||
ASSERT_EQ(buffer, rtBuffer);
|
||||
}
|
||||
|
||||
TEST(Serialization, fsckDirEntrySerialization)
|
||||
{
|
||||
FsckDirEntry dirEntryIn = DatabaseTk::createDummyFsckDirEntry();
|
||||
testObjectRoundTrip(dirEntryIn);
|
||||
}
|
||||
|
||||
TEST(Serialization, fsckDirInodeSerialization)
|
||||
{
|
||||
FsckDirInode dirInodeIn = DatabaseTk::createDummyFsckDirInode();
|
||||
testObjectRoundTrip(dirInodeIn);
|
||||
}
|
||||
|
||||
TEST(Serialization, fsckFileInodeSerialization)
|
||||
{
|
||||
FsckFileInode fileInodeIn = DatabaseTk::createDummyFsckFileInode();
|
||||
testObjectRoundTrip(fileInodeIn);
|
||||
}
|
||||
|
||||
TEST(Serialization, fsckChunkSerialization)
|
||||
{
|
||||
FsckChunk chunkIn = DatabaseTk::createDummyFsckChunk();
|
||||
testObjectRoundTrip(chunkIn);
|
||||
}
|
||||
|
||||
TEST(Serialization, fsckContDirSerialization)
|
||||
{
|
||||
FsckContDir contDirIn = DatabaseTk::createDummyFsckContDir();
|
||||
testObjectRoundTrip(contDirIn);
|
||||
}
|
||||
|
||||
TEST(Serialization, fsckFsIDSerialization)
|
||||
{
|
||||
FsckFsID fsIDIn = DatabaseTk::createDummyFsckFsID();
|
||||
testObjectRoundTrip(fsIDIn);
|
||||
}
|
||||
177
fsck/tests/TestSet.cpp
Normal file
177
fsck/tests/TestSet.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
#include <database/Set.h>
|
||||
#include <program/Program.h>
|
||||
|
||||
#include "FlatTest.h"
|
||||
|
||||
class TestSet : public FlatTest {
|
||||
};
|
||||
|
||||
TEST_F(TestSet, open)
|
||||
{
|
||||
// break config
|
||||
int cfgFile = ::creat((fileName + ".t").c_str(), 0660);
|
||||
ASSERT_GE(cfgFile, 0);
|
||||
ASSERT_EQ(::truncate( (this->fileName + ".t").c_str(), 5), 0);
|
||||
do {
|
||||
try {
|
||||
Set<Data> set(this->fileName);
|
||||
} catch (const std::runtime_error& e) {
|
||||
ASSERT_NE(std::string(e.what()).find("cannot read set"), std::string::npos);
|
||||
ASSERT_NE(std::string(e.what()).find("with version 0"), std::string::npos);
|
||||
break;
|
||||
}
|
||||
|
||||
FAIL();
|
||||
} while (false);
|
||||
ASSERT_EQ(::write(cfgFile, "v1\n1\n", 5), 5);
|
||||
do {
|
||||
try {
|
||||
Set<Data> set(this->fileName);
|
||||
} catch (const std::runtime_error& e) {
|
||||
ASSERT_NE(std::string(e.what()).find("bad set description"), std::string::npos);
|
||||
break;
|
||||
}
|
||||
|
||||
FAIL();
|
||||
} while (false);
|
||||
ASSERT_EQ(::close(cfgFile), 0);
|
||||
ASSERT_EQ(::unlink((this->fileName + ".t").c_str()), 0);
|
||||
|
||||
// reopen with saved config
|
||||
{
|
||||
Set<Data> set(this->fileName);
|
||||
}
|
||||
{
|
||||
Set<Data> set(this->fileName);
|
||||
}
|
||||
|
||||
ASSERT_EQ(::truncate((this->fileName + ".t").c_str(), 5000), 0);
|
||||
do {
|
||||
try {
|
||||
Set<Data> set(this->fileName);
|
||||
} catch (const std::runtime_error& e) {
|
||||
ASSERT_NE(std::string(e.what()).find("bad set description"), std::string::npos);
|
||||
break;
|
||||
}
|
||||
|
||||
FAIL();
|
||||
} while (false);
|
||||
}
|
||||
|
||||
TEST_F(TestSet, drop)
|
||||
{
|
||||
{
|
||||
Set<Data> set(this->fileName);
|
||||
set.newFragment();
|
||||
}
|
||||
{
|
||||
Set<Data> set(this->fileName);
|
||||
set.drop();
|
||||
}
|
||||
|
||||
ASSERT_EQ(::rmdir(this->dirName.c_str()), 0);
|
||||
}
|
||||
|
||||
TEST_F(TestSet, clear)
|
||||
{
|
||||
{
|
||||
Set<Data> set(this->fileName);
|
||||
set.newFragment();
|
||||
}
|
||||
{
|
||||
Set<Data> set(this->fileName);
|
||||
set.clear();
|
||||
}
|
||||
|
||||
ASSERT_EQ(::unlink( (this->fileName + ".t").c_str()), 0);
|
||||
ASSERT_EQ(::rmdir(this->dirName.c_str()), 0);
|
||||
}
|
||||
|
||||
TEST_F(TestSet, merge)
|
||||
{
|
||||
Data d = {0, {}};
|
||||
|
||||
{
|
||||
Set<Data> set1(this->fileName + "1");
|
||||
set1.newFragment()->append(d);
|
||||
|
||||
Set<Data> set2(this->fileName + "2");
|
||||
set2.newFragment()->append(d);
|
||||
}
|
||||
|
||||
{
|
||||
Set<Data> set1(this->fileName + "1");
|
||||
Set<Data> set2(this->fileName + "2");
|
||||
set2.mergeInto(set1);
|
||||
}
|
||||
|
||||
{
|
||||
Set<Data> set1(this->fileName + "1");
|
||||
set1.drop();
|
||||
}
|
||||
|
||||
ASSERT_EQ(::unlink((this->fileName + "2.t").c_str()), 0);
|
||||
ASSERT_EQ(::rmdir(this->dirName.c_str()), 0);
|
||||
}
|
||||
|
||||
TEST_F(TestSet, dataOps)
|
||||
{
|
||||
static const unsigned FRAG_COUNT = 2 * 3 * 4;
|
||||
|
||||
Set<Data> set(this->fileName);
|
||||
|
||||
ASSERT_FALSE(set.cursor().step());
|
||||
|
||||
{
|
||||
for(unsigned i = 0; i < FRAG_COUNT; i++)
|
||||
{
|
||||
Data d = { i, {} };
|
||||
SetFragment<Data>* frag = set.newFragment();
|
||||
|
||||
frag->append(d);
|
||||
frag->append(d);
|
||||
|
||||
ASSERT_EQ(set.size(), 2 * (i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Set<Data>::Cursor cursor = set.cursor();
|
||||
|
||||
for(unsigned i = 0; i < FRAG_COUNT; i++) {
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, i);
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, i);
|
||||
}
|
||||
|
||||
ASSERT_FALSE(cursor.step());
|
||||
}
|
||||
|
||||
set.makeUnique();
|
||||
ASSERT_EQ(set.size(), FRAG_COUNT);
|
||||
|
||||
{
|
||||
Set<Data>::Cursor cursor = set.cursor();
|
||||
|
||||
for(unsigned i = 0; i < FRAG_COUNT; i++) {
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, i);
|
||||
}
|
||||
|
||||
ASSERT_FALSE(cursor.step());
|
||||
}
|
||||
|
||||
ASSERT_TRUE(set.getByKey(FRAG_COUNT / 2).first);
|
||||
ASSERT_EQ(set.getByKey(FRAG_COUNT / 2).second.id, FRAG_COUNT / 2);
|
||||
|
||||
ASSERT_FALSE(set.getByKey(FRAG_COUNT * 2).first);
|
||||
|
||||
struct ops
|
||||
{
|
||||
static int64_t key(uint64_t key) { return key + 6; }
|
||||
};
|
||||
|
||||
ASSERT_TRUE(set.getByKeyProjection(7, ops::key).first);
|
||||
ASSERT_EQ(set.getByKeyProjection(7, ops::key).second.id, 1u);
|
||||
}
|
||||
203
fsck/tests/TestSetFragment.cpp
Normal file
203
fsck/tests/TestSetFragment.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
#include <database/SetFragment.h>
|
||||
#include <program/Program.h>
|
||||
|
||||
#include "FlatTest.h"
|
||||
|
||||
class TestSetFragment : public FlatTest {
|
||||
};
|
||||
|
||||
TEST_F(TestSetFragment, open)
|
||||
{
|
||||
if(::getuid() == 0)
|
||||
{
|
||||
std::cerr << "Test run as root, skipping DAC tests\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
// bad directory permissions (can't open or create)
|
||||
ASSERT_EQ(::chmod(this->dirName.c_str(), 0), 0);
|
||||
do {
|
||||
try {
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
} catch (const std::runtime_error& e) {
|
||||
ASSERT_NE(std::string(e.what()).find("could not open fragment file"), std::string::npos);
|
||||
break;
|
||||
}
|
||||
|
||||
FAIL();
|
||||
} while (false);
|
||||
ASSERT_EQ(::chmod(this->dirName.c_str(), 0770), 0);
|
||||
}
|
||||
|
||||
// not a regular file
|
||||
ASSERT_EQ(::mkfifo(this->fileName.c_str(), 0660), 0);
|
||||
do {
|
||||
try {
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
} catch (const std::runtime_error& e) {
|
||||
ASSERT_NE(std::string(e.what()).find("error while opening fragment file"), std::string::npos);
|
||||
break;
|
||||
}
|
||||
|
||||
FAIL();
|
||||
} while (false);
|
||||
ASSERT_EQ(::unlink(this->fileName.c_str()), 0);
|
||||
|
||||
// corrupted config area
|
||||
ASSERT_EQ(::close(::creat(this->fileName.c_str(), 0660)), 0);
|
||||
ASSERT_EQ(::truncate(this->fileName.c_str(), SetFragment<Data>::CONFIG_AREA_SIZE - 1), 0);
|
||||
do {
|
||||
try {
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
} catch (const std::runtime_error& e) {
|
||||
ASSERT_NE(std::string(e.what()).find("error while opening fragment file"), std::string::npos);
|
||||
break;
|
||||
}
|
||||
|
||||
FAIL();
|
||||
} while (false);
|
||||
ASSERT_EQ(::unlink(this->fileName.c_str()), 0);
|
||||
|
||||
// corrupted data
|
||||
ASSERT_EQ(::close(::creat(this->fileName.c_str(), 0660)), 0);
|
||||
ASSERT_EQ(::truncate(this->fileName.c_str(),
|
||||
SetFragment<Data>::CONFIG_AREA_SIZE + sizeof(Data) - 1), 0);
|
||||
do {
|
||||
try {
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
} catch (const std::runtime_error& e) {
|
||||
ASSERT_NE(std::string(e.what()).find("error while opening fragment file"), std::string::npos);
|
||||
break;
|
||||
}
|
||||
|
||||
FAIL();
|
||||
} while (false);
|
||||
ASSERT_EQ(::unlink(this->fileName.c_str()), 0);
|
||||
|
||||
// should work
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
}
|
||||
|
||||
TEST_F(TestSetFragment, drop)
|
||||
{
|
||||
struct stat stat;
|
||||
|
||||
{
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
}
|
||||
|
||||
ASSERT_EQ(::stat(this->fileName.c_str(), &stat), 0);
|
||||
|
||||
{
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
frag.drop();
|
||||
}
|
||||
|
||||
ASSERT_EQ(::stat(this->fileName.c_str(), &stat), -1);
|
||||
ASSERT_EQ(errno, ENOENT);
|
||||
}
|
||||
|
||||
TEST_F(TestSetFragment, flush)
|
||||
{
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
Data d = {0, {}};
|
||||
struct stat stat;
|
||||
|
||||
frag.append(d);
|
||||
|
||||
ASSERT_EQ(::stat(this->fileName.c_str(), &stat), 0);
|
||||
ASSERT_EQ(stat.st_size, 0);
|
||||
|
||||
frag.flush();
|
||||
|
||||
ASSERT_EQ(::stat(this->fileName.c_str(), &stat), 0);
|
||||
ASSERT_EQ(size_t(stat.st_size), frag.CONFIG_AREA_SIZE + sizeof(d));
|
||||
}
|
||||
|
||||
TEST_F(TestSetFragment, appendAndAccess)
|
||||
{
|
||||
struct ops
|
||||
{
|
||||
static void fill(SetFragment<Data>& frag, unsigned limit)
|
||||
{
|
||||
Data d = {0, {}};
|
||||
|
||||
for (unsigned i = 0; i < limit; i++) {
|
||||
d.id = i;
|
||||
frag.append(d);
|
||||
}
|
||||
}
|
||||
|
||||
static void check(SetFragment<Data>& frag, unsigned limit)
|
||||
{
|
||||
ASSERT_EQ(frag.size(), limit);
|
||||
|
||||
for (unsigned i = 0; i < limit; i++) {
|
||||
ASSERT_EQ(frag[i].id, i);
|
||||
ASSERT_EQ(
|
||||
size_t(std::count(
|
||||
frag[i].dummy,
|
||||
frag[i].dummy + sizeof(frag[i].dummy),
|
||||
0)),
|
||||
sizeof(frag[i].dummy));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static const unsigned LIMIT = 2 * SetFragment<Data>::BUFFER_SIZE / sizeof(Data);
|
||||
|
||||
{
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
|
||||
ops::fill(frag, LIMIT);
|
||||
ops::check(frag, LIMIT);
|
||||
}
|
||||
|
||||
// read everything again, with a new instance
|
||||
{
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
|
||||
ops::check(frag, LIMIT);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestSetFragment, sortAndGet)
|
||||
{
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
|
||||
// generate a pseudo-random sequence as {0, 1, ...} * 4001 mod 4003 (both prime)
|
||||
for (unsigned i = 0; i < 4003; i++)
|
||||
{
|
||||
Data d = { i * 4001 % 4003, {} };
|
||||
frag.append(d);
|
||||
}
|
||||
|
||||
frag.sort();
|
||||
|
||||
for (unsigned i = 0; i < 4003; i++)
|
||||
ASSERT_EQ(frag[i].id, i);
|
||||
|
||||
ASSERT_TRUE(frag.getByKey(17).first);
|
||||
ASSERT_EQ(frag.getByKey(17).second.id, 17u);
|
||||
|
||||
ASSERT_TRUE(!frag.getByKey(170000).first);
|
||||
|
||||
struct ops
|
||||
{
|
||||
static int64_t key(uint64_t key) { return key + 6; }
|
||||
};
|
||||
|
||||
ASSERT_TRUE(frag.getByKeyProjection(23, ops::key).first);
|
||||
ASSERT_EQ(frag.getByKeyProjection(23, ops::key).second.id, 17u);
|
||||
}
|
||||
|
||||
TEST_F(TestSetFragment, rename)
|
||||
{
|
||||
SetFragment<Data> frag(this->fileName);
|
||||
|
||||
frag.rename(this->fileName + "1");
|
||||
|
||||
ASSERT_EQ(::close(::open( (this->fileName + "1").c_str(), O_RDWR)), 0);
|
||||
|
||||
frag.rename(this->fileName);
|
||||
}
|
||||
182
fsck/tests/TestTable.cpp
Normal file
182
fsck/tests/TestTable.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
#include "TestTable.h"
|
||||
|
||||
void TestTable::SetUp()
|
||||
{
|
||||
FlatTest::SetUp();
|
||||
this->table.reset(new Table<Data>(this->fileName, 4096) );
|
||||
}
|
||||
|
||||
void TestTable::TearDown()
|
||||
{
|
||||
if(this->table)
|
||||
this->table->drop();
|
||||
|
||||
FlatTest::TearDown();
|
||||
}
|
||||
|
||||
TEST_F(TestTable, dataHandling)
|
||||
{
|
||||
Data d = {0, {}};
|
||||
|
||||
// first, only ordered inserts
|
||||
d.id = 0;
|
||||
this->table->insert(d);
|
||||
d.id = 1;
|
||||
this->table->insert(d);
|
||||
|
||||
// complete the base set
|
||||
this->table->commitChanges();
|
||||
|
||||
// insert "in order"
|
||||
d.id = 2;
|
||||
this->table->insert(d);
|
||||
|
||||
// unordered delete should fail
|
||||
ASSERT_THROW(this->table->remove(1), std::runtime_error);
|
||||
|
||||
// ordered delete should work
|
||||
this->table->remove(3);
|
||||
|
||||
// reinsert of same id should work
|
||||
d.id = 3;
|
||||
this->table->insert(d);
|
||||
|
||||
// unordered insert should now fail
|
||||
ASSERT_THROW(this->table->insert(d), std::runtime_error);
|
||||
|
||||
// reset changes
|
||||
this->table->commitChanges();
|
||||
|
||||
// unordered insert should work
|
||||
d.id = 0;
|
||||
this->table->insert(d);
|
||||
d.id = 4;
|
||||
this->table->insert(d);
|
||||
d.id = 1;
|
||||
this->table->insert(d);
|
||||
|
||||
// delete should fail
|
||||
ASSERT_THROW(this->table->remove(1), std::runtime_error);
|
||||
|
||||
// reset changes
|
||||
this->table->commitChanges();
|
||||
|
||||
// ordered delete should work
|
||||
this->table->remove(1);
|
||||
this->table->remove(2);
|
||||
this->table->remove(3);
|
||||
|
||||
// unordered insert should fail
|
||||
d.id = 2;
|
||||
ASSERT_THROW(this->table->insert(d), std::runtime_error);
|
||||
|
||||
// unordered delete should work
|
||||
this->table->remove(3);
|
||||
|
||||
// insert should fail after unordered delete
|
||||
d.id = 10;
|
||||
ASSERT_THROW(this->table->insert(d), std::runtime_error);
|
||||
|
||||
// uncomitted changes should yield the sequence [0, 0, 1, 1, 2, 3, 4]
|
||||
{
|
||||
Table<Data>::QueryType cursor = this->table->cursor();
|
||||
|
||||
unsigned expect[7] = { 0, 0, 1, 1, 2, 3, 4 };
|
||||
for(unsigned i = 0; i < 7; i++)
|
||||
{
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, expect[i]);
|
||||
}
|
||||
|
||||
ASSERT_FALSE(cursor.step());
|
||||
}
|
||||
|
||||
// commited should contain the sequence [0, 0, 4]
|
||||
this->table->commitChanges();
|
||||
{
|
||||
Table<Data>::QueryType cursor = this->table->cursor();
|
||||
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, 0u);
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, 0u);
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, 4u);
|
||||
ASSERT_FALSE(cursor.step());
|
||||
}
|
||||
|
||||
ASSERT_TRUE(this->table->getByKey(4).first);
|
||||
ASSERT_EQ(this->table->getByKey(4).second.id, 4u);
|
||||
|
||||
ASSERT_FALSE(this->table->getByKey(3).first);
|
||||
|
||||
struct ops
|
||||
{
|
||||
static int64_t key(uint64_t key) { return key + 6; }
|
||||
};
|
||||
|
||||
ASSERT_TRUE(this->table->getByKeyProjection(10, ops::key).first);
|
||||
ASSERT_EQ(this->table->getByKeyProjection(10, ops::key).second.id, 4u);
|
||||
}
|
||||
|
||||
TEST_F(TestTable, bulkInsert)
|
||||
{
|
||||
{
|
||||
boost::shared_ptr<Buffer<Data> > buf1, buf2;
|
||||
|
||||
buf1 = this->table->bulkInsert();
|
||||
buf2 = this->table->bulkInsert();
|
||||
|
||||
Data d = { 0, {} };
|
||||
|
||||
d.id = 1;
|
||||
buf1->append(d);
|
||||
|
||||
d.id = 2;
|
||||
buf2->append(d);
|
||||
}
|
||||
|
||||
// all insert must have gone to base, queryable immediatly
|
||||
{
|
||||
Table<Data>::QueryType cursor = this->table->cursor();
|
||||
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, 1u);
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, 2u);
|
||||
ASSERT_FALSE(cursor.step());
|
||||
}
|
||||
|
||||
// another bulk insert is fine, no modifications happened yet
|
||||
{
|
||||
boost::shared_ptr<Buffer<Data> > buf;
|
||||
|
||||
buf = this->table->bulkInsert();
|
||||
|
||||
Data d = { 3, {} };
|
||||
buf->append(d);
|
||||
|
||||
// can't do normal insert or remove during bulk insert
|
||||
ASSERT_THROW(this->table->insert(d), std::runtime_error);
|
||||
ASSERT_THROW(this->table->remove(4), std::runtime_error);
|
||||
}
|
||||
|
||||
// all insert must have gone to base, queryable immediatly
|
||||
{
|
||||
Table<Data>::QueryType cursor = this->table->cursor();
|
||||
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, 1u);
|
||||
ASSERT_TRUE(cursor.step());
|
||||
ASSERT_EQ(cursor.get()->id, 2u);
|
||||
ASSERT_TRUE(cursor.step() );
|
||||
ASSERT_EQ(cursor.get()->id, 3u);
|
||||
ASSERT_FALSE(cursor.step());
|
||||
}
|
||||
|
||||
this->table->remove(1);
|
||||
ASSERT_THROW(this->table->bulkInsert(), std::runtime_error);
|
||||
|
||||
this->table->commitChanges();
|
||||
ASSERT_THROW(this->table->bulkInsert(), std::runtime_error);
|
||||
}
|
||||
20
fsck/tests/TestTable.h
Normal file
20
fsck/tests/TestTable.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#ifndef TESTTABLE_H_
|
||||
#define TESTTABLE_H_
|
||||
|
||||
#include <database/Table.h>
|
||||
#include "FlatTest.h"
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
class TestTable : public FlatTest
|
||||
{
|
||||
public:
|
||||
void SetUp();
|
||||
void TearDown();
|
||||
|
||||
protected:
|
||||
boost::scoped_ptr<Table<Data> > table;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user