New upstream version 8.1.0
This commit is contained in:
20
meta/source/net/message/storage/GetHighResStatsMsgEx.cpp
Normal file
20
meta/source/net/message/storage/GetHighResStatsMsgEx.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/GetHighResStatsRespMsg.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include "GetHighResStatsMsgEx.h"
|
||||
|
||||
|
||||
bool GetHighResStatsMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
HighResStatsList statsHistory;
|
||||
uint64_t lastStatsMS = getValue();
|
||||
|
||||
// get stats history
|
||||
StatsCollector* statsCollector = Program::getApp()->getStatsCollector();
|
||||
statsCollector->getStatsSince(lastStatsMS, statsHistory);
|
||||
|
||||
ctx.sendResponse(GetHighResStatsRespMsg(&statsHistory) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
12
meta/source/net/message/storage/GetHighResStatsMsgEx.h
Normal file
12
meta/source/net/message/storage/GetHighResStatsMsgEx.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/GetHighResStatsMsg.h>
|
||||
|
||||
|
||||
class GetHighResStatsMsgEx : public GetHighResStatsMsg
|
||||
{
|
||||
public:
|
||||
virtual bool processIncoming(ResponseContext& ctx);
|
||||
};
|
||||
|
||||
49
meta/source/net/message/storage/StatStoragePathMsgEx.cpp
Normal file
49
meta/source/net/message/storage/StatStoragePathMsgEx.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/StatStoragePathRespMsg.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include "StatStoragePathMsgEx.h"
|
||||
|
||||
|
||||
bool StatStoragePathMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
int64_t sizeTotal = 0;
|
||||
int64_t sizeFree = 0;
|
||||
int64_t inodesTotal = 0;
|
||||
int64_t inodesFree = 0;
|
||||
|
||||
FhgfsOpsErr statRes = statStoragePath(&sizeTotal, &sizeFree, &inodesTotal, &inodesFree);
|
||||
|
||||
ctx.sendResponse(StatStoragePathRespMsg(statRes, sizeTotal, sizeFree, inodesTotal, inodesFree) );
|
||||
|
||||
App* app = Program::getApp();
|
||||
app->getNodeOpStats()->updateNodeOp(ctx.getSocket()->getPeerIP(), MetaOpCounter_STATFS,
|
||||
getMsgHeaderUserID() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FhgfsOpsErr StatStoragePathMsgEx::statStoragePath(int64_t* outSizeTotal, int64_t* outSizeFree,
|
||||
int64_t* outInodesTotal, int64_t* outInodesFree)
|
||||
{
|
||||
const char* logContext = "StatStoragePathMsg (stat path)";
|
||||
|
||||
std::string pathStr = Program::getApp()->getMetaPath();
|
||||
|
||||
bool statSuccess = StorageTk::statStoragePath(
|
||||
pathStr, outSizeTotal, outSizeFree, outInodesTotal, outInodesFree);
|
||||
|
||||
if(unlikely(!statSuccess) )
|
||||
{ // error
|
||||
LogContext(logContext).logErr("Unable to statfs() storage path: " + pathStr +
|
||||
" (SysErr: " + System::getErrString() );
|
||||
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
|
||||
StorageTk::statStoragePathOverride(pathStr, outSizeFree, outInodesFree);
|
||||
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
18
meta/source/net/message/storage/StatStoragePathMsgEx.h
Normal file
18
meta/source/net/message/storage/StatStoragePathMsgEx.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/StatStoragePathMsg.h>
|
||||
|
||||
// derives from config option "StoragePath" and actually is similar to statfs()
|
||||
|
||||
class StatStoragePathMsgEx : public StatStoragePathMsg
|
||||
{
|
||||
public:
|
||||
virtual bool processIncoming(ResponseContext& ctx);
|
||||
|
||||
private:
|
||||
FhgfsOpsErr statStoragePath(int64_t* outSizeTotal, int64_t* outSizeFree,
|
||||
int64_t* outInodesTotal, int64_t* outInodesFree);
|
||||
};
|
||||
|
||||
|
||||
81
meta/source/net/message/storage/TruncFileMsgEx.cpp
Normal file
81
meta/source/net/message/storage/TruncFileMsgEx.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include <common/net/message/control/GenericResponseMsg.h>
|
||||
#include <common/net/message/storage/TruncFileRespMsg.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <components/FileEventLogger.h>
|
||||
#include <net/msghelpers/MsgHelperTrunc.h>
|
||||
#include "TruncFileMsgEx.h"
|
||||
#include <program/Program.h>
|
||||
|
||||
FileIDLock TruncFileMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
return {&store, getEntryInfo()->getEntryID(), true};
|
||||
}
|
||||
|
||||
bool TruncFileMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> TruncFileMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
// update operation counters
|
||||
updateNodeOp(ctx, MetaOpCounter_TRUNCATE);
|
||||
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
if (!isSecondary)
|
||||
{
|
||||
FhgfsOpsErr truncRes = MsgHelperTrunc::truncFile(getEntryInfo(), getFilesize(),
|
||||
isMsgHeaderFeatureFlagSet(TRUNCFILEMSG_FLAG_USE_QUOTA), getMsgHeaderUserID(),
|
||||
dynAttribs);
|
||||
if (truncRes == FhgfsOpsErr_SUCCESS && (shouldFixTimestamps() || getFileEvent()))
|
||||
{
|
||||
auto [inode, referenceRes] = metaStore->referenceFile(getEntryInfo());
|
||||
unsigned numHardlinks = 0;
|
||||
|
||||
if (likely(inode))
|
||||
{
|
||||
if (shouldFixTimestamps())
|
||||
fixInodeTimestamp(*inode, mirroredTimestamps, nullptr);
|
||||
|
||||
numHardlinks = inode->getNumHardlinks();
|
||||
metaStore->releaseFile(getEntryInfo()->getParentEntryID(), inode);
|
||||
}
|
||||
|
||||
if (app->getFileEventLogger() && getFileEvent())
|
||||
{
|
||||
EventContext eventCtx = makeEventContext(
|
||||
getEntryInfo(),
|
||||
getEntryInfo()->getParentEntryID(),
|
||||
getMsgHeaderUserID(),
|
||||
"",
|
||||
numHardlinks,
|
||||
isSecondary
|
||||
);
|
||||
|
||||
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
}
|
||||
return boost::make_unique<ResponseState>(truncRes);
|
||||
}
|
||||
|
||||
auto [inode, referenceRes] = metaStore->referenceFile(getEntryInfo());
|
||||
if(!inode)
|
||||
return boost::make_unique<ResponseState>(referenceRes);
|
||||
|
||||
inode->setDynAttribs(dynAttribs);
|
||||
if (shouldFixTimestamps())
|
||||
fixInodeTimestamp(*inode, mirroredTimestamps, nullptr);
|
||||
inode->updateInodeOnDisk(getEntryInfo());
|
||||
|
||||
metaStore->releaseFile(getEntryInfo()->getParentEntryID(), inode);
|
||||
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_SUCCESS);
|
||||
}
|
||||
|
||||
void TruncFileMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_TruncFileResp);
|
||||
}
|
||||
34
meta/source/net/message/storage/TruncFileMsgEx.h
Normal file
34
meta/source/net/message/storage/TruncFileMsgEx.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/TruncFileMsg.h>
|
||||
#include <common/net/message/storage/TruncFileRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
#include <storage/MetaStore.h>
|
||||
|
||||
|
||||
class TruncFileMsgEx : public MirroredMessage<TruncFileMsg, FileIDLock>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<TruncFileRespMsg, NETMSGTYPE_TruncFile> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
FileIDLock lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<TruncFileRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "TruncFileMsgEx/forward"; }
|
||||
};
|
||||
|
||||
|
||||
143
meta/source/net/message/storage/attribs/GetEntryInfoMsgEx.cpp
Normal file
143
meta/source/net/message/storage/attribs/GetEntryInfoMsgEx.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
#include <common/net/message/storage/attribs/GetEntryInfoRespMsg.h>
|
||||
#include <common/storage/striping/Raid0Pattern.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <program/Program.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include "GetEntryInfoMsgEx.h"
|
||||
|
||||
|
||||
bool GetEntryInfoMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
#ifdef BEEGFS_DEBUG
|
||||
const char* logContext = "GetEntryInfoMsg incoming";
|
||||
#endif // BEEGFS_DEBUG
|
||||
|
||||
LOG_DEBUG(logContext, Log_SPAM,
|
||||
"ParentEntryID: " + this->getEntryInfo()->getParentEntryID() + "; "
|
||||
"entryID: " + this->getEntryInfo()->getParentEntryID() );
|
||||
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
FileIDLock GetEntryInfoMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
return {&store, getEntryInfo()->getEntryID(), false};
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> GetEntryInfoMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
|
||||
GetEntryInfoMsgResponseState resp;
|
||||
|
||||
EntryInfo* entryInfo = this->getEntryInfo();
|
||||
StripePattern* pattern = NULL;
|
||||
FhgfsOpsErr getInfoRes;
|
||||
PathInfo pathInfo;
|
||||
RemoteStorageTarget rstInfo;
|
||||
uint32_t numSessionsRead = 0;
|
||||
uint32_t numSessionsWrite = 0;
|
||||
uint8_t dataState = 0;
|
||||
|
||||
if (entryInfo->getParentEntryID().empty())
|
||||
{
|
||||
// special case: get info for root directory
|
||||
// no pathInfo here, as this is currently only used for fileInodes
|
||||
getInfoRes = getRootInfo(&pattern, &rstInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
getInfoRes = getInfo(entryInfo, &pattern, &pathInfo, &rstInfo, numSessionsRead,
|
||||
numSessionsWrite, dataState);
|
||||
}
|
||||
|
||||
if (getInfoRes != FhgfsOpsErr_SUCCESS)
|
||||
{ // error occurred => create a dummy pattern
|
||||
UInt16Vector dummyStripeNodes;
|
||||
pattern = new Raid0Pattern(1, dummyStripeNodes);
|
||||
}
|
||||
|
||||
resp.setGetEntryInfoResult(getInfoRes);
|
||||
resp.setStripePattern(pattern);
|
||||
resp.setPathInfo(pathInfo);
|
||||
resp.setRemoteStorageTarget(rstInfo);
|
||||
resp.setNumSessionsRead(numSessionsRead);
|
||||
resp.setNumSessionsWrite(numSessionsWrite);
|
||||
resp.setFileDataState(dataState);
|
||||
|
||||
App* app = Program::getApp();
|
||||
app->getNodeOpStats()->updateNodeOp(ctx.getSocket()->getPeerIP(), MetaOpCounter_GETENTRYINFO,
|
||||
getMsgHeaderUserID() );
|
||||
|
||||
return boost::make_unique<ResponseState>(std::move(resp));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param outPattern StripePattern clone (in case of success), that must be deleted by the caller
|
||||
*/
|
||||
FhgfsOpsErr GetEntryInfoMsgEx::getInfo(EntryInfo* entryInfo, StripePattern** outPattern,
|
||||
PathInfo* outPathInfo, RemoteStorageTarget* outRstInfo, uint32_t& outNumReadSessions,
|
||||
uint32_t& outNumWriteSessions, uint8_t& outDataState)
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
if (entryInfo->getEntryType() == DirEntryType_DIRECTORY)
|
||||
{ // entry is a directory
|
||||
DirInode* dir = metaStore->referenceDir(entryInfo->getEntryID(),
|
||||
entryInfo->getIsBuddyMirrored(), true);
|
||||
if(dir)
|
||||
{
|
||||
*outPattern = dir->getStripePatternClone();
|
||||
outRstInfo->set(dir->getRemoteStorageTargetInfo());
|
||||
metaStore->releaseDir(entryInfo->getEntryID() );
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // entry is a file
|
||||
auto [fileInode, referenceRes] = metaStore->referenceFile(entryInfo);
|
||||
if(fileInode)
|
||||
{
|
||||
*outPattern = fileInode->getStripePattern()->clone();
|
||||
outRstInfo->set(fileInode->getRemoteStorageTargetInfo());
|
||||
fileInode->getPathInfo(outPathInfo);
|
||||
|
||||
outNumReadSessions = fileInode->getNumSessionsRead();
|
||||
outNumWriteSessions = fileInode->getNumSessionsWrite();
|
||||
outDataState = fileInode->getFileState().getRawValue();
|
||||
|
||||
metaStore->releaseFile(entryInfo->getParentEntryID(), fileInode);
|
||||
return referenceRes;
|
||||
}
|
||||
}
|
||||
|
||||
return FhgfsOpsErr_PATHNOTEXISTS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param outPattern StripePattern clone (in case of success), that must be deleted by the caller
|
||||
*/
|
||||
FhgfsOpsErr GetEntryInfoMsgEx::getRootInfo(StripePattern** outPattern, RemoteStorageTarget* outRstInfo)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
MirrorBuddyGroupMapper* metaBuddyGroupMapper = app->getMetaBuddyGroupMapper();
|
||||
DirInode* rootDir = app->getRootDir();
|
||||
|
||||
NumNodeID localNodeID = app->getLocalNodeNumID();
|
||||
|
||||
if (rootDir->getIsBuddyMirrored())
|
||||
{
|
||||
uint16_t buddyGroupID = metaBuddyGroupMapper->getBuddyGroupID(localNodeID.val());
|
||||
if (buddyGroupID != rootDir->getOwnerNodeID().val() )
|
||||
return FhgfsOpsErr_NOTOWNER;
|
||||
}
|
||||
else
|
||||
if (localNodeID != rootDir->getOwnerNodeID() )
|
||||
return FhgfsOpsErr_NOTOWNER;
|
||||
|
||||
*outPattern = rootDir->getStripePatternClone();
|
||||
outRstInfo->set(rootDir->getRemoteStorageTargetInfo());
|
||||
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
98
meta/source/net/message/storage/attribs/GetEntryInfoMsgEx.h
Normal file
98
meta/source/net/message/storage/attribs/GetEntryInfoMsgEx.h
Normal file
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include <storage/DirInode.h>
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/attribs/GetEntryInfoMsg.h>
|
||||
#include <common/net/message/storage/attribs/GetEntryInfoRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
class GetEntryInfoMsgResponseState : public MirroredMessageResponseState
|
||||
{
|
||||
public:
|
||||
GetEntryInfoMsgResponseState() : result(FhgfsOpsErr_INTERNAL), mirrorNodeID(0)
|
||||
{
|
||||
}
|
||||
|
||||
GetEntryInfoMsgResponseState(GetEntryInfoMsgResponseState&& other) :
|
||||
result(other.result),
|
||||
mirrorNodeID(other.mirrorNodeID),
|
||||
pattern(std::move(other.pattern)),
|
||||
pathInfo(std::move(other.pathInfo)),
|
||||
rst(std::move(other.rst)),
|
||||
numSessionsRead(other.numSessionsRead),
|
||||
numSessionsWrite(other.numSessionsWrite),
|
||||
fileDataState(other.fileDataState)
|
||||
{
|
||||
}
|
||||
|
||||
void sendResponse(NetMessage::ResponseContext& ctx) override
|
||||
{
|
||||
GetEntryInfoRespMsg resp(result, pattern.get(), mirrorNodeID, &pathInfo, &rst,
|
||||
numSessionsRead, numSessionsWrite, fileDataState);
|
||||
ctx.sendResponse(resp);
|
||||
}
|
||||
|
||||
// GetEntryInfoMsgEx is transformed into a mirrored message to utilize
|
||||
// MirroredMessage::lock(), thereby preventing races with operations such
|
||||
// as unlink. However, forwarding this message to the secondary is unnecessary.
|
||||
// Overriding the changeObservableState() function to always return false ensures
|
||||
// that this message never gets forwarded to seconadary.
|
||||
bool changesObservableState() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void setGetEntryInfoResult(FhgfsOpsErr result) { this->result = result; }
|
||||
void setMirrorNodeID(uint16_t nodeId) { this->mirrorNodeID = nodeId; }
|
||||
void setStripePattern(StripePattern* pattern) { this->pattern.reset(pattern); }
|
||||
void setPathInfo(PathInfo const& pathInfo) { this->pathInfo = pathInfo; }
|
||||
void setRemoteStorageTarget(RemoteStorageTarget const& rstInfo) { rst = rstInfo; }
|
||||
void setNumSessionsRead(uint32_t numReaders) { numSessionsRead = numReaders; }
|
||||
void setNumSessionsWrite(uint32_t numWriters) { numSessionsWrite = numWriters; }
|
||||
void setFileDataState(uint8_t dataState) { fileDataState = dataState; }
|
||||
|
||||
protected:
|
||||
uint32_t serializerTag() const override { return NETMSGTYPE_GetEntryInfo; }
|
||||
void serializeContents(Serializer& ser) const override {}
|
||||
|
||||
private:
|
||||
FhgfsOpsErr result;
|
||||
uint16_t mirrorNodeID; // metadata mirror node (0 means "none")
|
||||
std::unique_ptr<StripePattern> pattern;
|
||||
PathInfo pathInfo;
|
||||
RemoteStorageTarget rst;
|
||||
uint32_t numSessionsRead;
|
||||
uint32_t numSessionsWrite;
|
||||
uint8_t fileDataState;
|
||||
};
|
||||
|
||||
class GetEntryInfoMsgEx : public MirroredMessage<GetEntryInfoMsg, FileIDLock>
|
||||
{
|
||||
public:
|
||||
typedef GetEntryInfoMsgResponseState ResponseState;
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
FileIDLock lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override
|
||||
{
|
||||
return getEntryInfo()->getIsBuddyMirrored();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override {}
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
FhgfsOpsErr getInfo(EntryInfo* entryInfo, StripePattern** outPattern, PathInfo* outPathInfo,
|
||||
RemoteStorageTarget* outRstInfo, uint32_t& outNumReadSessions, uint32_t& outNumWriteSessions,
|
||||
uint8_t& outDataState);
|
||||
FhgfsOpsErr getRootInfo(StripePattern** outPattern, RemoteStorageTarget* outRstInfo);
|
||||
|
||||
const char* mirrorLogContext() const override { return "GetEntryInfoMsgEx/forward"; }
|
||||
};
|
||||
|
||||
66
meta/source/net/message/storage/attribs/GetXAttrMsgEx.cpp
Normal file
66
meta/source/net/message/storage/attribs/GetXAttrMsgEx.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/attribs/GetXAttrRespMsg.h>
|
||||
#include <net/msghelpers/MsgHelperXAttr.h>
|
||||
#include "GetXAttrMsgEx.h"
|
||||
|
||||
bool GetXAttrMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
const char* logContext = "GetXAttrMsg incoming";
|
||||
LogContext(logContext).log(Log_DEBUG , "name: " + this->getName() + "; size: " +
|
||||
StringTk::intToStr(this->getSize()) + ";");
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
FileIDLock GetXAttrMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
return {&store, getEntryInfo()->getEntryID(), false};
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> GetXAttrMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
const char* logContext = "Get XAttr Msg";
|
||||
GetXAttrMsgResponseState resp;
|
||||
|
||||
EntryInfo* entryInfo = this->getEntryInfo();
|
||||
CharVector xAttrValue;
|
||||
FhgfsOpsErr getXAttrRes;
|
||||
ssize_t size = this->getSize();
|
||||
const std::string& name = this->getName();
|
||||
|
||||
App* app = Program::getApp();
|
||||
Config* config = app->getConfig();
|
||||
if (!config->getStoreClientXAttrs())
|
||||
{
|
||||
LogContext(logContext).log(Log_ERR,
|
||||
"Received a GetXAttrMsg, but client-side extended attributes are disabled in config.");
|
||||
|
||||
getXAttrRes = FhgfsOpsErr_NOTSUPP;
|
||||
goto resp;
|
||||
}
|
||||
|
||||
// Clamp buffer size to the maximum the NetMsg can handle, plus one byte in the (unlikely) case
|
||||
// the on-disk metadata is larger than that.
|
||||
if (size > MsgHelperXAttr::MAX_VALUE_SIZE)
|
||||
size = MsgHelperXAttr::MAX_VALUE_SIZE + 1;
|
||||
|
||||
std::tie(getXAttrRes, xAttrValue, size) = MsgHelperXAttr::getxattr(entryInfo, name, size);
|
||||
|
||||
if (size >= MsgHelperXAttr::MAX_VALUE_SIZE + 1 && getXAttrRes == FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
// The xattr on disk is at least one byte too large. In this case, we have to return
|
||||
// an internal error because it won't fit the net message.
|
||||
xAttrValue.clear();
|
||||
getXAttrRes = FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
resp:
|
||||
resp.setGetXAttrValue(xAttrValue);
|
||||
resp.setSize(size);
|
||||
resp.setGetXAttrResult(getXAttrRes);
|
||||
|
||||
app->getNodeOpStats()->updateNodeOp(ctx.getSocket()->getPeerIP(), MetaOpCounter_GETXATTR,
|
||||
getMsgHeaderUserID() );
|
||||
|
||||
return boost::make_unique<ResponseState>(std::move(resp));
|
||||
}
|
||||
74
meta/source/net/message/storage/attribs/GetXAttrMsgEx.h
Normal file
74
meta/source/net/message/storage/attribs/GetXAttrMsgEx.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/attribs/GetXAttrMsg.h>
|
||||
#include <common/net/message/storage/attribs/GetXAttrRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
class GetXAttrMsgResponseState : public MirroredMessageResponseState
|
||||
{
|
||||
public:
|
||||
GetXAttrMsgResponseState() : size(0), returnCode(FhgfsOpsErr_INTERNAL)
|
||||
{
|
||||
}
|
||||
|
||||
GetXAttrMsgResponseState(GetXAttrMsgResponseState&& other) :
|
||||
value(std::move(other.value)),
|
||||
size(other.size),
|
||||
returnCode(other.returnCode)
|
||||
{
|
||||
}
|
||||
|
||||
void sendResponse(NetMessage::ResponseContext& ctx) override
|
||||
{
|
||||
GetXAttrRespMsg resp(value, size, returnCode);
|
||||
ctx.sendResponse(resp);
|
||||
}
|
||||
|
||||
// GetXAttrMsgEx is converted into a mirrored message to utilize MirroredMessage::lock(),
|
||||
// thereby preventing races with operations like unlink. However, forwarding this message
|
||||
// to the secondary is unnecessary. Overriding the changesObservableState() function to
|
||||
// always return false ensures that this message is never forwarded unnecessarily.
|
||||
bool changesObservableState() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void setGetXAttrValue(const CharVector& value) { this->value = value; }
|
||||
void setSize(size_t size) { this->size = size; }
|
||||
void setGetXAttrResult(FhgfsOpsErr result) { this->returnCode = result; }
|
||||
|
||||
protected:
|
||||
uint32_t serializerTag() const override { return NETMSGTYPE_GetXAttr; }
|
||||
void serializeContents(Serializer& ser) const override {}
|
||||
|
||||
private:
|
||||
CharVector value;
|
||||
size_t size;
|
||||
FhgfsOpsErr returnCode;
|
||||
};
|
||||
|
||||
class GetXAttrMsgEx : public MirroredMessage<GetXAttrMsg, FileIDLock>
|
||||
{
|
||||
public:
|
||||
typedef GetXAttrMsgResponseState ResponseState;
|
||||
virtual bool processIncoming(ResponseContext& ctx);
|
||||
FileIDLock lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override
|
||||
{
|
||||
return getEntryInfo()->getIsBuddyMirrored();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override {}
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "GetXAttrMsgEx/forward"; }
|
||||
};
|
||||
|
||||
72
meta/source/net/message/storage/attribs/ListXAttrMsgEx.cpp
Normal file
72
meta/source/net/message/storage/attribs/ListXAttrMsgEx.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/attribs/ListXAttrRespMsg.h>
|
||||
#include <net/msghelpers/MsgHelperXAttr.h>
|
||||
#include "ListXAttrMsgEx.h"
|
||||
|
||||
bool ListXAttrMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
#ifdef BEEGFS_DEBUG
|
||||
const char* logContext = "ListXAttrMsg incoming";
|
||||
#endif // BEEGFS_DEBUG
|
||||
|
||||
LOG_DEBUG(logContext, Log_DEBUG, "size: " + StringTk::intToStr(this->getSize()) + ";");
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
FileIDLock ListXAttrMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
return {&store, getEntryInfo()->getEntryID(), false};
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> ListXAttrMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
const char* logContext = "List XAttr Msg";
|
||||
ListXAttrMsgResponseState resp;
|
||||
|
||||
App* app = Program::getApp();
|
||||
Config* config = app->getConfig();
|
||||
EntryInfo* entryInfo = this->getEntryInfo();
|
||||
size_t listSize = 0;
|
||||
|
||||
StringVector xAttrVec;
|
||||
FhgfsOpsErr listXAttrRes;
|
||||
|
||||
if (!config->getStoreClientXAttrs() )
|
||||
{
|
||||
LogContext(logContext).log(Log_ERR,
|
||||
"Received a ListXAttrMsg, but client-side extended attributes are disabled in config.");
|
||||
|
||||
listXAttrRes = FhgfsOpsErr_NOTSUPP;
|
||||
goto resp;
|
||||
}
|
||||
|
||||
std::tie(listXAttrRes, xAttrVec) = MsgHelperXAttr::listxattr(entryInfo);
|
||||
|
||||
for (StringVectorConstIter it = xAttrVec.begin(); it != xAttrVec.end(); ++it)
|
||||
{
|
||||
// Plus one byte to account for the '\0's separating the list elements in the buffer.
|
||||
listSize += it->size() + 1;
|
||||
}
|
||||
|
||||
// note: MsgHelperXAttr::MAX_SIZE is a ssize_t, which is always positive, so it will always fit
|
||||
// into a size_t
|
||||
if ((listSize >= (size_t)MsgHelperXAttr::MAX_VALUE_SIZE + 1)
|
||||
&& (listXAttrRes == FhgfsOpsErr_SUCCESS))
|
||||
{
|
||||
// The xattr list on disk is at least one byte too large. In this case, we have to return
|
||||
// an internal error because it won't fit the net message.
|
||||
xAttrVec.clear();
|
||||
listXAttrRes = FhgfsOpsErr_TOOBIG;
|
||||
}
|
||||
|
||||
resp:
|
||||
resp.setListXAttrValue(xAttrVec);
|
||||
resp.setSize(listSize);
|
||||
resp.setListXAttrResult(listXAttrRes);
|
||||
|
||||
app->getNodeOpStats()->updateNodeOp(ctx.getSocket()->getPeerIP(), MetaOpCounter_LISTXATTR,
|
||||
getMsgHeaderUserID() );
|
||||
|
||||
return boost::make_unique<ResponseState>(std::move(resp));
|
||||
}
|
||||
74
meta/source/net/message/storage/attribs/ListXAttrMsgEx.h
Normal file
74
meta/source/net/message/storage/attribs/ListXAttrMsgEx.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/attribs/ListXAttrMsg.h>
|
||||
#include <common/net/message/storage/attribs/ListXAttrRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
class ListXAttrMsgResponseState : public MirroredMessageResponseState
|
||||
{
|
||||
public:
|
||||
ListXAttrMsgResponseState() : size(0), returnCode(FhgfsOpsErr_INTERNAL)
|
||||
{
|
||||
}
|
||||
|
||||
ListXAttrMsgResponseState(ListXAttrMsgResponseState&& other) :
|
||||
value(std::move(other.value)),
|
||||
size(other.size),
|
||||
returnCode(other.returnCode)
|
||||
{
|
||||
}
|
||||
|
||||
void sendResponse(NetMessage::ResponseContext& ctx) override
|
||||
{
|
||||
ListXAttrRespMsg resp(value, size, returnCode);
|
||||
ctx.sendResponse(resp);
|
||||
}
|
||||
|
||||
// ListXAttrMsgEx is transformed into a mirrored message to leverage MirroredMessage::lock(),
|
||||
// preventing races with operations such as unlink. However, forwarding this message to the
|
||||
// secondary is unnecessary. Overriding the changesObservableState() function to always
|
||||
// return false ensures that this message is never forwarded unnecessarily.
|
||||
bool changesObservableState() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void setListXAttrValue(const StringVector& value) { this->value = value; }
|
||||
void setSize(size_t size) { this->size = size; }
|
||||
void setListXAttrResult(FhgfsOpsErr result) { this->returnCode = result; }
|
||||
|
||||
protected:
|
||||
uint32_t serializerTag() const override { return NETMSGTYPE_ListXAttr; }
|
||||
void serializeContents(Serializer& ser) const override {}
|
||||
|
||||
private:
|
||||
StringVector value;
|
||||
size_t size;
|
||||
FhgfsOpsErr returnCode;
|
||||
};
|
||||
|
||||
class ListXAttrMsgEx : public MirroredMessage<ListXAttrMsg, FileIDLock>
|
||||
{
|
||||
public:
|
||||
typedef ListXAttrMsgResponseState ResponseState;
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
FileIDLock lock(EntryLockStore& store) override;
|
||||
bool isMirrored() override
|
||||
{
|
||||
return getEntryInfo()->getIsBuddyMirrored();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override {}
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "ListXAttrMsgEx/forward"; }
|
||||
};
|
||||
|
||||
104
meta/source/net/message/storage/attribs/RefreshEntryInfoMsg.cpp
Normal file
104
meta/source/net/message/storage/attribs/RefreshEntryInfoMsg.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include <common/net/message/storage/attribs/RefreshEntryInfoRespMsg.h>
|
||||
#include <common/storage/striping/Raid0Pattern.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <net/msghelpers/MsgHelperStat.h>
|
||||
#include <common/storage/EntryInfo.h>
|
||||
#include <program/Program.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include "RefreshEntryInfoMsgEx.h"
|
||||
|
||||
|
||||
bool RefreshEntryInfoMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
LogContext log("RefreshEntryInfoMsgEx incoming");
|
||||
|
||||
LOG_DEBUG_CONTEXT(log, Log_DEBUG, "Received a RefreshEntryInfoMsg from: " + ctx.peerName() );
|
||||
|
||||
LOG_DEBUG("RefreshEntryInfoMsgEx::processIncoming", Log_SPAM,
|
||||
"ParentID: " + getEntryInfo()->getParentEntryID() + " EntryID: " +
|
||||
getEntryInfo()->getEntryID() + " BuddyMirrored: " +
|
||||
(getEntryInfo()->getIsBuddyMirrored() ? "Yes" : "No") + " Secondary: " +
|
||||
(hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond) ? "Yes" : "No"));
|
||||
|
||||
BaseType::processIncoming(ctx);
|
||||
|
||||
updateNodeOp(ctx, MetaOpCounter_REFRESHENTRYINFO);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::tuple<FileIDLock, FileIDLock> RefreshEntryInfoMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
if (DirEntryType_ISDIR(getEntryInfo()->getEntryType()))
|
||||
return std::make_tuple(
|
||||
FileIDLock(),
|
||||
FileIDLock(&store, getEntryInfo()->getEntryID(), true));
|
||||
else
|
||||
return std::make_tuple(
|
||||
FileIDLock(&store, getEntryInfo()->getEntryID(), true),
|
||||
FileIDLock());
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> RefreshEntryInfoMsgEx::executeLocally(
|
||||
ResponseContext& ctx, bool isSecondary)
|
||||
{
|
||||
if (getEntryInfo()->getParentEntryID().empty()) // special case: get info for root directory
|
||||
return boost::make_unique<ResponseState>(refreshInfoRoot());
|
||||
else
|
||||
return boost::make_unique<ResponseState>(refreshInfoRec());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param outPattern StripePattern clone (in case of success), that must be deleted by the caller
|
||||
*/
|
||||
FhgfsOpsErr RefreshEntryInfoMsgEx::refreshInfoRec()
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
EntryInfo* entryInfo = getEntryInfo();
|
||||
|
||||
DirInode* dir = metaStore->referenceDir(entryInfo->getEntryID(),
|
||||
entryInfo->getIsBuddyMirrored(), true);
|
||||
if(dir)
|
||||
{ // entry is a directory
|
||||
dir->refreshMetaInfo();
|
||||
metaStore->releaseDir(entryInfo->getEntryID() );
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
// not a dir => try file
|
||||
auto refreshRes = MsgHelperStat::refreshDynAttribs(entryInfo, true, getMsgHeaderUserID());
|
||||
if (refreshRes == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
{
|
||||
auto [file, referenceRes] = metaStore->referenceFile(getEntryInfo());
|
||||
if (file)
|
||||
fixInodeTimestamp(*file, fileTimestamps, getEntryInfo());
|
||||
|
||||
metaStore->releaseFile(getEntryInfo()->getParentEntryID(), file);
|
||||
}
|
||||
|
||||
return refreshRes;
|
||||
}
|
||||
|
||||
FhgfsOpsErr RefreshEntryInfoMsgEx::refreshInfoRoot()
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
DirInode* rootDir = app->getRootDir();
|
||||
|
||||
NumNodeID expectedOwnerNode = rootDir->getIsBuddyMirrored()
|
||||
? NumNodeID(app->getMetaBuddyGroupMapper()->getLocalGroupID() )
|
||||
: app->getLocalNode().getNumID();
|
||||
|
||||
if ( expectedOwnerNode != rootDir->getOwnerNodeID() )
|
||||
return FhgfsOpsErr_NOTOWNER;
|
||||
|
||||
rootDir->refreshMetaInfo();
|
||||
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
void RefreshEntryInfoMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_RefreshEntryInfoResp);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <storage/DirInode.h>
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/attribs/RefreshEntryInfoMsg.h>
|
||||
#include <common/net/message/storage/attribs/RefreshEntryInfoRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
// Update entry info, called by fsck or by fhgfs-ctl
|
||||
|
||||
class RefreshEntryInfoMsgEx : public MirroredMessage<RefreshEntryInfoMsg,
|
||||
std::tuple<FileIDLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<RefreshEntryInfoRespMsg, NETMSGTYPE_RefreshEntryInfo>
|
||||
ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<FileIDLock, FileIDLock> lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
FhgfsOpsErr refreshInfoRec();
|
||||
FhgfsOpsErr refreshInfoRoot();
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<RefreshEntryInfoRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "RefreshEntryInfoMsgEx/forward"; }
|
||||
};
|
||||
|
||||
|
||||
86
meta/source/net/message/storage/attribs/RemoveXAttrMsgEx.cpp
Normal file
86
meta/source/net/message/storage/attribs/RemoveXAttrMsgEx.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/attribs/RemoveXAttrRespMsg.h>
|
||||
#include <net/msghelpers/MsgHelperXAttr.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include "RemoveXAttrMsgEx.h"
|
||||
|
||||
|
||||
bool RemoveXAttrMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
#ifdef BEEGFS_DEBUG
|
||||
const char* logContext = "RemoveXAttrMsg incoming";
|
||||
EntryInfo* entryInfo = this->getEntryInfo();
|
||||
const std::string& name = this->getName();
|
||||
|
||||
LOG_DEBUG(logContext, Log_DEBUG, "name: " + name + ";");
|
||||
|
||||
LOG_DEBUG("RemoveXAttrMsgEx::processIncoming", Log_DEBUG,
|
||||
"ParentID: " + entryInfo->getParentEntryID() + " EntryID: " +
|
||||
entryInfo->getEntryID() + " BuddyMirrored: " +
|
||||
(entryInfo->getIsBuddyMirrored() ? "Yes" : "No") + " Secondary: " +
|
||||
(hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond) ? "Yes" : "No"));
|
||||
#endif
|
||||
|
||||
BaseType::processIncoming(ctx);
|
||||
|
||||
updateNodeOp(ctx, MetaOpCounter_REMOVEXATTR);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::tuple<FileIDLock, FileIDLock> RemoveXAttrMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
if (getEntryInfo()->getEntryType() == DirEntryType_DIRECTORY)
|
||||
return std::make_tuple(
|
||||
FileIDLock(),
|
||||
FileIDLock(&store, getEntryInfo()->getEntryID(), true));
|
||||
else
|
||||
return std::make_tuple(
|
||||
FileIDLock(&store, getEntryInfo()->getEntryID(), true),
|
||||
FileIDLock());
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> RemoveXAttrMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
if (!Program::getApp()->getConfig()->getStoreClientXAttrs() ) // xattrs disabled in config
|
||||
{
|
||||
LOG(GENERAL, ERR, "Received a RemoveXAttrMsg, "
|
||||
"but client-side extended attributes are disabled in config.");
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_NOTSUPP);
|
||||
}
|
||||
|
||||
auto rmRes = MsgHelperXAttr::removexattr(getEntryInfo(), getName());
|
||||
|
||||
if (rmRes == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
if (DirEntryType_ISDIR(getEntryInfo()->getEntryType()))
|
||||
{
|
||||
auto dir = metaStore->referenceDir(getEntryInfo()->getEntryID(),
|
||||
getEntryInfo()->getIsBuddyMirrored(), true);
|
||||
if (dir)
|
||||
{
|
||||
fixInodeTimestamp(*dir, inodeTimestamps);
|
||||
metaStore->releaseDir(dir->getID());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto [file, referenceRes] = metaStore->referenceFile(getEntryInfo());
|
||||
if (file)
|
||||
{
|
||||
fixInodeTimestamp(*file, inodeTimestamps, getEntryInfo());
|
||||
metaStore->releaseFile(getEntryInfo()->getParentEntryID(), file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(rmRes);
|
||||
}
|
||||
|
||||
void RemoveXAttrMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_RemoveXAttrResp);
|
||||
}
|
||||
32
meta/source/net/message/storage/attribs/RemoveXAttrMsgEx.h
Normal file
32
meta/source/net/message/storage/attribs/RemoveXAttrMsgEx.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/attribs/RemoveXAttrMsg.h>
|
||||
#include <common/net/message/storage/attribs/RemoveXAttrRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
class RemoveXAttrMsgEx : public MirroredMessage<RemoveXAttrMsg, std::tuple<FileIDLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<RemoveXAttrRespMsg, NETMSGTYPE_RemoveXAttr> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<FileIDLock, FileIDLock> lock(EntryLockStore& store) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
bool isMirrored() override { return getEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<RemoveXAttrRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "RemoveXAttrMsgEx/forward"; }
|
||||
};
|
||||
|
||||
|
||||
436
meta/source/net/message/storage/attribs/SetAttrMsgEx.cpp
Normal file
436
meta/source/net/message/storage/attribs/SetAttrMsgEx.cpp
Normal file
@@ -0,0 +1,436 @@
|
||||
#include <common/components/streamlistenerv2/IncomingPreprocessedMsgWork.h>
|
||||
#include <common/net/message/control/GenericResponseMsg.h>
|
||||
#include <common/net/message/storage/attribs/SetAttrRespMsg.h>
|
||||
#include <common/net/message/storage/attribs/SetLocalAttrMsg.h>
|
||||
#include <common/net/message/storage/attribs/SetLocalAttrRespMsg.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <components/FileEventLogger.h>
|
||||
#include <components/worker/SetChunkFileAttribsWork.h>
|
||||
#include <program/Program.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include "SetAttrMsgEx.h"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
|
||||
bool SetAttrMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
#ifdef BEEGFS_DEBUG
|
||||
const char* logContext = "SetAttrMsg incoming";
|
||||
#endif // BEEGFS_DEBUG
|
||||
|
||||
EntryInfo* entryInfo = getEntryInfo();
|
||||
|
||||
LOG_DEBUG(logContext, Log_DEBUG,
|
||||
"BuddyMirrored: " + std::string(entryInfo->getIsBuddyMirrored() ? "Yes" : "No") +
|
||||
" Secondary: " + std::string(hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond)
|
||||
? "Yes" : "No") );
|
||||
(void) entryInfo;
|
||||
|
||||
// update operation counters (here on top because we have an early sock release in this msg)
|
||||
updateNodeOp(ctx, MetaOpCounter_SETATTR);
|
||||
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
std::tuple<FileIDLock, FileIDLock> SetAttrMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
if (DirEntryType_ISDIR(getEntryInfo()->getEntryType()))
|
||||
return std::make_tuple(
|
||||
FileIDLock(),
|
||||
FileIDLock(&store, getEntryInfo()->getEntryID(), true));
|
||||
else
|
||||
return std::make_tuple(
|
||||
FileIDLock(&store, getEntryInfo()->getEntryID(), true),
|
||||
FileIDLock());
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> SetAttrMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
const char* logContext = "Set file attribs";
|
||||
|
||||
App* app = Program::getApp();
|
||||
MetaStore* metaStore = app->getMetaStore();
|
||||
Config* cfg = app->getConfig();
|
||||
|
||||
EntryInfo* entryInfo = getEntryInfo();
|
||||
FhgfsOpsErr setAttrRes;
|
||||
const bool forwardToStorage = !entryInfo->getIsBuddyMirrored()
|
||||
|| !hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond);
|
||||
|
||||
bool responseSent = false;
|
||||
|
||||
if (entryInfo->getParentEntryID().empty() || DirEntryType_ISDIR(entryInfo->getEntryType()))
|
||||
{
|
||||
// special case: setAttr for root directory
|
||||
if (entryInfo->getParentEntryID().empty())
|
||||
setAttrRes = setAttrRoot();
|
||||
else
|
||||
setAttrRes = metaStore->setAttr(entryInfo, getValidAttribs(), getAttribs());
|
||||
|
||||
if (setAttrRes != FhgfsOpsErr_SUCCESS || !(shouldFixTimestamps() || getFileEvent()))
|
||||
return boost::make_unique<ResponseState>(setAttrRes);
|
||||
|
||||
if (shouldFixTimestamps())
|
||||
{
|
||||
auto dir = metaStore->referenceDir(entryInfo->getEntryID(),
|
||||
entryInfo->getIsBuddyMirrored(), true);
|
||||
if (dir)
|
||||
{
|
||||
fixInodeTimestamp(*dir, inodeTimestamps);
|
||||
metaStore->releaseDir(dir->getID());
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSecondary && getFileEvent() && app->getFileEventLogger() && getFileEvent())
|
||||
{
|
||||
unsigned numHardlinks = 0;
|
||||
auto dir = metaStore->referenceDir(entryInfo->getEntryID(),
|
||||
entryInfo->getIsBuddyMirrored(), true);
|
||||
if (likely(dir))
|
||||
{
|
||||
numHardlinks = dir->getNumHardlinks();
|
||||
metaStore->releaseDir(dir->getID());
|
||||
}
|
||||
|
||||
EventContext eventCtx = makeEventContext(entryInfo, entryInfo->getParentEntryID(),
|
||||
getMsgHeaderUserID(), "", numHardlinks, isSecondary);
|
||||
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(setAttrRes);
|
||||
}
|
||||
|
||||
// update nlink count if requested by caller
|
||||
if (isMsgHeaderFeatureFlagSet(SETATTRMSG_FLAG_INCR_NLINKCNT) &&
|
||||
isMsgHeaderFeatureFlagSet(SETATTRMSG_FLAG_DECR_NLINKCNT))
|
||||
{
|
||||
// error: both increase and decrease of nlink count was requested
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_INTERNAL);
|
||||
}
|
||||
else if (isMsgHeaderFeatureFlagSet(SETATTRMSG_FLAG_INCR_NLINKCNT) ||
|
||||
isMsgHeaderFeatureFlagSet(SETATTRMSG_FLAG_DECR_NLINKCNT))
|
||||
{
|
||||
int val = isMsgHeaderFeatureFlagSet(SETATTRMSG_FLAG_INCR_NLINKCNT) ? 1 : -1;
|
||||
setAttrRes = metaStore->incDecLinkCount(entryInfo, val);
|
||||
return boost::make_unique<ResponseState>(setAttrRes);
|
||||
}
|
||||
|
||||
// we need to reference the inode first, as we want to use it several times
|
||||
auto [inode, referenceRes] = metaStore->referenceFile(entryInfo);
|
||||
if (!inode)
|
||||
return boost::make_unique<ResponseState>(referenceRes);
|
||||
|
||||
// in the following we need to distinguish between several cases.
|
||||
// 1. if times shall be updated we need to send the update to the storage servers first
|
||||
// because, we need to rely on storage server's attrib version to prevent races with
|
||||
// other messages that update the times (e.g. Close)
|
||||
// 2. if times shall not be updated (must be chmod or chown then) and quota is enabled
|
||||
// we first set the local attributes and then send the update to the storage server.
|
||||
// if an early response optimization is set in this case we send the response between
|
||||
// these two steps
|
||||
// 3. no times update (i.e. chmod or chown) and quota is disabled => only update locally,
|
||||
// as we don't have a reason to waste time with contacting the storage servers
|
||||
|
||||
bool timeUpdate =
|
||||
getValidAttribs() & (SETATTR_CHANGE_MODIFICATIONTIME | SETATTR_CHANGE_LASTACCESSTIME);
|
||||
|
||||
// note: time update and mode/owner update can never be at the same time
|
||||
if (timeUpdate && forwardToStorage)
|
||||
{
|
||||
// only relevant if message needs to be sent to the storage server. if not (e.g.
|
||||
// because this is secondary of a buddy group, we can use the "default" case later
|
||||
setAttrRes = setChunkFileAttribs(*inode, true);
|
||||
|
||||
if (setAttrRes == FhgfsOpsErr_SUCCESS)
|
||||
setAttrRes = metaStore->setAttr(entryInfo, getValidAttribs(), getAttribs());
|
||||
}
|
||||
else if (isMsgHeaderFeatureFlagSet(SETATTRMSG_FLAG_USE_QUOTA) && forwardToStorage)
|
||||
{
|
||||
const UInt16Vector* targetIdVec = inode->getStripePattern()->getStripeTargetIDs();
|
||||
ExceededQuotaStorePtr quotaExStore;
|
||||
for (auto targetIter = targetIdVec->begin(); targetIter != targetIdVec->end(); targetIter++)
|
||||
{
|
||||
if (inode->getStripePattern()->getPatternType() == StripePatternType_BuddyMirror)
|
||||
{
|
||||
uint16_t primaryTargetID = app->getStorageBuddyGroupMapper()->getPrimaryTargetID(*targetIter);
|
||||
quotaExStore = app->getExceededQuotaStores()->get(primaryTargetID);
|
||||
}
|
||||
else
|
||||
{
|
||||
// this is not very efficient at the moment, as we need to look at every quota exceeded
|
||||
// store for every target in the stripe pattern; we need to do that because storage pools
|
||||
// might change over time, i.e. the pool that is stored in the metadata doesn't always
|
||||
// match the actual targets a file is stored on
|
||||
quotaExStore = app->getExceededQuotaStores()->get(*targetIter);
|
||||
}
|
||||
|
||||
// check if exceeded quotas exists, before doing a more expensive and explicit check
|
||||
if (quotaExStore && quotaExStore->someQuotaExceeded())
|
||||
{ // store for this target is present AND someQuotaExceeded() was true
|
||||
QuotaExceededErrorType quotaExceeded = quotaExStore->isQuotaExceeded(
|
||||
getAttribs()->userID, getAttribs()->groupID);
|
||||
|
||||
if (quotaExceeded != QuotaExceededErrorType_NOT_EXCEEDED)
|
||||
{
|
||||
LogContext(logContext).log(Log_NOTICE,
|
||||
QuotaData::QuotaExceededErrorTypeToString(quotaExceeded) + " "
|
||||
"UID: " + StringTk::uintToStr(getAttribs()->userID) + "; "
|
||||
"GID: " + StringTk::uintToStr(getAttribs()->groupID));
|
||||
|
||||
setAttrRes = FhgfsOpsErr_DQUOT;
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only relevant if message needs to be sent to the storage server. if not (e.g.
|
||||
// because this is secondary of a buddy group, we can use the "default" case later
|
||||
setAttrRes = metaStore->setAttr(entryInfo, getValidAttribs(), getAttribs());
|
||||
|
||||
// allowed only if early chown respnse for quota is set
|
||||
if (cfg->getQuotaEarlyChownResponse())
|
||||
{
|
||||
earlyComplete(ctx, ResponseState(setAttrRes));
|
||||
responseSent = true;
|
||||
}
|
||||
|
||||
if (setAttrRes == FhgfsOpsErr_SUCCESS)
|
||||
setChunkFileAttribs(*inode, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
setAttrRes = metaStore->setAttr(entryInfo, getValidAttribs(), getAttribs());
|
||||
}
|
||||
|
||||
finish:
|
||||
if (shouldFixTimestamps())
|
||||
fixInodeTimestamp(*inode, inodeTimestamps, entryInfo);
|
||||
|
||||
if (!isSecondary && setAttrRes == FhgfsOpsErr_SUCCESS &&
|
||||
app->getFileEventLogger() && getFileEvent())
|
||||
{
|
||||
EventContext eventCtx = makeEventContext(
|
||||
entryInfo,
|
||||
entryInfo->getParentEntryID(),
|
||||
getMsgHeaderUserID(),
|
||||
"",
|
||||
inode->getNumHardlinks(),
|
||||
isSecondary
|
||||
);
|
||||
|
||||
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
|
||||
metaStore->releaseFile(entryInfo->getParentEntryID(), inode);
|
||||
|
||||
if (!responseSent)
|
||||
return boost::make_unique<ResponseState>(setAttrRes);
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
void SetAttrMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_SetAttrResp);
|
||||
}
|
||||
|
||||
FhgfsOpsErr SetAttrMsgEx::setAttrRoot()
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
DirInode* rootDir = app->getRootDir();
|
||||
|
||||
NumNodeID expectedOwnerNode = rootDir->getIsBuddyMirrored()
|
||||
? NumNodeID(app->getMetaBuddyGroupMapper()->getLocalGroupID() )
|
||||
: app->getLocalNode().getNumID();
|
||||
|
||||
if ( expectedOwnerNode != rootDir->getOwnerNodeID() )
|
||||
return FhgfsOpsErr_NOTOWNER;
|
||||
|
||||
if(!rootDir->setAttrData(getValidAttribs(), getAttribs() ) )
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
|
||||
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
FhgfsOpsErr SetAttrMsgEx::setChunkFileAttribs(FileInode& file, bool requestDynamicAttribs)
|
||||
{
|
||||
StripePattern* pattern = file.getStripePattern();
|
||||
|
||||
if( (pattern->getStripeTargetIDs()->size() > 1) ||
|
||||
(pattern->getPatternType() == StripePatternType_BuddyMirror) )
|
||||
return setChunkFileAttribsParallel(file, requestDynamicAttribs);
|
||||
else
|
||||
return setChunkFileAttribsSequential(file, requestDynamicAttribs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: This method does not work for mirrored files; use setChunkFileAttribsParallel() for those.
|
||||
*/
|
||||
FhgfsOpsErr SetAttrMsgEx::setChunkFileAttribsSequential(FileInode& inode,
|
||||
bool requestDynamicAttribs)
|
||||
{
|
||||
const char* logContext = "Set chunk file attribs S";
|
||||
|
||||
StripePattern* pattern = inode.getStripePattern();
|
||||
const UInt16Vector* targetIDs = pattern->getStripeTargetIDs();
|
||||
TargetMapper* targetMapper = Program::getApp()->getTargetMapper();
|
||||
TargetStateStore* targetStates = Program::getApp()->getTargetStateStore();
|
||||
NodeStore* nodes = Program::getApp()->getStorageNodes();
|
||||
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
|
||||
|
||||
std::string fileID(inode.getEntryID());
|
||||
|
||||
PathInfo pathInfo;
|
||||
inode.getPathInfo(&pathInfo);
|
||||
|
||||
// send request to each node and receive the response message
|
||||
unsigned currentTargetIndex = 0;
|
||||
for(UInt16VectorConstIter iter = targetIDs->begin();
|
||||
iter != targetIDs->end();
|
||||
iter++, currentTargetIndex++)
|
||||
{
|
||||
uint16_t targetID = *iter;
|
||||
bool enableFileCreation = (currentTargetIndex == 0); // enable inode creation of first node
|
||||
|
||||
SetLocalAttrMsg setAttrMsg(fileID, targetID, &pathInfo, getValidAttribs(), getAttribs(),
|
||||
enableFileCreation);
|
||||
|
||||
setAttrMsg.setMsgHeaderUserID(inode.getUserID());
|
||||
|
||||
if(isMsgHeaderFeatureFlagSet(SETATTRMSG_FLAG_USE_QUOTA))
|
||||
setAttrMsg.addMsgHeaderFeatureFlag(SETLOCALATTRMSG_FLAG_USE_QUOTA);
|
||||
|
||||
RequestResponseArgs rrArgs(NULL, &setAttrMsg, NETMSGTYPE_SetLocalAttrResp);
|
||||
RequestResponseTarget rrTarget(targetID, targetMapper, nodes);
|
||||
|
||||
rrTarget.setTargetStates(targetStates);
|
||||
|
||||
// send request to node and receive response
|
||||
FhgfsOpsErr requestRes = MessagingTk::requestResponseTarget(&rrTarget, &rrArgs);
|
||||
|
||||
if(requestRes != FhgfsOpsErr_SUCCESS)
|
||||
{ // communication error
|
||||
LogContext(logContext).log(Log_WARNING,
|
||||
"Communication with storage target failed: " + StringTk::uintToStr(targetID) + "; "
|
||||
"fileID: " + inode.getEntryID() + "; "
|
||||
"Error: " + boost::lexical_cast<std::string>(requestRes));
|
||||
|
||||
if(retVal == FhgfsOpsErr_SUCCESS)
|
||||
retVal = requestRes;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// correct response type received
|
||||
const auto setRespMsg = (const SetLocalAttrRespMsg*)rrArgs.outRespMsg.get();
|
||||
|
||||
FhgfsOpsErr setRespResult = setRespMsg->getResult();
|
||||
if (setRespResult != FhgfsOpsErr_SUCCESS)
|
||||
{ // error: local inode attribs not set
|
||||
LogContext(logContext).log(Log_WARNING,
|
||||
"Target failed to set attribs of chunk file: " + StringTk::uintToStr(targetID) + "; "
|
||||
"fileID: " + inode.getEntryID());
|
||||
|
||||
if(retVal == FhgfsOpsErr_SUCCESS)
|
||||
retVal = setRespResult;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// success: local inode attribs set
|
||||
if (setRespMsg->isMsgHeaderFeatureFlagSet(SETLOCALATTRRESPMSG_FLAG_HAS_ATTRS))
|
||||
{
|
||||
DynamicFileAttribsVec dynAttribsVec(1);
|
||||
setRespMsg->getDynamicAttribs(&(dynAttribsVec[0]));
|
||||
inode.setDynAttribs(dynAttribsVec);
|
||||
}
|
||||
|
||||
LOG_DEBUG(logContext, Log_DEBUG,
|
||||
"Target has set attribs of chunk file: " + StringTk::uintToStr(targetID) + "; " +
|
||||
"fileID: " + inode.getEntryID());
|
||||
}
|
||||
|
||||
if(unlikely(retVal != FhgfsOpsErr_SUCCESS) )
|
||||
LogContext(logContext).log(Log_WARNING,
|
||||
"Problems occurred during setting of chunk file attribs. "
|
||||
"fileID: " + inode.getEntryID());
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
FhgfsOpsErr SetAttrMsgEx::setChunkFileAttribsParallel(FileInode& inode, bool requestDynamicAttribs)
|
||||
{
|
||||
const char* logContext = "Set chunk file attribs";
|
||||
|
||||
App* app = Program::getApp();
|
||||
MultiWorkQueue* slaveQ = app->getCommSlaveQueue();
|
||||
StripePattern* pattern = inode.getStripePattern();
|
||||
const UInt16Vector* targetIDs = pattern->getStripeTargetIDs();
|
||||
|
||||
size_t numTargetWorks = targetIDs->size();
|
||||
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
|
||||
DynamicFileAttribsVec dynAttribsVec(numTargetWorks);
|
||||
FhgfsOpsErrVec nodeResults(numTargetWorks);
|
||||
SynchronizedCounter counter;
|
||||
|
||||
PathInfo pathInfo;
|
||||
inode.getPathInfo(&pathInfo);
|
||||
|
||||
// generate work for storage targets...
|
||||
|
||||
for(size_t i=0; i < numTargetWorks; i++)
|
||||
{
|
||||
bool enableFileCreation = (i == 0); // enable inode creation on first target
|
||||
|
||||
SetChunkFileAttribsWork* work = NULL;
|
||||
if (requestDynamicAttribs) // we are interested in the chunk's dynamic attributes, because we
|
||||
{ // modify timestamps and this operation might race with others
|
||||
work = new SetChunkFileAttribsWork(inode.getEntryID(), getValidAttribs(), getAttribs(),
|
||||
enableFileCreation, pattern, (*targetIDs)[i], &pathInfo, &(dynAttribsVec[i]),
|
||||
&(nodeResults[i]), &counter);
|
||||
}
|
||||
else
|
||||
{
|
||||
work = new SetChunkFileAttribsWork(inode.getEntryID(), getValidAttribs(), getAttribs(),
|
||||
enableFileCreation, pattern, (*targetIDs)[i], &pathInfo, NULL, &(nodeResults[i]),
|
||||
&counter);
|
||||
}
|
||||
|
||||
work->setQuotaChown(isMsgHeaderFeatureFlagSet(SETATTRMSG_FLAG_USE_QUOTA) );
|
||||
work->setMsgUserID(getMsgHeaderUserID() );
|
||||
|
||||
slaveQ->addDirectWork(work);
|
||||
}
|
||||
|
||||
// wait for work completion...
|
||||
counter.waitForCount(numTargetWorks);
|
||||
|
||||
// we set the dynamic attribs here, no matter if the remote operation suceeded or not. If it
|
||||
// did not, storageVersion will be zero and the corresponding data will be ignored
|
||||
// note: if the chunk's attributes were not requested from server at all, this is also OK here,
|
||||
// because the storageVersion will be 0
|
||||
inode.setDynAttribs(dynAttribsVec);
|
||||
|
||||
// check target results...
|
||||
for(size_t i=0; i < numTargetWorks; i++)
|
||||
{
|
||||
if(unlikely(nodeResults[i] != FhgfsOpsErr_SUCCESS) )
|
||||
{
|
||||
LogContext(logContext).log(Log_WARNING,
|
||||
"Problems occurred during setting of chunk file attribs. "
|
||||
"fileID: " + inode.getEntryID());
|
||||
|
||||
retVal = nodeResults[i];
|
||||
goto error_exit;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
error_exit:
|
||||
return retVal;
|
||||
}
|
||||
38
meta/source/net/message/storage/attribs/SetAttrMsgEx.h
Normal file
38
meta/source/net/message/storage/attribs/SetAttrMsgEx.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <storage/MetaStore.h>
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/attribs/SetAttrMsg.h>
|
||||
#include <common/net/message/storage/attribs/SetAttrRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
class SetAttrMsgEx : public MirroredMessage<SetAttrMsg, std::tuple<FileIDLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<SetAttrRespMsg, NETMSGTYPE_SetAttr> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<FileIDLock, FileIDLock> lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr setAttrRoot();
|
||||
FhgfsOpsErr setChunkFileAttribs(FileInode& file, bool requestDynamicAttribs);
|
||||
FhgfsOpsErr setChunkFileAttribsSequential(FileInode& inode, bool requestDynamicAttribs);
|
||||
FhgfsOpsErr setChunkFileAttribsParallel(FileInode& inode, bool requestDynamicAttribs);
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<SetAttrRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "SetAttrMsgEx/forward"; }
|
||||
};
|
||||
|
||||
|
||||
127
meta/source/net/message/storage/attribs/SetDirPatternMsgEx.cpp
Normal file
127
meta/source/net/message/storage/attribs/SetDirPatternMsgEx.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
#include <common/net/message/storage/attribs/SetDirPatternRespMsg.h>
|
||||
#include <common/storage/striping/Raid0Pattern.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <program/Program.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <storage/DirInode.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include "SetDirPatternMsgEx.h"
|
||||
|
||||
|
||||
bool SetDirPatternMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
EntryInfo* entryInfo = this->getEntryInfo();
|
||||
|
||||
LOG_DEBUG("SetDirPatternMsgEx::processIncoming", Log_SPAM,
|
||||
"parentEntryID: " + entryInfo->getParentEntryID() + " EntryID: " +
|
||||
entryInfo->getEntryID() + " BuddyMirrored: " +
|
||||
(entryInfo->getIsBuddyMirrored() ? "Yes" : "No") + " Secondary: " +
|
||||
(hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond) ? "Yes" : "No"));
|
||||
(void) entryInfo;
|
||||
|
||||
BaseType::processIncoming(ctx);
|
||||
|
||||
updateNodeOp(ctx, MetaOpCounter_SETDIRPATTERN);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> SetDirPatternMsgEx::executeLocally(
|
||||
ResponseContext& ctx, bool isSecondary)
|
||||
{
|
||||
const char* logContext = "SetDirPatternMsg (set dir pattern)";
|
||||
|
||||
App* app = Program::getApp();
|
||||
MetaStore* metaStore = app->getMetaStore();
|
||||
|
||||
|
||||
EntryInfo* entryInfo = getEntryInfo();
|
||||
StripePattern* pattern = &getPattern();
|
||||
RemoteStorageTarget* rst = getRemoteStorageTarget();
|
||||
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_NOTADIR;
|
||||
|
||||
uint32_t actorUID = isMsgHeaderFeatureFlagSet(Flags::HAS_UID)
|
||||
? getUID()
|
||||
: 0;
|
||||
|
||||
if (actorUID != 0 && !app->getConfig()->getSysAllowUserSetPattern())
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PERM);
|
||||
|
||||
if (pattern->getChunkSize() < STRIPEPATTERN_MIN_CHUNKSIZE ||
|
||||
!MathTk::isPowerOfTwo(pattern->getChunkSize()))
|
||||
{ // check of stripe pattern details validity failed
|
||||
LOG(GENERAL, ERR, "Received an invalid pattern chunksize",
|
||||
("chunkSize", pattern->getChunkSize()));
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_INTERNAL);
|
||||
}
|
||||
|
||||
// verify owner of root dir
|
||||
if (entryInfo->getEntryID() == META_ROOTDIR_ID_STR)
|
||||
{
|
||||
const bool isMirrored = entryInfo->getIsBuddyMirrored();
|
||||
const NumNodeID rootOwnerID = app->getRootDir()->getOwnerNodeID();
|
||||
const NumNodeID localGroupID(app->getMetaBuddyGroupMapper()->getLocalGroupID());
|
||||
|
||||
if ((!isMirrored && rootOwnerID != app->getLocalNodeNumID())
|
||||
|| (isMirrored && rootOwnerID != localGroupID))
|
||||
{
|
||||
LogContext(logContext).log(Log_DEBUG, "This node does not own the root directory.");
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_NOTOWNER);
|
||||
}
|
||||
}
|
||||
|
||||
DirInode* dir = metaStore->referenceDir(entryInfo->getEntryID(), entryInfo->getIsBuddyMirrored(),
|
||||
true);
|
||||
|
||||
if (unlikely(!dir))
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
|
||||
|
||||
// entry is a directory
|
||||
retVal = dir->setStripePattern(*pattern, actorUID);
|
||||
if (retVal != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LogContext(logContext).logErr("Update of stripe pattern failed. "
|
||||
"DirID: " + entryInfo->getEntryID());
|
||||
metaStore->releaseDir(entryInfo->getEntryID());
|
||||
return boost::make_unique<ResponseState>(retVal);
|
||||
}
|
||||
|
||||
// Ignore if the request did not contain RST configuration:
|
||||
if (!rst->hasInvalidVersion())
|
||||
{
|
||||
auto const& rstIDs = rst->getRstIdVector();
|
||||
if (rstIDs.empty())
|
||||
{
|
||||
// Empty RST ID list indicates a request to clear/unset RSTs for this directory.
|
||||
retVal = dir->clearRemoteStorageTarget();
|
||||
if (retVal != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LogContext(logContext).logErr("Failed to clear RST info; "
|
||||
"DirID: " + entryInfo->getEntryID());
|
||||
metaStore->releaseDir(entryInfo->getEntryID());
|
||||
return boost::make_unique<ResponseState>(retVal);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
retVal = dir->setRemoteStorageTarget(*rst);
|
||||
if (retVal != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LogContext(logContext).logErr("Storing remote storage targets failed. "
|
||||
"DirID: " + entryInfo->getEntryID());
|
||||
metaStore->releaseDir(entryInfo->getEntryID());
|
||||
return boost::make_unique<ResponseState>(retVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
metaStore->releaseDir(entryInfo->getEntryID());
|
||||
return boost::make_unique<ResponseState>(retVal);
|
||||
}
|
||||
|
||||
void SetDirPatternMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_SetDirPatternResp);
|
||||
}
|
||||
37
meta/source/net/message/storage/attribs/SetDirPatternMsgEx.h
Normal file
37
meta/source/net/message/storage/attribs/SetDirPatternMsgEx.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/attribs/SetDirPatternMsg.h>
|
||||
#include <common/net/message/storage/attribs/SetDirPatternRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
// set stripe pattern, called by fhgfs-ctl
|
||||
|
||||
class SetDirPatternMsgEx : public MirroredMessage<SetDirPatternMsg, FileIDLock>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<SetDirPatternRespMsg, NETMSGTYPE_SetDirPattern> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
FileIDLock lock(EntryLockStore& store) override
|
||||
{
|
||||
return {&store, getEntryInfo()->getEntryID(), true};
|
||||
}
|
||||
|
||||
bool isMirrored() override { return getEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<SetDirPatternRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "SetDirPatternMsgEx/forward"; }
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#include <session/EntryLock.h>
|
||||
#include "SetFilePatternMsgEx.h"
|
||||
|
||||
bool SetFilePatternMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
#ifdef BEEGFS_DEBUG
|
||||
const char* logContext = "SetFilePatternMsgEx incoming";
|
||||
|
||||
EntryInfo* entryInfo = this->getEntryInfo();
|
||||
LOG_DEBUG(logContext, 5, "EntryID: " + entryInfo->getEntryID() +
|
||||
"; FileName: " + entryInfo->getFileName() +
|
||||
"; EntryType: " + StringTk::intToStr(entryInfo->getEntryType()) +
|
||||
"; isBuddyMirrored: " + StringTk::intToStr(entryInfo->getIsBuddyMirrored()) +
|
||||
"; remoteStorageTarget (to be set) info: " + getRemoteStorageTarget()->toStr());
|
||||
#endif
|
||||
|
||||
BaseType::processIncoming(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
FileIDLock SetFilePatternMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
return {&store, getEntryInfo()->getEntryID(), true};
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> SetFilePatternMsgEx::executeLocally(
|
||||
ResponseContext& ctx, bool isSecondary)
|
||||
{
|
||||
const char* logContext = "Set File Pattern";
|
||||
FhgfsOpsErr res = FhgfsOpsErr_SUCCESS;
|
||||
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
EntryInfo* entryInfo = this->getEntryInfo();
|
||||
RemoteStorageTarget* rst = this->getRemoteStorageTarget();
|
||||
|
||||
auto [inode, referenceRes] = metaStore->referenceFile(entryInfo);
|
||||
if (unlikely(!inode))
|
||||
return boost::make_unique<ResponseState>(referenceRes);
|
||||
|
||||
// Ignore if the request did not contain RST configuration:
|
||||
if (!rst->hasInvalidVersion())
|
||||
{
|
||||
auto const& rstIDs = rst->getRstIdVector();
|
||||
if (rstIDs.empty())
|
||||
{
|
||||
// Empty RST ID list indicates a request to clear/unset RSTs for this file.
|
||||
res = inode->clearRemoteStorageTarget(entryInfo);
|
||||
if (res != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LogContext(logContext).logErr("Failed to clear RST info; entryID: " +
|
||||
entryInfo->getEntryID());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
res = inode->setRemoteStorageTarget(entryInfo, *rst);
|
||||
if (res != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LogContext(logContext).logErr("Setting RST info failed; entryID: " +
|
||||
entryInfo->getEntryID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
metaStore->releaseFile(entryInfo->getParentEntryID(), inode);
|
||||
return boost::make_unique<ResponseState>(res);
|
||||
}
|
||||
|
||||
void SetFilePatternMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_SetFilePatternResp);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <net/message/MirroredMessage.h>
|
||||
#include <common/net/message/storage/attribs/SetFilePatternMsg.h>
|
||||
#include <common/net/message/storage/attribs/SetFilePatternRespMsg.h>
|
||||
|
||||
class SetFilePatternMsgEx : public MirroredMessage<SetFilePatternMsg, FileIDLock>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<SetFilePatternRespMsg, NETMSGTYPE_SetFilePattern> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
FileIDLock lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<SetFilePatternRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "SetFilePatternMsgEx/forward"; }
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
#include <session/EntryLock.h>
|
||||
#include "SetFileStateMsgEx.h"
|
||||
|
||||
bool SetFileStateMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
#ifdef BEEGFS_DEBUG
|
||||
const char* logContext = "SetFileStateMsgEx incoming";
|
||||
|
||||
EntryInfo* entryInfo = this->getEntryInfo();
|
||||
LOG_DEBUG(logContext, 5, "EntryID: " + entryInfo->getEntryID() +
|
||||
"; FileName: " + entryInfo->getFileName() +
|
||||
"; EntryType: " + StringTk::intToStr(entryInfo->getEntryType()) +
|
||||
"; isBuddyMirrored: " + StringTk::intToStr(entryInfo->getIsBuddyMirrored()) +
|
||||
"; file state (accessFlags+dataState): " + StringTk::intToStr(this->getFileState()));
|
||||
#endif
|
||||
|
||||
BaseType::processIncoming(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
FileIDLock SetFileStateMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
return {&store, getEntryInfo()->getEntryID(), true};
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> SetFileStateMsgEx::executeLocally(
|
||||
ResponseContext& ctx, bool isSecondary)
|
||||
{
|
||||
const char* logContext = "Set File State";
|
||||
FhgfsOpsErr res = FhgfsOpsErr_INTERNAL;
|
||||
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
res = metaStore->setFileState(getEntryInfo(), FileState(getFileState()));
|
||||
|
||||
if (res != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LogContext(logContext).log(Log_DEBUG, "Setting file state failed. EntryID: " +
|
||||
getEntryInfo()->getEntryID());
|
||||
return boost::make_unique<ResponseState>(res);
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(res);
|
||||
}
|
||||
|
||||
void SetFileStateMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_SetFileStateResp);
|
||||
}
|
||||
30
meta/source/net/message/storage/attribs/SetFileStateMsgEx.h
Normal file
30
meta/source/net/message/storage/attribs/SetFileStateMsgEx.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <net/message/MirroredMessage.h>
|
||||
#include <common/net/message/storage/attribs/SetFileStateMsg.h>
|
||||
#include <common/net/message/storage/attribs/SetFileStateRespMsg.h>
|
||||
|
||||
class SetFileStateMsgEx : public MirroredMessage<SetFileStateMsg, FileIDLock>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<SetFileStateRespMsg, NETMSGTYPE_SetFileState> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
FileIDLock lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<SetFileStateRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "SetFileStateMsgEx/forward"; }
|
||||
};
|
||||
85
meta/source/net/message/storage/attribs/SetXAttrMsgEx.cpp
Normal file
85
meta/source/net/message/storage/attribs/SetXAttrMsgEx.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/attribs/SetXAttrRespMsg.h>
|
||||
#include <net/msghelpers/MsgHelperXAttr.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include "SetXAttrMsgEx.h"
|
||||
|
||||
std::tuple<FileIDLock, FileIDLock> SetXAttrMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
if (getEntryInfo()->getEntryType() == DirEntryType_DIRECTORY)
|
||||
return std::make_tuple(
|
||||
FileIDLock(),
|
||||
FileIDLock(&store, getEntryInfo()->getEntryID(), true));
|
||||
else
|
||||
return std::make_tuple(
|
||||
FileIDLock(&store, getEntryInfo()->getEntryID(), true),
|
||||
FileIDLock());
|
||||
}
|
||||
|
||||
bool SetXAttrMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
#ifdef BEEGFS_DEBUG
|
||||
const char* logContext = "SetXAttrMsg incoming";
|
||||
EntryInfo* entryInfo = this->getEntryInfo();
|
||||
const std::string& name = this->getName();
|
||||
|
||||
LOG_DEBUG(logContext, Log_DEBUG, "name: " + name + ";");
|
||||
|
||||
LOG_DEBUG("SetXAttrMsgEx::processIncoming", Log_DEBUG,
|
||||
"ParentID: " + entryInfo->getParentEntryID() + " EntryID: " +
|
||||
entryInfo->getEntryID() + " BuddyMirrored: " +
|
||||
(entryInfo->getIsBuddyMirrored() ? "Yes" : "No") + " Secondary: " +
|
||||
(hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond) ? "Yes" : "No"));
|
||||
#endif
|
||||
|
||||
BaseType::processIncoming(ctx);
|
||||
|
||||
updateNodeOp(ctx, MetaOpCounter_SETXATTR);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> SetXAttrMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
if (!Program::getApp()->getConfig()->getStoreClientXAttrs())
|
||||
{
|
||||
LOG(GENERAL, ERR,
|
||||
"Received a SetXAttrMsg, but client-side extended attributes are disabled in config.");
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_NOTSUPP);
|
||||
}
|
||||
|
||||
auto setXAttrRes = MsgHelperXAttr::setxattr(getEntryInfo(), getName(), getValue(), getFlags());
|
||||
|
||||
if (setXAttrRes == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
if (DirEntryType_ISDIR(getEntryInfo()->getEntryType()))
|
||||
{
|
||||
auto dir = metaStore->referenceDir(getEntryInfo()->getEntryID(),
|
||||
getEntryInfo()->getIsBuddyMirrored(), true);
|
||||
if (dir)
|
||||
{
|
||||
fixInodeTimestamp(*dir, inodeTimestamps);
|
||||
metaStore->releaseDir(dir->getID());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto [file, referenceRes] = metaStore->referenceFile(getEntryInfo());
|
||||
if (file)
|
||||
{
|
||||
fixInodeTimestamp(*file, inodeTimestamps, getEntryInfo());
|
||||
metaStore->releaseFile(getEntryInfo()->getParentEntryID(), file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(setXAttrRes);
|
||||
}
|
||||
|
||||
void SetXAttrMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_SetXAttrResp);
|
||||
}
|
||||
31
meta/source/net/message/storage/attribs/SetXAttrMsgEx.h
Normal file
31
meta/source/net/message/storage/attribs/SetXAttrMsgEx.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/attribs/SetXAttrMsg.h>
|
||||
#include <common/net/message/storage/attribs/SetXAttrRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
class SetXAttrMsgEx : public MirroredMessage<SetXAttrMsg, std::tuple<FileIDLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<SetXAttrRespMsg, NETMSGTYPE_SetXAttr> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<FileIDLock, FileIDLock> lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
private:
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<SetXAttrRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "SetXAttrMsgEx/forward"; }
|
||||
};
|
||||
|
||||
87
meta/source/net/message/storage/attribs/StatMsgEx.cpp
Normal file
87
meta/source/net/message/storage/attribs/StatMsgEx.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/attribs/StatRespMsg.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <net/msghelpers/MsgHelperStat.h>
|
||||
#include "StatMsgEx.h"
|
||||
|
||||
|
||||
bool StatMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
#ifdef BEEGFS_DEBUG
|
||||
const char* logContext = "StatMsgEx incoming";
|
||||
#endif // BEEGFS_DEBUG
|
||||
|
||||
LOG_DEBUG(logContext, 5, "ParentID: " + getEntryInfo()->getParentEntryID() +
|
||||
"; EntryID: " + getEntryInfo()->getEntryID() +
|
||||
"; EntryType: " + StringTk::intToStr(getEntryInfo()->getEntryType() ) +
|
||||
"; isBuddyMirrored: " + StringTk::intToStr(getEntryInfo()->getIsBuddyMirrored()));
|
||||
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
FileIDLock StatMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
return {&store, getEntryInfo()->getEntryID(), false};
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> StatMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
StatMsgResponseState resp;
|
||||
|
||||
StatData statData;
|
||||
FhgfsOpsErr statRes;
|
||||
|
||||
NumNodeID parentNodeID;
|
||||
std::string parentEntryID;
|
||||
|
||||
EntryInfo* entryInfo = this->getEntryInfo();
|
||||
|
||||
if (entryInfo->getParentEntryID().empty() || (entryInfo->getEntryID() == META_ROOTDIR_ID_STR) )
|
||||
{ // special case: stat for root directory
|
||||
statRes = statRoot(statData);
|
||||
}
|
||||
else
|
||||
{
|
||||
statRes = MsgHelperStat::stat(entryInfo, true, getMsgHeaderUserID(), statData, &parentNodeID,
|
||||
&parentEntryID);
|
||||
}
|
||||
|
||||
LOG_DBG(GENERAL, DEBUG, "", statRes);
|
||||
|
||||
resp.setStatResult(statRes);
|
||||
resp.setStatData(statData);
|
||||
|
||||
if (isMsgHeaderFeatureFlagSet(STATMSG_FLAG_GET_PARENTINFO) && parentNodeID)
|
||||
resp.setParentInfo(parentNodeID, parentEntryID);
|
||||
|
||||
updateNodeOp(ctx, MetaOpCounter_STAT);
|
||||
|
||||
return boost::make_unique<ResponseState>(std::move(resp));
|
||||
}
|
||||
|
||||
FhgfsOpsErr StatMsgEx::statRoot(StatData& outStatData)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
Node& localNode = app->getLocalNode();
|
||||
DirInode* rootDir = app->getRootDir();
|
||||
NumNodeID expectedOwnerID;
|
||||
|
||||
// if root is buddy mirrored compare ownership to buddy group id, otherwise to node id itself
|
||||
if ( rootDir->getIsBuddyMirrored() )
|
||||
expectedOwnerID =
|
||||
NumNodeID(app->getMetaBuddyGroupMapper()->getBuddyGroupID(localNode.getNumID().val() ) );
|
||||
else
|
||||
expectedOwnerID = localNode.getNumID();
|
||||
|
||||
if(expectedOwnerID != rootDir->getOwnerNodeID() )
|
||||
{
|
||||
return FhgfsOpsErr_NOTOWNER;
|
||||
}
|
||||
|
||||
rootDir->getStatData(outStatData);
|
||||
|
||||
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
124
meta/source/net/message/storage/attribs/StatMsgEx.h
Normal file
124
meta/source/net/message/storage/attribs/StatMsgEx.h
Normal file
@@ -0,0 +1,124 @@
|
||||
#pragma once
|
||||
|
||||
#include <storage/DirInode.h>
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/attribs/StatMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
// The "getattr" operation for linux-kernel filesystems
|
||||
|
||||
class StatMsgResponseState : public MirroredMessageResponseState
|
||||
{
|
||||
public:
|
||||
StatMsgResponseState() : result(FhgfsOpsErr_INTERNAL), hasParentInfo(false) {}
|
||||
|
||||
explicit StatMsgResponseState(Deserializer& des)
|
||||
{
|
||||
serialize(this, des);
|
||||
}
|
||||
|
||||
StatMsgResponseState(StatMsgResponseState&& other) :
|
||||
result(other.result),
|
||||
statData(other.statData),
|
||||
parentNodeID(other.parentNodeID),
|
||||
parentEntryID(other.parentEntryID),
|
||||
hasParentInfo(other.hasParentInfo)
|
||||
{}
|
||||
|
||||
void sendResponse(NetMessage::ResponseContext& ctx) override
|
||||
{
|
||||
StatRespMsg resp(result, statData);
|
||||
|
||||
if (this->hasParentInfo)
|
||||
{
|
||||
resp.addParentInfo(parentNodeID, parentEntryID);
|
||||
}
|
||||
|
||||
ctx.sendResponse(resp);
|
||||
}
|
||||
|
||||
// StatMsgEx is converted to mirrored message to leverage locking part of
|
||||
// mirrored messages to prevent races with unlink, open etc.
|
||||
//
|
||||
// Always return false from changeObservableState() to prohibit forwarding
|
||||
// to secondary. See MirroredMessage::finishOperation() for more details.
|
||||
bool changesObservableState() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void setParentInfo(NumNodeID nodeID, const std::string& parentEntryID)
|
||||
{
|
||||
this->hasParentInfo = true;
|
||||
this->parentNodeID = nodeID;
|
||||
this->parentEntryID = parentEntryID;
|
||||
}
|
||||
|
||||
void setStatData(const StatData& statData)
|
||||
{
|
||||
this->statData = statData;
|
||||
}
|
||||
|
||||
void setStatResult(FhgfsOpsErr statRes) { this->result = statRes; }
|
||||
|
||||
protected:
|
||||
uint32_t serializerTag() const override { return NETMSGTYPE_Stat; }
|
||||
|
||||
template<typename This, typename Ctx>
|
||||
static void serialize(This obj, Ctx& ctx)
|
||||
{
|
||||
ctx
|
||||
% obj->result
|
||||
% obj->statData.serializeAs(StatDataFormat_NET);
|
||||
|
||||
if (obj->hasParentInfo)
|
||||
{
|
||||
ctx
|
||||
% serdes::stringAlign4(obj->parentEntryID)
|
||||
% obj->parentNodeID;
|
||||
}
|
||||
}
|
||||
|
||||
void serializeContents(Serializer& ser) const override
|
||||
{
|
||||
serialize(this, ser);
|
||||
}
|
||||
|
||||
private:
|
||||
FhgfsOpsErr result;
|
||||
StatData statData;
|
||||
NumNodeID parentNodeID;
|
||||
std::string parentEntryID;
|
||||
bool hasParentInfo;
|
||||
};
|
||||
|
||||
class StatMsgEx: public MirroredMessage<StatMsg, FileIDLock>
|
||||
{
|
||||
public:
|
||||
typedef StatMsgResponseState ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
FileIDLock lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override
|
||||
{
|
||||
return getEntryInfo()->getIsBuddyMirrored();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override {}
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "StatMsgEx/forward"; }
|
||||
|
||||
FhgfsOpsErr statRoot(StatData& outStatData);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/attribs/UpdateDirParentRespMsg.h>
|
||||
#include <common/net/message/storage/attribs/SetLocalAttrMsg.h>
|
||||
#include <common/net/message/storage/attribs/SetLocalAttrRespMsg.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <components/worker/SetChunkFileAttribsWork.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include "UpdateDirParentMsgEx.h"
|
||||
|
||||
|
||||
bool UpdateDirParentMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
EntryInfo* entryInfo = getEntryInfo();
|
||||
NumNodeID parentNodeID = getParentNodeID();
|
||||
|
||||
LOG_DEBUG("UpdateDirParentMsgEx::processIncoming", Log_DEBUG,
|
||||
"ParentID: " + entryInfo->getParentEntryID() + " EntryID: " +
|
||||
entryInfo->getEntryID() + " parentNodeID: " + parentNodeID.str() + " BuddyMirrored: " +
|
||||
(entryInfo->getIsBuddyMirrored() ? "Yes" : "No") + " Secondary: " +
|
||||
(hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond) ? "Yes" : "No"));
|
||||
(void) entryInfo;
|
||||
(void) parentNodeID;
|
||||
|
||||
rctx = &ctx;
|
||||
|
||||
BaseType::processIncoming(ctx);
|
||||
|
||||
// update operation counters
|
||||
updateNodeOp(ctx, MetaOpCounter_UPDATEDIRPARENT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FileIDLock UpdateDirParentMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
if (rctx->isLocallyGenerated())
|
||||
return {};
|
||||
|
||||
return {&store, getEntryInfo()->getEntryID(), true};
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> UpdateDirParentMsgEx::executeLocally(
|
||||
ResponseContext& ctx, bool isSecondary)
|
||||
{
|
||||
auto setRes = Program::getApp()->getMetaStore()->setDirParent(getEntryInfo(), getParentNodeID());
|
||||
|
||||
if (setRes == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
{
|
||||
auto dir = Program::getApp()->getMetaStore()->referenceDir(getEntryInfo()->getEntryID(),
|
||||
true, true);
|
||||
if (dir)
|
||||
{
|
||||
fixInodeTimestamp(*dir, dirTimestamps);
|
||||
Program::getApp()->getMetaStore()->releaseDir(dir->getID());
|
||||
}
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(setRes);
|
||||
}
|
||||
|
||||
void UpdateDirParentMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_UpdateDirParentResp);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <storage/MetaStore.h>
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/attribs/UpdateDirParentMsg.h>
|
||||
#include <common/net/message/storage/attribs/UpdateDirParentRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
class UpdateDirParentMsgEx : public MirroredMessage<UpdateDirParentMsg, FileIDLock>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<UpdateDirParentRespMsg, NETMSGTYPE_UpdateDirParent>
|
||||
ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
FileIDLock lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
ResponseContext* rctx;
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<UpdateDirParentRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "UpdateDirParentMsgEx/forward"; }
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
#include <toolkit/StorageTkEx.h>
|
||||
#include <program/Program.h>
|
||||
|
||||
#include "ChunkBalanceMsgEx.h"
|
||||
|
||||
|
||||
FileIDLock ChunkBalanceMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
return {&store, getEntryInfo()->getEntryID(), true};
|
||||
}
|
||||
|
||||
bool ChunkBalanceMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
#ifdef BEEGFS_DEBUG
|
||||
const char* logContext = "ChunkBalanceMsg incoming";
|
||||
#endif // BEEGFS_DEBUG
|
||||
|
||||
LOG_DEBUG(logContext, Log_SPAM, "Starting ChunkBalance job from localTargetID: "
|
||||
+ StringTk::uintToStr(getTargetID()) + "; to destinationTargetID: "
|
||||
+ StringTk::uintToStr(getDestinationID()));
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> ChunkBalanceMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
ChunkBalanceMsgResponseState resp;
|
||||
|
||||
FhgfsOpsErr chunkBalanceMsgRes = FhgfsOpsErr_SUCCESS;
|
||||
|
||||
const char* logContext = "Update Stripe Pattern";
|
||||
|
||||
LogContext(logContext).logErr("This message is not yet implemented. \n It should trigger chunk balancing components on the metadata service. ");
|
||||
|
||||
resp.setResult(chunkBalanceMsgRes);
|
||||
return boost::make_unique<ResponseState>(std::move(resp));
|
||||
}
|
||||
|
||||
ChunkBalancerJob* ChunkBalanceMsgEx::addChunkBalanceJob()
|
||||
{
|
||||
std::lock_guard<Mutex> mutexLock(ChunkBalanceJobMutex);
|
||||
|
||||
ChunkBalancerJob* chunkBalanceJob = nullptr;
|
||||
return chunkBalanceJob;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/chunkbalancing/ChunkBalanceMsg.h>
|
||||
#include <common/net/message/storage/chunkbalancing/ChunkBalanceRespMsg.h>
|
||||
|
||||
#include <net/message/MirroredMessage.h>
|
||||
#include <session/EntryLock.h>
|
||||
|
||||
class ChunkBalancerJob;
|
||||
|
||||
class ChunkBalanceMsgResponseState : public ErrorCodeResponseState<ChunkBalanceRespMsg, NETMSGTYPE_ChunkBalance>
|
||||
{
|
||||
public:
|
||||
|
||||
ChunkBalanceMsgResponseState() : ErrorCodeResponseState<ChunkBalanceRespMsg, NETMSGTYPE_ChunkBalance>(FhgfsOpsErr_INTERNAL)
|
||||
{
|
||||
}
|
||||
|
||||
ChunkBalanceMsgResponseState(ChunkBalanceMsgResponseState&& other) :
|
||||
ErrorCodeResponseState<ChunkBalanceRespMsg, NETMSGTYPE_ChunkBalance>(other.result)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
// Always return false from changeObservableState() to prohibit forwarding
|
||||
// to secondary. See MirroredMessage::finishOperation() for more details.
|
||||
*/
|
||||
|
||||
bool changesObservableState() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void setResult(FhgfsOpsErr result) { this->result = result; }
|
||||
|
||||
private:
|
||||
FhgfsOpsErr result;
|
||||
};
|
||||
|
||||
class ChunkBalanceMsgEx : public MirroredMessage<ChunkBalanceMsg, FileIDLock>
|
||||
{
|
||||
public:
|
||||
typedef ChunkBalanceMsgResponseState ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
FileIDLock lock(EntryLockStore& store) override;
|
||||
bool isMirrored() override { return getEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
Mutex ChunkBalanceJobMutex;
|
||||
ChunkBalancerJob* addChunkBalanceJob();
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override {};
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
const char* mirrorLogContext() const override { return "ChunkBalanceMsgEx/forward"; }
|
||||
|
||||
};
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#include <toolkit/StorageTkEx.h>
|
||||
#include <program/Program.h>
|
||||
|
||||
#include "StripePatternUpdateMsgEx.h"
|
||||
|
||||
FileIDLock StripePatternUpdateMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
return {&store, getEntryInfo()->getEntryID(), true};
|
||||
}
|
||||
|
||||
bool StripePatternUpdateMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
#ifdef BEEGFS_DEBUG
|
||||
const char* logContext = "StripePatternUpdateMsg incoming";
|
||||
#endif // BEEGFS_DEBUG
|
||||
|
||||
LOG_DEBUG(logContext, Log_SPAM, "Attempting to change stripe pattern of file at chunkPath: " + getRelativePath() + "; from localTargetID: "
|
||||
+ StringTk::uintToStr(getTargetID()) + "; to destinationTargetID: "
|
||||
+ StringTk::uintToStr(getDestinationID()));
|
||||
return BaseType::processIncoming(ctx);
|
||||
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> StripePatternUpdateMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
|
||||
FhgfsOpsErr stripePatternMsgRes;
|
||||
const char* logContext = "Update Stripe Pattern";
|
||||
|
||||
LogContext(logContext).logErr("This message is not yet implemented. \n It should change the metadata stripe pattern of the chunk that is being balanced. ");
|
||||
|
||||
stripePatternMsgRes = FhgfsOpsErr_SUCCESS;
|
||||
return boost::make_unique<ResponseState>(stripePatternMsgRes);
|
||||
}
|
||||
|
||||
ChunkBalancerJob* StripePatternUpdateMsgEx::addChunkBalanceJob()
|
||||
{
|
||||
std::lock_guard<Mutex> mutexLock(ChunkBalanceJobMutex);
|
||||
|
||||
ChunkBalancerJob* chunkBalanceJob = nullptr;
|
||||
return chunkBalanceJob;
|
||||
}
|
||||
|
||||
void StripePatternUpdateMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_StripePatternUpdate);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/chunkbalancing/StripePatternUpdateMsg.h>
|
||||
#include <common/net/message/storage/chunkbalancing/StripePatternUpdateRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
#include <session/EntryLock.h>
|
||||
|
||||
|
||||
class ChunkBalancerJob;
|
||||
|
||||
class StripePatternUpdateMsgEx : public MirroredMessage<StripePatternUpdateMsg, FileIDLock>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<StripePatternUpdateRespMsg, NETMSGTYPE_StripePatternUpdate> ResponseState;
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
FileIDLock lock(EntryLockStore& store) override;
|
||||
bool isMirrored() override { return getEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
Mutex ChunkBalanceJobMutex;
|
||||
Mutex StripePatternUpdateMutex;
|
||||
Condition StripePatternUpdateCond;
|
||||
|
||||
ChunkBalancerJob* addChunkBalanceJob();
|
||||
void waitForStripePatternUpdate();
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return static_cast<FhgfsOpsErr>(static_cast<StripePatternUpdateRespMsg&>(resp).getValue());
|
||||
}
|
||||
const char* mirrorLogContext() const override { return "StripePatternUpdateMsgEx/forward"; }
|
||||
};
|
||||
|
||||
303
meta/source/net/message/storage/creating/HardlinkMsgEx.cpp
Normal file
303
meta/source/net/message/storage/creating/HardlinkMsgEx.cpp
Normal file
@@ -0,0 +1,303 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/creating/HardlinkRespMsg.h>
|
||||
#include <common/net/message/storage/creating/MoveFileInodeMsg.h>
|
||||
#include <common/net/message/storage/creating/MoveFileInodeRespMsg.h>
|
||||
#include <common/net/message/storage/attribs/SetAttrMsg.h>
|
||||
#include <common/net/message/storage/attribs/SetAttrRespMsg.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <components/FileEventLogger.h>
|
||||
#include "HardlinkMsgEx.h"
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock, ParentNameLock, FileIDLock> HardlinkMsgEx::lock(
|
||||
EntryLockStore& store)
|
||||
{
|
||||
// NOTE: normally we'd need to also lock on the MDS holding the destination file,
|
||||
// but we don't support hardlinks to different servers, yet
|
||||
ParentNameLock fromLock;
|
||||
ParentNameLock toLock;
|
||||
|
||||
FileIDLock fileLock;
|
||||
|
||||
FileIDLock dirLock(&store, getToDirInfo()->getEntryID(), true);
|
||||
|
||||
// take care about lock ordering! see MirroredMessage::lock()
|
||||
if (getFromDirInfo()->getEntryID() < getToDirInfo()->getEntryID()
|
||||
|| (getFromDirInfo()->getEntryID() == getToDirInfo()->getEntryID()
|
||||
&& getFromName() < getToName()))
|
||||
{
|
||||
fromLock = {&store, getFromDirInfo()->getEntryID(), getFromName()};
|
||||
toLock = {&store, getToDirInfo()->getEntryID(), getToName()};
|
||||
}
|
||||
else if (getFromDirInfo()->getEntryID() == getToDirInfo()->getEntryID()
|
||||
&& getFromName() == getToName())
|
||||
{
|
||||
fromLock = {&store, getFromDirInfo()->getEntryID(), getFromName()};
|
||||
}
|
||||
else
|
||||
{
|
||||
toLock = {&store, getToDirInfo()->getEntryID(), getToName()};
|
||||
fromLock = {&store, getFromDirInfo()->getEntryID(), getFromName()};
|
||||
}
|
||||
|
||||
// We need to lock the inode because hardlinking modifies the link count.
|
||||
// If the file inode is present on the current meta node or buddy-group,
|
||||
// then acquire the inodeLock.
|
||||
App* app = Program::getApp();
|
||||
NumNodeID ownerNodeID = getFromInfo()->getOwnerNodeID();
|
||||
bool isLocalInode = ((!isMirrored() && ownerNodeID == app->getLocalNode().getNumID()) ||
|
||||
(isMirrored() && ownerNodeID.val() == app->getMetaBuddyGroupMapper()->getLocalGroupID()));
|
||||
|
||||
if (isLocalInode)
|
||||
fileLock = {&store, getFromInfo()->getEntryID(), true};
|
||||
|
||||
return std::make_tuple(
|
||||
std::move(dirLock),
|
||||
std::move(fromLock),
|
||||
std::move(toLock),
|
||||
std::move(fileLock));
|
||||
}
|
||||
|
||||
bool HardlinkMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
LOG_DBG(GENERAL, DEBUG, "",
|
||||
("fromDirID", getFromDirInfo()->getEntryID()),
|
||||
("fromID", getFromInfo()->getEntryID()),
|
||||
("fromName", getFromName()),
|
||||
("toDirID", getToDirInfo()->getEntryID()),
|
||||
("toName", getToName()),
|
||||
("buddyMirrored", getToDirInfo()->getIsBuddyMirrored()));
|
||||
|
||||
BaseType::processIncoming(ctx);
|
||||
|
||||
updateNodeOp(ctx, MetaOpCounter_HARDLINK);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> HardlinkMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
const char* logContext = "create hard link";
|
||||
|
||||
App* app = Program::getApp();
|
||||
MetaStore* metaStore = app->getMetaStore();
|
||||
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
|
||||
unsigned updatedLinkCount = 0;
|
||||
|
||||
// reference directory where hardlink needs to be created
|
||||
DirInode* toDir = metaStore->referenceDir(getToDirInfo()->getEntryID(),
|
||||
getToDirInfo()->getIsBuddyMirrored(), true);
|
||||
|
||||
// check if file dentry already exists with same name as hardlink
|
||||
// return error if link already exists
|
||||
if (likely(toDir))
|
||||
{
|
||||
DirEntry newLink(getToName());
|
||||
if (toDir->getFileDentry(getToName(), newLink))
|
||||
{
|
||||
metaStore->releaseDir(toDir->getID());
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_EXISTS);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogContext(logContext).logErr(std::string("target directory for hard-link doesn't exist,"
|
||||
"dirname: " + getToDirInfo()->getFileName()));
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
|
||||
}
|
||||
|
||||
// Prevent hardlinks across directories with different buddy mirror configurations
|
||||
if (getFromDirInfo()->getIsBuddyMirrored() != getToDirInfo()->getIsBuddyMirrored())
|
||||
{
|
||||
LogContext(logContext).logErr("Hardlinks not supported across directories with different "
|
||||
"buddy mirror config setting. SourceDirID: " + getFromDirInfo()->getEntryID() +
|
||||
"; TargetDirID: " + getToDirInfo()->getEntryID());
|
||||
metaStore->releaseDir(toDir->getID());
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_NOTSUPP);
|
||||
}
|
||||
|
||||
// check whether local node/group owns source file's inode or not
|
||||
NumNodeID ownerNodeID = getFromInfo()->getOwnerNodeID();
|
||||
bool isLocalOwner = ((!isMirrored() && ownerNodeID == app->getLocalNode().getNumID()) ||
|
||||
(isMirrored() && ownerNodeID.val() == app->getMetaBuddyGroupMapper()->getLocalGroupID()));
|
||||
|
||||
if (isLocalOwner)
|
||||
{
|
||||
std::tie(retVal, updatedLinkCount) = metaStore->makeNewHardlink(getFromInfo());
|
||||
}
|
||||
else if (!isSecondary)
|
||||
{
|
||||
MoveFileInodeMsg deinlineMsg(getFromInfo(), MODE_DEINLINE, true);
|
||||
|
||||
RequestResponseArgs rrArgs(NULL, &deinlineMsg, NETMSGTYPE_MoveFileInodeResp);
|
||||
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes());
|
||||
rrNode.setTargetStates(app->getMetaStateStore());
|
||||
|
||||
if (getFromInfo()->getIsBuddyMirrored())
|
||||
{
|
||||
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
// send request to other MDs and receive response
|
||||
FhgfsOpsErr resp = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
|
||||
if (unlikely(resp != FhgfsOpsErr_SUCCESS))
|
||||
{
|
||||
// error
|
||||
LogContext(logContext).logErr("Communication with metadata server failed. "
|
||||
"nodeID: " + ownerNodeID.str());
|
||||
|
||||
retVal = resp;
|
||||
break;
|
||||
}
|
||||
|
||||
// response received
|
||||
const auto moveFileInodeRespMsg = (MoveFileInodeRespMsg*) rrArgs.outRespMsg.get();
|
||||
FhgfsOpsErr res = moveFileInodeRespMsg->getResult();
|
||||
if (res != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
// error: either source file not exists or deinline operation failed
|
||||
LogContext(logContext).logErr("verify and move file inode failed! nodeID: "
|
||||
+ ownerNodeID.str() + "; entryID: " + getFromInfo()->getEntryID());
|
||||
|
||||
retVal = res;
|
||||
break;
|
||||
}
|
||||
|
||||
// success
|
||||
retVal = res;
|
||||
updatedLinkCount = moveFileInodeRespMsg->getHardlinkCount();
|
||||
} while (false);
|
||||
}
|
||||
|
||||
// If verify/deinline inode and link count update is successful - only then create new
|
||||
// dentry in "toDir" with entryID and ownerNodeID of "fromFile" which might reside
|
||||
// on another meta node/buddy-group
|
||||
if (retVal == FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
DirEntry newHardlink(DirEntryType_REGULARFILE, getToName(),
|
||||
getFromInfo()->getEntryID(), getFromInfo()->getOwnerNodeID());
|
||||
|
||||
newHardlink.removeDentryFeatureFlag(DENTRY_FEATURE_INODE_INLINE);
|
||||
|
||||
// buddy mirroring is inherited from parent directory
|
||||
if (toDir->getIsBuddyMirrored())
|
||||
newHardlink.setBuddyMirrorFeatureFlag();
|
||||
|
||||
FhgfsOpsErr makeRes = toDir->makeDirEntry(newHardlink);
|
||||
|
||||
if (makeRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
// compensate link count if dentry create fails
|
||||
//
|
||||
// for a remote inode, Use SetAttrMsg to decrease link count
|
||||
// since its a mirrored message so it will update link count on both primary and
|
||||
// secondary hence don't send this message from secondary buddy
|
||||
if (!isSecondary && !isLocalOwner)
|
||||
incDecRemoteLinkCount(ownerNodeID, false);
|
||||
else
|
||||
metaStore->incDecLinkCount(getFromInfo(), -1);
|
||||
|
||||
retVal = makeRes;
|
||||
}
|
||||
}
|
||||
|
||||
if (retVal == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
{
|
||||
fixInodeTimestamp(*toDir, dirTimestamps);
|
||||
|
||||
if (isLocalOwner)
|
||||
{
|
||||
auto [file, referenceRes] = metaStore->referenceFile(getFromInfo());
|
||||
if (file)
|
||||
{
|
||||
fixInodeTimestamp(*file, fileTimestamps, getFromInfo());
|
||||
metaStore->releaseFile(getFromInfo()->getParentEntryID(), file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
metaStore->releaseDir(toDir->getID());
|
||||
|
||||
if (!isSecondary && retVal == FhgfsOpsErr_SUCCESS && app->getFileEventLogger() && getFileEvent())
|
||||
{
|
||||
EventContext eventCtx = makeEventContext(getFromInfo(), getFromInfo()->getParentEntryID(),
|
||||
getMsgHeaderUserID(), "", updatedLinkCount, isSecondary);
|
||||
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(retVal);
|
||||
}
|
||||
|
||||
FhgfsOpsErr HardlinkMsgEx::incDecRemoteLinkCount(NumNodeID const& ownerNodeID, bool increment)
|
||||
{
|
||||
const char* logContext = "incDecRemoteLinkCount";
|
||||
App* app = Program::getApp();
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_INTERNAL;
|
||||
|
||||
SettableFileAttribs dummyAttrib = {};
|
||||
SetAttrMsg msg(getFromInfo(), 0, &dummyAttrib);
|
||||
|
||||
if (increment)
|
||||
{
|
||||
msg.addMsgHeaderFeatureFlag(SETATTRMSG_FLAG_INCR_NLINKCNT);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.addMsgHeaderFeatureFlag(SETATTRMSG_FLAG_DECR_NLINKCNT);
|
||||
}
|
||||
|
||||
RequestResponseArgs rrArgs(NULL, &msg, NETMSGTYPE_SetAttrResp);
|
||||
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes());
|
||||
|
||||
rrNode.setTargetStates(app->getMetaStateStore());
|
||||
|
||||
if (getFromInfo()->getIsBuddyMirrored())
|
||||
{
|
||||
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
// send request to other MDs and receive response
|
||||
FhgfsOpsErr resp = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
|
||||
if (unlikely(resp != FhgfsOpsErr_SUCCESS))
|
||||
{
|
||||
// error
|
||||
LogContext(logContext).log(Log_WARNING,
|
||||
"Communication with metadata server failed. "
|
||||
"nodeID: " + ownerNodeID.str());
|
||||
retVal = resp;
|
||||
break;
|
||||
}
|
||||
|
||||
// response received
|
||||
const auto incLinkCountResp = (SetAttrRespMsg*) rrArgs.outRespMsg.get();
|
||||
FhgfsOpsErr res = static_cast<FhgfsOpsErr>(incLinkCountResp->getValue());
|
||||
if (res != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
// error: either source file not exists or nlink count increment failed
|
||||
std::string operationType = (increment ? "increase": "decrease");
|
||||
LogContext(logContext).logErr("nLink count " + operationType
|
||||
+ " failed! nodeID: " + ownerNodeID.str() + "; "
|
||||
+ "entryID: " + getFromInfo()->getEntryID());
|
||||
|
||||
retVal = res;
|
||||
break;
|
||||
}
|
||||
|
||||
// success
|
||||
retVal = res;
|
||||
} while (false);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void HardlinkMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_HardlinkResp);
|
||||
}
|
||||
38
meta/source/net/message/storage/creating/HardlinkMsgEx.h
Normal file
38
meta/source/net/message/storage/creating/HardlinkMsgEx.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/creating/HardlinkMsg.h>
|
||||
#include <common/net/message/storage/creating/HardlinkRespMsg.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <storage/DirEntry.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
class HardlinkMsgEx : public MirroredMessage<HardlinkMsg,
|
||||
std::tuple<FileIDLock, ParentNameLock, ParentNameLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<HardlinkRespMsg, NETMSGTYPE_Hardlink> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock, ParentNameLock, FileIDLock>
|
||||
lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getFromInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<HardlinkRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
FhgfsOpsErr incDecRemoteLinkCount(NumNodeID const& ownerNodeID, bool increment);
|
||||
const char* mirrorLogContext() const override { return "HardlinkMsgEx/forward"; }
|
||||
};
|
||||
|
||||
|
||||
411
meta/source/net/message/storage/creating/MkDirMsgEx.cpp
Normal file
411
meta/source/net/message/storage/creating/MkDirMsgEx.cpp
Normal file
@@ -0,0 +1,411 @@
|
||||
#include <common/components/streamlistenerv2/IncomingPreprocessedMsgWork.h>
|
||||
#include <common/net/message/control/GenericResponseMsg.h>
|
||||
#include <common/net/message/storage/creating/MkLocalDirMsg.h>
|
||||
#include <common/net/message/storage/creating/MkLocalDirRespMsg.h>
|
||||
#include <common/net/message/storage/creating/MkDirRespMsg.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <components/FileEventLogger.h>
|
||||
#include <components/ModificationEventFlusher.h>
|
||||
#include <program/Program.h>
|
||||
#include <storage/PosixACL.h>
|
||||
#include "RmDirMsgEx.h"
|
||||
#include "MkDirMsgEx.h"
|
||||
|
||||
|
||||
bool MkDirMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
|
||||
entryID = StorageTk::generateFileID(app->getLocalNode().getNumID());
|
||||
|
||||
BaseType::processIncoming(ctx);
|
||||
|
||||
// update operation counters
|
||||
updateNodeOp(ctx, MetaOpCounter_MKDIR);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> MkDirMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
auto result = isSecondary
|
||||
? mkDirSecondary()
|
||||
: mkDirPrimary(ctx);
|
||||
|
||||
if (result && result->getResult() != FhgfsOpsErr_SUCCESS)
|
||||
LOG_DBG(GENERAL, DEBUG, "Failed to create directory",
|
||||
("parentID", getParentInfo()->getEntryID()),
|
||||
("newDirName", getNewDirName()),
|
||||
("error", result->getResult()));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock, ParentNameLock> MkDirMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
HashDirLock hashLock;
|
||||
|
||||
// during resync we must lock the hash dir of the new inode even if the inode will be created on
|
||||
// a different node because we select the target node for the inode only after we have locked our
|
||||
// structures.
|
||||
if (resyncJob && resyncJob->isRunning())
|
||||
hashLock = {&store, MetaStorageTk::getMetaInodeHash(entryID)};
|
||||
|
||||
FileIDLock dirLock(&store, getParentInfo()->getEntryID(), true);
|
||||
ParentNameLock dentryLock(&store, getParentInfo()->getEntryID(), getNewDirName());
|
||||
|
||||
return std::make_tuple(std::move(hashLock), std::move(dirLock), std::move(dentryLock));
|
||||
}
|
||||
|
||||
std::unique_ptr<MkDirMsgEx::ResponseState> MkDirMsgEx::mkDirPrimary(ResponseContext& ctx)
|
||||
{
|
||||
const char* logContext = "MkDirMsg (mkDirPrimary)";
|
||||
|
||||
App* app = Program::getApp();
|
||||
ModificationEventFlusher* modEventFlusher = app->getModificationEventFlusher();
|
||||
const bool modEventLoggingEnabled = modEventFlusher->isLoggingEnabled();
|
||||
MetaStore* metaStore = app->getMetaStore();
|
||||
Config* config = app->getConfig();
|
||||
MirrorBuddyGroupMapper* metaBuddyGroupMapper = app->getMetaBuddyGroupMapper();
|
||||
NodeCapacityPools* metaCapacityPools;
|
||||
NumNodeID expectedOwnerID;
|
||||
|
||||
const EntryInfo* const parentInfo = getParentInfo();
|
||||
const std::string& newName = getNewDirName();
|
||||
|
||||
FhgfsOpsErr retVal;
|
||||
|
||||
// not a good idea to use scoped locks here, because we don't have a well-defined scope with
|
||||
// only buddy mirrored paths here; directly use entrylockstore
|
||||
EntryLockStore* entryLockStore = Program::getApp()->getSessions()->getEntryLockStore();
|
||||
|
||||
// reference parent
|
||||
DirInode* parentDir = metaStore->referenceDir(parentInfo->getEntryID(),
|
||||
parentInfo->getIsBuddyMirrored(), true);
|
||||
if(!parentDir)
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS, EntryInfo());
|
||||
|
||||
// check whether localNode owns this (parent) directory
|
||||
NumNodeID localNodeID = app->getLocalNodeNumID();
|
||||
bool isBuddyMirrored = parentDir->getIsBuddyMirrored()
|
||||
&& !isMsgHeaderFeatureFlagSet(MKDIRMSG_FLAG_NOMIRROR);
|
||||
|
||||
// check whether localNode owns this (parent) directory; if parentDir is buddy mirrored compare
|
||||
// ownership to buddy group id, otherwise to node id itself
|
||||
if (parentDir->getIsBuddyMirrored())
|
||||
expectedOwnerID = NumNodeID(metaBuddyGroupMapper->getLocalGroupID() );
|
||||
else
|
||||
expectedOwnerID = localNodeID;
|
||||
|
||||
if (isBuddyMirrored)
|
||||
metaCapacityPools = app->getMetaBuddyCapacityPools();
|
||||
else
|
||||
metaCapacityPools = app->getMetaCapacityPools();
|
||||
|
||||
if(parentDir->getOwnerNodeID() != expectedOwnerID)
|
||||
{ // this node doesn't own the parent dir
|
||||
LogContext(logContext).logErr(std::string("Dir-owner mismatch: \"") +
|
||||
parentDir->getOwnerNodeID().str() + "\" vs. \"" +
|
||||
expectedOwnerID.str() + "\"");
|
||||
metaStore->releaseDir(parentInfo->getEntryID() );
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_NOTOWNER, EntryInfo());
|
||||
}
|
||||
|
||||
// choose new directory owner...
|
||||
unsigned numDesiredTargets = 1;
|
||||
unsigned minNumRequiredTargets = numDesiredTargets;
|
||||
UInt16Vector newOwnerNodes;
|
||||
|
||||
metaCapacityPools->chooseStorageTargets(numDesiredTargets, minNumRequiredTargets,
|
||||
&getPreferredNodes(), &newOwnerNodes);
|
||||
|
||||
if(unlikely(newOwnerNodes.size() < minNumRequiredTargets) )
|
||||
{ // (might be caused by a bad list of preferred targets)
|
||||
LogContext(logContext).logErr("No metadata servers available for new directory: " + newName);
|
||||
|
||||
metaStore->releaseDir(parentInfo->getEntryID() );
|
||||
// we know that *some* metadata server must exist, since we are obviously active when we get
|
||||
// here. most likely a client has received a pool update before we have, or we have been
|
||||
// switched from secondary to primary and haven't been set to Good yet.
|
||||
// if preferred nodes have been set (currently only done by ctl), those may also be registered
|
||||
// as unavailable at the current time.
|
||||
// have the client retry until things work out.
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_COMMUNICATION, EntryInfo());
|
||||
}
|
||||
|
||||
const uint16_t ownerNodeID = newOwnerNodes[0];
|
||||
const std::string parentEntryID = parentInfo->getEntryID();
|
||||
int entryInfoFlags = isBuddyMirrored ? ENTRYINFO_FEATURE_BUDDYMIRRORED : 0;
|
||||
|
||||
int mode = getMode();
|
||||
const int umask = getUmask();
|
||||
CharVector parentDefaultACLXAttr;
|
||||
CharVector accessACLXAttr;
|
||||
|
||||
if (config->getStoreClientACLs())
|
||||
{
|
||||
// Determine the ACLs of the new directory.
|
||||
PosixACL parentDefaultACL;
|
||||
bool needsACL;
|
||||
FhgfsOpsErr parentDefaultACLRes;
|
||||
|
||||
std::tie(parentDefaultACLRes, parentDefaultACLXAttr, std::ignore) = parentDir->getXAttr(
|
||||
nullptr, PosixACL::defaultACLXAttrName, XATTR_SIZE_MAX);
|
||||
|
||||
if (parentDefaultACLRes == FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
// parent has a default ACL
|
||||
if (!parentDefaultACL.deserializeXAttr(parentDefaultACLXAttr))
|
||||
{
|
||||
LogContext(logContext).log(Log_ERR,
|
||||
"Error deserializing directory default ACL for directory ID " + parentDir->getID());
|
||||
retVal = FhgfsOpsErr_INTERNAL;
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
if (!parentDefaultACL.empty())
|
||||
{
|
||||
// Note: This modifies the mode bits as well as the ACL itself.
|
||||
FhgfsOpsErr modeRes = parentDefaultACL.modifyModeBits(mode, needsACL);
|
||||
setMode(mode, 0);
|
||||
|
||||
if (modeRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LogContext(logContext).log(Log_ERR, "Error generating access ACL for new directory "
|
||||
+ newName);
|
||||
retVal = FhgfsOpsErr_INTERNAL;
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
if (needsACL)
|
||||
parentDefaultACL.serializeXAttr(accessACLXAttr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// On empty ACL, clear the Xattr, so it doesn't get set on the newly created dir
|
||||
parentDefaultACLXAttr.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (parentDefaultACLRes == FhgfsOpsErr_NODATA
|
||||
|| (parentDefaultACLRes == FhgfsOpsErr_SUCCESS && parentDefaultACL.empty()))
|
||||
{
|
||||
// containing dir has no ACL, so we can continue without one.
|
||||
mode &= ~umask;
|
||||
setMode(mode, umask);
|
||||
}
|
||||
|
||||
if (parentDefaultACLRes != FhgfsOpsErr_SUCCESS && parentDefaultACLRes != FhgfsOpsErr_NODATA)
|
||||
{
|
||||
LogContext(logContext).log(Log_ERR,
|
||||
"Error loading default ACL for directory ID " + parentDir->getID() );
|
||||
retVal = parentDefaultACLRes;
|
||||
goto clean_up;
|
||||
}
|
||||
}
|
||||
|
||||
newEntryInfo.set(NumNodeID(ownerNodeID), parentEntryID, entryID, newName,
|
||||
DirEntryType_DIRECTORY, entryInfoFlags);
|
||||
|
||||
// create remote dir metadata
|
||||
// (we create this before the dentry to reduce the risk of dangling dentries)
|
||||
retVal = mkRemoteDirInode(*parentDir, newName, &newEntryInfo, parentDefaultACLXAttr,
|
||||
accessACLXAttr);
|
||||
|
||||
if ( likely(retVal == FhgfsOpsErr_SUCCESS) )
|
||||
{ // remote dir created => create dentry in parent dir
|
||||
// note: we can't lock before this point, because mkRemoteDirInode will also send a message
|
||||
// that needs to aquire a lock on the ID
|
||||
FileIDLock lock;
|
||||
|
||||
if (parentInfo->getIsBuddyMirrored())
|
||||
lock = {entryLockStore, entryID, true};
|
||||
|
||||
retVal = mkDirDentry(*parentDir, newName, &newEntryInfo, isBuddyMirrored);
|
||||
|
||||
if ( retVal != FhgfsOpsErr_SUCCESS )
|
||||
{ // error (or maybe name just existed already) => compensate metaDir creation
|
||||
// note: unlock needs to happen before a possible remoteDirCompensation, because rmDir
|
||||
// will also need to lock entryID
|
||||
lock = {};
|
||||
mkRemoteDirCompensate(&newEntryInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (app->getFileEventLogger() && getFileEvent())
|
||||
{
|
||||
EventContext eventCtx = makeEventContext(&newEntryInfo, newEntryInfo.getParentEntryID(),
|
||||
// This is not the secondary node if mkDirPrimary() was called.
|
||||
getMsgHeaderUserID(), "", INITIAL_DIR_LINK_COUNT, false);
|
||||
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clean_up:
|
||||
metaStore->releaseDir(parentInfo->getEntryID() );
|
||||
|
||||
if (modEventLoggingEnabled)
|
||||
modEventFlusher->add(ModificationEvent_DIRCREATED, newEntryInfo.getEntryID());
|
||||
|
||||
return boost::make_unique<ResponseState>(retVal, std::move(newEntryInfo));
|
||||
}
|
||||
|
||||
std::unique_ptr<MkDirMsgEx::ResponseState> MkDirMsgEx::mkDirSecondary()
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
// only create the dentry here; forwarding of inode creation directly happens in MkLocalFileMsg
|
||||
// and error handling and compensation is done by primary
|
||||
FhgfsOpsErr retVal;
|
||||
|
||||
// reference parent
|
||||
DirInode* parentDir = metaStore->referenceDir(getParentInfo()->getEntryID(),
|
||||
getParentInfo()->getIsBuddyMirrored(), true);
|
||||
if(!parentDir)
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS, EntryInfo());
|
||||
|
||||
retVal = mkDirDentry(*parentDir, getCreatedEntryInfo()->getFileName(), getCreatedEntryInfo(),
|
||||
getCreatedEntryInfo()->getIsBuddyMirrored());
|
||||
|
||||
metaStore->releaseDir(getParentInfo()->getEntryID());
|
||||
|
||||
return boost::make_unique<ResponseState>(retVal, *getCreatedEntryInfo());
|
||||
}
|
||||
|
||||
FhgfsOpsErr MkDirMsgEx::mkDirDentry(DirInode& parentDir, const std::string& name,
|
||||
const EntryInfo* entryInfo, const bool isBuddyMirrored)
|
||||
{
|
||||
const std::string entryID = entryInfo->getEntryID();
|
||||
const NumNodeID ownerNodeID = entryInfo->getOwnerNodeID();
|
||||
|
||||
DirEntry newDirDentry(DirEntryType_DIRECTORY, name, entryID, ownerNodeID);
|
||||
|
||||
if(isBuddyMirrored)
|
||||
newDirDentry.setBuddyMirrorFeatureFlag();
|
||||
|
||||
const FhgfsOpsErr mkRes = parentDir.makeDirEntry(newDirDentry);
|
||||
|
||||
if (mkRes == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
fixInodeTimestamp(parentDir, parentTimestamps);
|
||||
|
||||
return mkRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create dir inode on a remote server.
|
||||
*
|
||||
* @param name only used for logging
|
||||
* @param mirrorNodeID 0 for disabled mirroring
|
||||
*/
|
||||
FhgfsOpsErr MkDirMsgEx::mkRemoteDirInode(DirInode& parentDir, const std::string& name,
|
||||
EntryInfo* entryInfo, const CharVector& defaultACLXAttr, const CharVector& accessACLXAttr)
|
||||
{
|
||||
const char* logContext = "MkDirMsg (mk dir inode)";
|
||||
|
||||
App* app = Program::getApp();
|
||||
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
|
||||
|
||||
StripePattern* pattern = parentDir.getStripePatternClone();
|
||||
NumNodeID ownerNodeID = entryInfo->getOwnerNodeID();
|
||||
|
||||
RemoteStorageTarget rstInfo;
|
||||
if (parentDir.getIsRstAvailable())
|
||||
rstInfo.set(parentDir.getRemoteStorageTargetInfo());
|
||||
|
||||
LOG_DEBUG(logContext, Log_DEBUG,
|
||||
"Creating dir inode at metadata node: " + ownerNodeID.str() + "; dirname: " + name);
|
||||
|
||||
// prepare request
|
||||
|
||||
NumNodeID parentNodeID = app->getLocalNode().getNumID();
|
||||
MkLocalDirMsg mkMsg(entryInfo, getUserID(), getGroupID(), getMode(), pattern, &rstInfo,
|
||||
parentNodeID, defaultACLXAttr, accessACLXAttr);
|
||||
|
||||
RequestResponseArgs rrArgs(NULL, &mkMsg, NETMSGTYPE_MkLocalDirResp);
|
||||
|
||||
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes() );
|
||||
rrNode.setTargetStates(app->getMetaStateStore() );
|
||||
|
||||
if(entryInfo->getIsBuddyMirrored())
|
||||
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
|
||||
|
||||
do // (this loop just exists to enable the "break"-jump, so it's not really a loop)
|
||||
{
|
||||
// send request to other mds and receive response
|
||||
|
||||
FhgfsOpsErr requestRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
|
||||
if(unlikely(requestRes != FhgfsOpsErr_SUCCESS) )
|
||||
{ // communication error
|
||||
LogContext(logContext).log(Log_WARNING,
|
||||
"Communication with metadata server failed. "
|
||||
"nodeID: " + ownerNodeID.str() + "; " +
|
||||
"dirname: " + name);
|
||||
retVal = requestRes;
|
||||
break;
|
||||
}
|
||||
|
||||
// correct response type received
|
||||
const auto mkRespMsg = (const MkLocalDirRespMsg*)rrArgs.outRespMsg.get();
|
||||
|
||||
FhgfsOpsErr mkRemoteInodeRes = mkRespMsg->getResult();
|
||||
if(mkRemoteInodeRes != FhgfsOpsErr_SUCCESS)
|
||||
{ // error: remote dir inode not created
|
||||
LogContext(logContext).log(Log_WARNING,
|
||||
"Metadata server failed to create dir inode. "
|
||||
"nodeID: " + ownerNodeID.str() + "; " +
|
||||
"dirname: " + name);
|
||||
|
||||
retVal = mkRemoteInodeRes;
|
||||
break;
|
||||
}
|
||||
|
||||
// success: remote dir inode created
|
||||
LOG_DEBUG(logContext, Log_DEBUG,
|
||||
"Metadata server created dir inode. "
|
||||
"nodeID: " + ownerNodeID.str() + "; "
|
||||
"dirname: " + name);
|
||||
|
||||
} while(false);
|
||||
|
||||
|
||||
delete(pattern);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove dir metadata on a remote server to compensate for creation.
|
||||
*/
|
||||
FhgfsOpsErr MkDirMsgEx::mkRemoteDirCompensate(EntryInfo* entryInfo)
|
||||
{
|
||||
LogContext log("MkDirMsg (undo dir inode [" + entryInfo->getFileName() + "])");
|
||||
|
||||
FhgfsOpsErr rmRes = RmDirMsgEx::rmRemoteDirInode(entryInfo);
|
||||
|
||||
if(unlikely(rmRes != FhgfsOpsErr_SUCCESS) )
|
||||
{ // error
|
||||
log.log(Log_WARNING, std::string("Compensation not completely successful. ") +
|
||||
"File system might contain (uncritical) inconsistencies.");
|
||||
|
||||
return rmRes;
|
||||
}
|
||||
|
||||
log.log(Log_SPAM, "Creation of dir inode compensated");
|
||||
|
||||
return rmRes;
|
||||
}
|
||||
|
||||
void MkDirMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
// secondary needs to know the created entryInfo, because it needs to use the same information
|
||||
setCreatedEntryInfo(&newEntryInfo);
|
||||
|
||||
// not needed on secondary
|
||||
clearPreferredNodes();
|
||||
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_MkDirResp);
|
||||
}
|
||||
53
meta/source/net/message/storage/creating/MkDirMsgEx.h
Normal file
53
meta/source/net/message/storage/creating/MkDirMsgEx.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/creating/MkDirMsg.h>
|
||||
#include <common/net/message/storage/creating/MkDirRespMsg.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
|
||||
class MkDirMsgEx : public MirroredMessage<MkDirMsg, std::tuple<HashDirLock, FileIDLock, ParentNameLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorAndEntryResponseState<MkDirRespMsg, NETMSGTYPE_MkDir> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock, ParentNameLock> lock(EntryLockStore& store) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
bool isMirrored() override { return getParentInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
// Initial hard link count for a newly created directory (self and "." entry).
|
||||
// Note: ".." entry increments the parent's link count, not this directory's.
|
||||
static constexpr unsigned INITIAL_DIR_LINK_COUNT = 2;
|
||||
|
||||
std::string entryID;
|
||||
|
||||
std::unique_ptr<ResponseState> mkDirPrimary(ResponseContext& ctx);
|
||||
std::unique_ptr<ResponseState> mkDirSecondary();
|
||||
|
||||
FhgfsOpsErr mkDirDentry(DirInode& parentDir, const std::string& name,
|
||||
const EntryInfo* entryInfo, const bool isBuddyMirrored);
|
||||
|
||||
FhgfsOpsErr mkRemoteDirInode(DirInode& parentDir, const std::string& name,
|
||||
EntryInfo* entryInfo, const CharVector& defaultACLXAttr, const CharVector& accessACLXAttr);
|
||||
FhgfsOpsErr mkRemoteDirCompensate(EntryInfo* entryInfo);
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<MkDirRespMsg&>(resp).getResult();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "MkDirMsgEx/forward"; }
|
||||
|
||||
EntryInfo newEntryInfo;
|
||||
};
|
||||
|
||||
136
meta/source/net/message/storage/creating/MkFileMsgEx.cpp
Normal file
136
meta/source/net/message/storage/creating/MkFileMsgEx.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#include <common/net/message/storage/creating/MkFileRespMsg.h>
|
||||
#include <common/net/message/control/GenericResponseMsg.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <common/storage/StatData.h>
|
||||
#include <components/FileEventLogger.h>
|
||||
#include <net/msghelpers/MsgHelperMkFile.h>
|
||||
#include <program/Program.h>
|
||||
#include "MkFileMsgEx.h"
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock> MkFileMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
FileIDLock dirLock(&store, getParentInfo()->getEntryID(), true);
|
||||
ParentNameLock dentryLock(&store, getParentInfo()->getEntryID(), getNewName());
|
||||
FileIDLock fileLock(&store, newEntryID, true);
|
||||
|
||||
return std::make_tuple(
|
||||
std::move(dirLock),
|
||||
std::move(dentryLock),
|
||||
std::move(fileLock));
|
||||
}
|
||||
|
||||
|
||||
bool MkFileMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
#ifdef BEEGFS_DEBUG
|
||||
const char* logContext = "MkFileMsg incoming";
|
||||
#endif // BEEGFS_DEBUG
|
||||
|
||||
LOG_DEBUG(logContext, Log_DEBUG, "parentEntryID: " +
|
||||
getParentInfo()->getEntryID() + " newFileName: " + getNewName() );
|
||||
|
||||
LOG_DEBUG(logContext, Log_DEBUG,
|
||||
"BuddyMirrored: " + std::string(getParentInfo()->getIsBuddyMirrored() ? "'Yes'" : "'No'") +
|
||||
" Secondary: " + std::string(hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond) ?
|
||||
"Yes" : "No") );
|
||||
|
||||
if (!hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
|
||||
createTime = TimeAbs().getTimeval()->tv_sec;
|
||||
|
||||
mkDetails = {getNewName(), getUserID(), getGroupID(), getMode(), getUmask(), createTime};
|
||||
|
||||
if (getParentInfo()->getIsBuddyMirrored())
|
||||
{
|
||||
if (!hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
|
||||
newEntryID = StorageTk::generateFileID(Program::getApp()->getLocalNode().getNumID());
|
||||
else
|
||||
newEntryID = getNewEntryID();
|
||||
|
||||
mkDetails.setNewEntryID(newEntryID.c_str());
|
||||
}
|
||||
|
||||
BaseType::processIncoming(ctx);
|
||||
|
||||
updateNodeOp(ctx, MetaOpCounter_MKFILE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> MkFileMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
if (isSecondary)
|
||||
return executeSecondary();
|
||||
else
|
||||
return executePrimary();
|
||||
}
|
||||
|
||||
std::unique_ptr<MkFileMsgEx::ResponseState> MkFileMsgEx::executePrimary()
|
||||
{
|
||||
DirInode* dir = Program::getApp()->getMetaStore()->referenceDir(
|
||||
getParentInfo()->getEntryID(), getParentInfo()->getIsBuddyMirrored(), true);
|
||||
if (!dir)
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS, entryInfo);
|
||||
|
||||
FhgfsOpsErr mkRes;
|
||||
if (isMsgHeaderFeatureFlagSet(MKFILEMSG_FLAG_STORAGEPOOLID))
|
||||
{
|
||||
mkRes = MsgHelperMkFile::mkFile(*dir, &mkDetails, &getPreferredNodes(),
|
||||
getNumTargets(), getChunkSize(), NULL, getRemoteStorageTarget(),
|
||||
&entryInfo, &inodeData, storagePoolId);
|
||||
}
|
||||
else
|
||||
{
|
||||
mkRes = MsgHelperMkFile::mkFile(*dir, &mkDetails, &getPreferredNodes(),
|
||||
getNumTargets(), getChunkSize(), NULL, getRemoteStorageTarget(), &entryInfo, &inodeData);
|
||||
}
|
||||
|
||||
if (mkRes == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
fixInodeTimestamp(*dir, dirTimestamps);
|
||||
|
||||
Program::getApp()->getMetaStore()->releaseDir(dir->getID());
|
||||
|
||||
if (mkRes == FhgfsOpsErr_SUCCESS && Program::getApp()->getFileEventLogger() && getFileEvent())
|
||||
{
|
||||
EventContext eventCtx = makeEventContext(&entryInfo, entryInfo.getParentEntryID(),
|
||||
// This is not the secondary node if executePrimary() was called.
|
||||
getMsgHeaderUserID(), "", this->inodeData.getInodeStatData()->getNumHardlinks(), false);
|
||||
|
||||
logEvent(Program::getApp()->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(mkRes, entryInfo);
|
||||
}
|
||||
|
||||
std::unique_ptr<MkFileMsgEx::ResponseState> MkFileMsgEx::executeSecondary()
|
||||
{
|
||||
StripePattern* stripePattern = getPattern().clone();
|
||||
|
||||
FileInodeStoreData inodeData;
|
||||
|
||||
DirInode* dir = Program::getApp()->getMetaStore()->referenceDir(
|
||||
getParentInfo()->getEntryID(), getParentInfo()->getIsBuddyMirrored(), true);
|
||||
if (!dir)
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS, entryInfo);
|
||||
|
||||
FhgfsOpsErr mkRes = MsgHelperMkFile::mkFile(*dir, &mkDetails, &getPreferredNodes(),
|
||||
getNumTargets(), getChunkSize(), stripePattern, getRemoteStorageTarget(), &entryInfo, &inodeData);
|
||||
|
||||
if (mkRes == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
fixInodeTimestamp(*dir, dirTimestamps);
|
||||
|
||||
Program::getApp()->getMetaStore()->releaseDir(dir->getID());
|
||||
|
||||
return boost::make_unique<ResponseState>(mkRes, entryInfo);
|
||||
}
|
||||
|
||||
void MkFileMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
// secondary needs to know the created entryID and stripe pattern, because it needs to use the
|
||||
// same information
|
||||
setNewEntryID(newEntryID.c_str());
|
||||
setPattern(inodeData.getStripePattern());
|
||||
setRemoteStorageTarget(getRemoteStorageTarget());
|
||||
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_MkFileResp);
|
||||
}
|
||||
48
meta/source/net/message/storage/creating/MkFileMsgEx.h
Normal file
48
meta/source/net/message/storage/creating/MkFileMsgEx.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/creating/MkFileMsg.h>
|
||||
#include <common/net/message/storage/creating/MkFileRespMsg.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
|
||||
class MkFileMsgEx : public MirroredMessage<MkFileMsg,
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorAndEntryResponseState<MkFileRespMsg, NETMSGTYPE_MkFile> ResponseState;
|
||||
|
||||
MkFileMsgEx()
|
||||
: mkDetails({}, 0, 0, 0, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock> lock(EntryLockStore& store) override;
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
bool isMirrored() override { return getParentInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<ResponseState> executePrimary();
|
||||
std::unique_ptr<ResponseState> executeSecondary();
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<MkFileRespMsg&>(resp).getResult();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "MkFileMsgEx/forward"; }
|
||||
|
||||
FileInodeStoreData inodeData;
|
||||
MkFileDetails mkDetails;
|
||||
std::string newEntryID;
|
||||
|
||||
EntryInfo entryInfo;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
#include <common/net/message/storage/creating/MkFileMsg.h>
|
||||
#include <common/net/message/storage/creating/MkFileRespMsg.h>
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileMsg.h>
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileRespMsg.h>
|
||||
#include <common/storage/striping/StripePattern.h>
|
||||
#include <common/toolkit/MathTk.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <components/FileEventLogger.h>
|
||||
#include <net/msghelpers/MsgHelperMkFile.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include "MkFileWithPatternMsgEx.h"
|
||||
|
||||
// Called from fhgfs-ctl (online_cfg) to create a file on a specific node.
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock> MkFileWithPatternMsgEx::lock(
|
||||
EntryLockStore& store)
|
||||
{
|
||||
// no need to lock the created file as well, since
|
||||
// a) no other operation can create the same file id
|
||||
// b) until we finish, no part of the system except us knows the new file id
|
||||
// c) if bulk resync gets the file while it is incomplete, individual resync will get it again
|
||||
FileIDLock dirLock(&store, getParentInfo()->getEntryID(), true);
|
||||
ParentNameLock dentryLock(&store, getParentInfo()->getEntryID(), getNewFileName());
|
||||
FileIDLock fileLock(&store, entryID, true);
|
||||
|
||||
return std::make_tuple(std::move(dirLock), std::move(dentryLock), std::move(fileLock));
|
||||
}
|
||||
|
||||
bool MkFileWithPatternMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
const EntryInfo* parentInfo = getParentInfo();
|
||||
std::string newFileName = getNewFileName();
|
||||
EntryInfo newEntryInfo;
|
||||
|
||||
LOG_DEBUG("MkFileWithPatternMsg", Log_DEBUG,
|
||||
" parentDirID: " + parentInfo->getEntryID() + " newFileName: " + newFileName
|
||||
+ " isBuddyMirrored: " + (parentInfo->getIsBuddyMirrored() ? "'true'" : "'false'"));
|
||||
|
||||
// create ID first
|
||||
if (parentInfo->getIsBuddyMirrored())
|
||||
entryID = StorageTk::generateFileID(Program::getApp()->getLocalNode().getNumID());
|
||||
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> MkFileWithPatternMsgEx::executeLocally(
|
||||
ResponseContext& ctx, bool isSecondary)
|
||||
{
|
||||
EntryInfo newEntryInfo;
|
||||
|
||||
MkFileDetails mkDetails(getNewFileName(), getUserID(), getGroupID(), getMode(), getUmask(),
|
||||
TimeAbs().getTimeval()->tv_sec);
|
||||
if (!entryID.empty())
|
||||
mkDetails.setNewEntryID(entryID.c_str());
|
||||
|
||||
FhgfsOpsErr mkRes = mkFile(getParentInfo(), mkDetails, &newEntryInfo, inodeDiskData);
|
||||
|
||||
updateNodeOp(ctx, MetaOpCounter_MKFILE);
|
||||
|
||||
return boost::make_unique<ResponseState>(mkRes, std::move(newEntryInfo));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param dir current directory
|
||||
* @param currentDepth 1-based path depth
|
||||
*/
|
||||
FhgfsOpsErr MkFileWithPatternMsgEx::mkFile(const EntryInfo* parentInfo, MkFileDetails& mkDetails,
|
||||
EntryInfo* outEntryInfo, FileInodeStoreData& inodeDiskData)
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
FhgfsOpsErr retVal;
|
||||
|
||||
// reference parent
|
||||
DirInode* dir = metaStore->referenceDir(parentInfo->getEntryID(),
|
||||
parentInfo->getIsBuddyMirrored(), true);
|
||||
if ( !dir )
|
||||
return FhgfsOpsErr_PATHNOTEXISTS;
|
||||
|
||||
// create meta file
|
||||
retVal = mkMetaFile(*dir, mkDetails, outEntryInfo, inodeDiskData);
|
||||
|
||||
if (shouldFixTimestamps())
|
||||
fixInodeTimestamp(*dir, dirTimestamps);
|
||||
|
||||
// clean-up
|
||||
metaStore->releaseDir(parentInfo->getEntryID());
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an inode and directory-entry
|
||||
*/
|
||||
FhgfsOpsErr MkFileWithPatternMsgEx::mkMetaFile(DirInode& dir, MkFileDetails& mkDetails,
|
||||
EntryInfo* outEntryInfo, FileInodeStoreData& inodeDiskData)
|
||||
{
|
||||
// note: to guarantee amtomicity of file creation (from the view of a client), we have
|
||||
// to create the inode first and insert the directory entry afterwards
|
||||
|
||||
std::unique_ptr<StripePattern> stripePattern(getPattern().clone());
|
||||
const UInt16Vector* stripeTargets = stripePattern->getStripeTargetIDs();
|
||||
StoragePoolId storagePoolId = stripePattern->getStoragePoolId();
|
||||
|
||||
if (stripeTargets->empty())
|
||||
{
|
||||
StoragePoolPtr storagePool =
|
||||
Program::getApp()->getStoragePoolStore()->getPool(storagePoolId);
|
||||
|
||||
if (!storagePool)
|
||||
{
|
||||
LOG(GENERAL, ERR, "Given Storage Pool ID "
|
||||
+ StringTk::uintToStr(storagePoolId.val()) + " doesn't exist.");
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
UInt16Vector chosenStripeTargets;
|
||||
|
||||
if(stripePattern->getPatternType() == StripePatternType_BuddyMirror)
|
||||
{
|
||||
storagePool->getBuddyCapacityPools()->chooseStorageTargets(
|
||||
stripePattern->getDefaultNumTargets(), stripePattern->getMinNumTargets(),
|
||||
nullptr, &chosenStripeTargets);
|
||||
}
|
||||
else
|
||||
{
|
||||
storagePool->getTargetCapacityPools()->chooseStorageTargets(
|
||||
stripePattern->getDefaultNumTargets(), stripePattern->getMinNumTargets(),
|
||||
nullptr, &chosenStripeTargets);
|
||||
}
|
||||
stripePattern->getStripeTargetIDsModifyable()->swap(chosenStripeTargets);
|
||||
}
|
||||
|
||||
// check if num targets and target list match and if targets actually exist
|
||||
if(stripeTargets->empty() || (stripeTargets->size() < stripePattern->getMinNumTargets() ) )
|
||||
{
|
||||
LOG(GENERAL, ERR, "No (or not enough) storage targets defined.",
|
||||
("numTargets", stripeTargets->size()),
|
||||
("expectedMinNumTargets", stripePattern->getMinNumTargets()));
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
for (auto iter = stripeTargets->begin(); iter != stripeTargets->end(); iter++)
|
||||
{
|
||||
if(stripePattern->getPatternType() == StripePatternType_BuddyMirror)
|
||||
{
|
||||
if (!Program::getApp()->getStorageBuddyGroupMapper()->getPrimaryTargetID(*iter))
|
||||
{
|
||||
LOG(GENERAL, ERR, "Unknown buddy group targets defined.",
|
||||
("targetId", *iter));
|
||||
return FhgfsOpsErr_UNKNOWNTARGET;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Program::getApp()->getTargetMapper()->targetExists(*iter))
|
||||
{
|
||||
LOG(GENERAL, ERR, "Unknown storage targets defined.",
|
||||
("targetId", *iter));
|
||||
return FhgfsOpsErr_UNKNOWNTARGET;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if chunk size satisfies constraints
|
||||
if (!MathTk::isPowerOfTwo(stripePattern->getChunkSize()))
|
||||
{
|
||||
LOG(GENERAL, DEBUG, "Invalid chunk size: Must be a power of two.",
|
||||
stripePattern->getChunkSize());
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
if (stripePattern->getChunkSize() < STRIPEPATTERN_MIN_CHUNKSIZE)
|
||||
{
|
||||
LOG(GENERAL, DEBUG, "Invalid chunk size: Below minimum size.",
|
||||
stripePattern->getChunkSize(),
|
||||
("minChunkSize", STRIPEPATTERN_MIN_CHUNKSIZE));
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
return Program::getApp()->getMetaStore()->mkNewMetaFile(
|
||||
dir, &mkDetails, std::move(stripePattern), getRemoteStorageTarget(), outEntryInfo,
|
||||
&inodeDiskData);
|
||||
// (note: internally deletes stripePattern)
|
||||
}
|
||||
|
||||
void MkFileWithPatternMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
UInt16List preferredTargets; // can be an empty dummy
|
||||
std::string newFileName = getNewFileName();
|
||||
|
||||
MkFileMsg mkFileMsg(getParentInfo(), newFileName, getUserID(), getGroupID(), getMode(),
|
||||
getUmask(), &preferredTargets);
|
||||
|
||||
mkFileMsg.addFlag(NetMessageHeader::Flag_BuddyMirrorSecond);
|
||||
// secondary needs to know the created entryID ans stripe pattern, because it needs to use the
|
||||
// same information
|
||||
mkFileMsg.setNewEntryID(entryID.c_str());
|
||||
mkFileMsg.setPattern(inodeDiskData.getStripePattern());
|
||||
mkFileMsg.setDirTimestamps(dirTimestamps);
|
||||
mkFileMsg.setCreateTime(inodeDiskData.getInodeStatData()->getCreationTimeSecs());
|
||||
mkFileMsg.setRemoteStorageTarget(getRemoteStorageTarget());
|
||||
|
||||
sendToSecondary(ctx, mkFileMsg, NETMSGTYPE_MkFileResp);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/creating/MkFileRespMsg.h>
|
||||
#include <common/net/message/storage/creating/MkFileWithPatternMsg.h>
|
||||
#include <common/net/message/storage/creating/MkFileWithPatternRespMsg.h>
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <storage/MetaStore.h>
|
||||
|
||||
/**
|
||||
* Similar to class MsgHelperMkFile, but called with a create pattern, for example from fhgfs-ctl
|
||||
* or from ioctl calls.
|
||||
*/
|
||||
class MkFileWithPatternMsgEx : public MirroredMessage<MkFileWithPatternMsg,
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorAndEntryResponseState<MkFileWithPatternRespMsg, NETMSGTYPE_MkFileWithPattern>
|
||||
ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock> lock(EntryLockStore& store) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
bool isMirrored() override { return getParentInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
MirroredTimestamps dirTimestamps;
|
||||
|
||||
FhgfsOpsErr mkFile(const EntryInfo* parentInfo, MkFileDetails& mkDetails,
|
||||
EntryInfo* outEntryInfo, FileInodeStoreData& inodeDiskData);
|
||||
FhgfsOpsErr mkMetaFile(DirInode& dir, MkFileDetails& mkDetails,
|
||||
EntryInfo* outEntryInfo, FileInodeStoreData& inodeDiskData);
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<MkFileRespMsg&>(resp).getResult();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "MkFileWithPatternMsgEx/forward"; }
|
||||
|
||||
FileInodeStoreData inodeDiskData;
|
||||
std::string entryID;
|
||||
};
|
||||
|
||||
76
meta/source/net/message/storage/creating/MkLocalDirMsgEx.cpp
Normal file
76
meta/source/net/message/storage/creating/MkLocalDirMsgEx.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include <common/net/message/control/GenericResponseMsg.h>
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/creating/MkLocalDirRespMsg.h>
|
||||
|
||||
#include "MkLocalDirMsgEx.h"
|
||||
|
||||
HashDirLock MkLocalDirMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
// we usually need not lock anything here, because the inode ID will be completely unknown to
|
||||
// anyone until we finish processing here *and* on the metadata server that sent this message.
|
||||
// during resync though we need to lock the hash dir to avoid interefence between bulk resync and
|
||||
// mod resync.
|
||||
|
||||
// do not lock the hash dir if we are creating the inode on the same meta node as the dentry,
|
||||
// MkDir will have already locked the hash dir.
|
||||
if (!rctx->isLocallyGenerated() && resyncJob && resyncJob->isRunning())
|
||||
return {&store, MetaStorageTk::getMetaInodeHash(getEntryInfo()->getEntryID())};
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool MkLocalDirMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
EntryInfo* entryInfo = getEntryInfo();
|
||||
|
||||
LOG_DBG(GENERAL, DEBUG, "", entryInfo->getEntryID(), entryInfo->getFileName());
|
||||
(void) entryInfo;
|
||||
|
||||
rctx = &ctx;
|
||||
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> MkLocalDirMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
MetaStore* metaStore = app->getMetaStore();
|
||||
StripePattern& pattern = getPattern();
|
||||
|
||||
RemoteStorageTarget* rstInfo = getRemoteStorageTarget();
|
||||
|
||||
EntryInfo *entryInfo = getEntryInfo();
|
||||
NumNodeID parentNodeID = getParentNodeID();
|
||||
|
||||
NumNodeID ownerNodeID = entryInfo->getIsBuddyMirrored()
|
||||
? NumNodeID(app->getMetaBuddyGroupMapper()->getLocalGroupID() )
|
||||
: app->getLocalNode().getNumID();
|
||||
|
||||
DirInode newDir(entryInfo->getEntryID(), getMode(), getUserID(),
|
||||
getGroupID(), ownerNodeID, pattern, entryInfo->getIsBuddyMirrored());
|
||||
|
||||
newDir.setParentInfoInitial(entryInfo->getParentEntryID(), parentNodeID);
|
||||
|
||||
FhgfsOpsErr mkRes = metaStore->makeDirInode(newDir, getDefaultACLXAttr(), getAccessACLXAttr() );
|
||||
|
||||
if (!rstInfo->hasInvalidVersion() && (mkRes == FhgfsOpsErr_SUCCESS))
|
||||
{
|
||||
FhgfsOpsErr setRstRes = newDir.setRemoteStorageTarget(*rstInfo);
|
||||
if (setRstRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LogContext("MkLocalDir").log(Log_WARNING, "Failed to set remote storage targets for "
|
||||
"dirID: " + newDir.getID() + ". RST might be invalid.");
|
||||
}
|
||||
}
|
||||
|
||||
if (mkRes == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
fixInodeTimestamp(newDir, dirTimestamps);
|
||||
|
||||
return boost::make_unique<ResponseState>(mkRes);
|
||||
}
|
||||
|
||||
void MkLocalDirMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_MkLocalDirResp);
|
||||
}
|
||||
36
meta/source/net/message/storage/creating/MkLocalDirMsgEx.h
Normal file
36
meta/source/net/message/storage/creating/MkLocalDirMsgEx.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/creating/MkLocalDirMsg.h>
|
||||
#include <common/net/message/storage/creating/MkLocalDirRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <storage/MetaStore.h>
|
||||
|
||||
class MkLocalDirMsgEx : public MirroredMessage<MkLocalDirMsg, HashDirLock>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<MkLocalDirRespMsg, NETMSGTYPE_MkLocalDir> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
HashDirLock lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
ResponseContext* rctx;
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<MkLocalDirRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "MkLocalDirMsgEx/forward"; }
|
||||
};
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#include <common/net/message/storage/creating/MoveFileInodeRespMsg.h>
|
||||
#include "MoveFileInodeMsgEx.h"
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock> MoveFileInodeMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
EntryInfo* fileInfo = getFromFileEntryInfo();
|
||||
|
||||
FileIDLock dirLock(&store, fileInfo->getParentEntryID(), true);
|
||||
ParentNameLock dentryLock(&store, fileInfo->getParentEntryID(), fileInfo->getFileName());
|
||||
FileIDLock inodeLock(&store, fileInfo->getEntryID(), true);
|
||||
|
||||
return std::make_tuple(
|
||||
std::move(dirLock),
|
||||
std::move(dentryLock),
|
||||
std::move(inodeLock));
|
||||
}
|
||||
|
||||
bool MoveFileInodeMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
rctx = &ctx;
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> MoveFileInodeMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
MetaStore* metaStore = app->getMetaStore();
|
||||
MoveFileInodeMsgResponseState resp;
|
||||
|
||||
if (getMode() == FileInodeMode::MODE_INVALID)
|
||||
{
|
||||
// invalid operation requested
|
||||
return boost::make_unique<ResponseState>(std::move(resp));
|
||||
}
|
||||
|
||||
EntryInfo* fromFileInfo = getFromFileEntryInfo();
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_INTERNAL;
|
||||
unsigned linkCount = 0;
|
||||
|
||||
if (getCreateHardlink())
|
||||
{
|
||||
std::tie(retVal, linkCount) = metaStore->makeNewHardlink(fromFileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
DirInode* parentDir = metaStore->referenceDir(
|
||||
fromFileInfo->getParentEntryID(), fromFileInfo->getIsBuddyMirrored(), true);
|
||||
|
||||
if (likely(parentDir))
|
||||
{
|
||||
retVal = metaStore->verifyAndMoveFileInode(*parentDir, fromFileInfo, getMode());
|
||||
metaStore->releaseDir(parentDir->getID());
|
||||
}
|
||||
else
|
||||
retVal = FhgfsOpsErr_PATHNOTEXISTS;
|
||||
}
|
||||
|
||||
resp.setResult(retVal);
|
||||
resp.setHardlinkCount(linkCount);
|
||||
return boost::make_unique<ResponseState>(std::move(resp));
|
||||
}
|
||||
|
||||
void MoveFileInodeMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_MoveFileInodeResp);
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/creating/MoveFileInodeMsg.h>
|
||||
#include <common/net/message/storage/creating/MoveFileInodeRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <storage/MetaStore.h>
|
||||
|
||||
class MoveFileInodeMsgResponseState : public MirroredMessageResponseState
|
||||
{
|
||||
public:
|
||||
MoveFileInodeMsgResponseState() : result(FhgfsOpsErr_INTERNAL), linkCount(0) {}
|
||||
|
||||
explicit MoveFileInodeMsgResponseState(Deserializer& des)
|
||||
{
|
||||
serialize(this, des);
|
||||
}
|
||||
|
||||
MoveFileInodeMsgResponseState(MoveFileInodeMsgResponseState&& other) :
|
||||
result(other.result), linkCount(other.linkCount) {}
|
||||
|
||||
void sendResponse(NetMessage::ResponseContext& ctx) override
|
||||
{
|
||||
MoveFileInodeRespMsg resp(result, linkCount);
|
||||
ctx.sendResponse(resp);
|
||||
}
|
||||
|
||||
void setResult(FhgfsOpsErr res) { this->result = res; }
|
||||
void setHardlinkCount(unsigned linkCount) { this->linkCount = linkCount; }
|
||||
bool changesObservableState() const override { return this->result == FhgfsOpsErr_SUCCESS; }
|
||||
|
||||
protected:
|
||||
uint32_t serializerTag() const override { return NETMSGTYPE_MoveFileInodeResp; }
|
||||
|
||||
template<typename This, typename Ctx>
|
||||
static void serialize(This obj, Ctx& ctx)
|
||||
{
|
||||
ctx
|
||||
% obj->result
|
||||
% obj->linkCount;
|
||||
}
|
||||
|
||||
void serializeContents(Serializer& ser) const override
|
||||
{
|
||||
serialize(this, ser);
|
||||
}
|
||||
|
||||
private:
|
||||
FhgfsOpsErr result;
|
||||
unsigned linkCount;
|
||||
};
|
||||
|
||||
class MoveFileInodeMsgEx : public MirroredMessage<MoveFileInodeMsg,
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef MoveFileInodeMsgResponseState ResponseState;
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock> lock(EntryLockStore& store) override;
|
||||
bool isMirrored() override { return getFromFileEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
ResponseContext* rctx;
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<MoveFileInodeRespMsg&>(resp).getResult();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "MoveFileInodeMsgEx/forward"; }
|
||||
};
|
||||
67
meta/source/net/message/storage/creating/RmDirEntryMsgEx.cpp
Normal file
67
meta/source/net/message/storage/creating/RmDirEntryMsgEx.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/creating/RmDirEntryRespMsg.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include "RmDirEntryMsgEx.h"
|
||||
|
||||
bool RmDirEntryMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
EntryInfo* parentInfo = getParentInfo();
|
||||
std::string entryName = getEntryName();
|
||||
|
||||
FhgfsOpsErr rmRes = rmDirEntry(parentInfo, entryName);
|
||||
|
||||
ctx.sendResponse(RmDirEntryRespMsg(rmRes) );
|
||||
|
||||
App* app = Program::getApp();
|
||||
app->getNodeOpStats()->updateNodeOp(ctx.getSocket()->getPeerIP(), MetaOpCounter_RMLINK,
|
||||
getMsgHeaderUserID() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FhgfsOpsErr RmDirEntryMsgEx::rmDirEntry(EntryInfo* parentInfo, std::string& entryName)
|
||||
{
|
||||
const char* logContext = "RmDirEntryMsg (rm entry)";
|
||||
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
FhgfsOpsErr retVal;
|
||||
|
||||
|
||||
// reference parent
|
||||
DirInode* parentDir = metaStore->referenceDir(parentInfo->getEntryID(),
|
||||
parentInfo->getIsBuddyMirrored(), true);
|
||||
if(!parentDir)
|
||||
return FhgfsOpsErr_PATHNOTEXISTS;
|
||||
|
||||
DirEntry removeDentry(entryName);
|
||||
bool getRes = parentDir->getDentry(entryName, removeDentry);
|
||||
if(!getRes)
|
||||
retVal = FhgfsOpsErr_PATHNOTEXISTS;
|
||||
else
|
||||
{
|
||||
if(DirEntryType_ISDIR(removeDentry.getEntryType() ) )
|
||||
{ // remove dir link
|
||||
retVal = parentDir->removeDir(entryName, NULL);
|
||||
}
|
||||
else
|
||||
if(DirEntryType_ISFILE(removeDentry.getEntryType() ) )
|
||||
{
|
||||
retVal = parentDir->unlinkDirEntry(entryName, &removeDentry,
|
||||
DirEntry_UNLINK_ID_AND_FILENAME);
|
||||
}
|
||||
else
|
||||
{ // unknown entry type
|
||||
LogContext(logContext).logErr(std::string("Unknown entry type: ") +
|
||||
StringTk::intToStr(removeDentry.getEntryType() ) );
|
||||
|
||||
retVal = FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
// clean-up
|
||||
metaStore->releaseDir(parentInfo->getEntryID() );
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
17
meta/source/net/message/storage/creating/RmDirEntryMsgEx.h
Normal file
17
meta/source/net/message/storage/creating/RmDirEntryMsgEx.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/creating/RmDirEntryMsg.h>
|
||||
#include <storage/MetaStore.h>
|
||||
|
||||
// A repair operation, called from fhgfs-ctl
|
||||
|
||||
class RmDirEntryMsgEx : public RmDirEntryMsg
|
||||
{
|
||||
public:
|
||||
virtual bool processIncoming(ResponseContext& ctx);
|
||||
|
||||
private:
|
||||
FhgfsOpsErr rmDirEntry(EntryInfo* parentInfo, std::string& entryName);
|
||||
};
|
||||
|
||||
229
meta/source/net/message/storage/creating/RmDirMsgEx.cpp
Normal file
229
meta/source/net/message/storage/creating/RmDirMsgEx.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
#include <common/components/streamlistenerv2/IncomingPreprocessedMsgWork.h>
|
||||
#include <common/net/message/control/GenericResponseMsg.h>
|
||||
#include <common/net/message/storage/creating/RmDirRespMsg.h>
|
||||
#include <common/net/message/storage/creating/RmLocalDirMsg.h>
|
||||
#include <common/net/message/storage/creating/RmLocalDirRespMsg.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <components/FileEventLogger.h>
|
||||
#include <components/ModificationEventFlusher.h>
|
||||
#include <program/Program.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include "RmDirMsgEx.h"
|
||||
|
||||
|
||||
bool RmDirMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
BaseType::processIncoming(ctx);
|
||||
|
||||
// update operation counters
|
||||
updateNodeOp(ctx, MetaOpCounter_RMDIR);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock, FileIDLock, ParentNameLock> RmDirMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
EntryInfo delEntryInfo;
|
||||
|
||||
DirInode* parentDir = metaStore->referenceDir(
|
||||
getParentInfo()->getEntryID(), getParentInfo()->getIsBuddyMirrored(), true);
|
||||
if (!parentDir)
|
||||
return {};
|
||||
else
|
||||
{
|
||||
parentDir->getDirEntryInfo(getDelDirName(), delEntryInfo);
|
||||
metaStore->releaseDir(getParentInfo()->getEntryID());
|
||||
}
|
||||
|
||||
FileIDLock parentDirLock;
|
||||
FileIDLock delDirLock;
|
||||
HashDirLock hashLock;
|
||||
|
||||
if (resyncJob && resyncJob->isRunning())
|
||||
hashLock = {&store, MetaStorageTk::getMetaInodeHash(delEntryInfo.getEntryID())};
|
||||
|
||||
// lock directories in deadlock-avoidance order, see MirroredMessage::lock()
|
||||
if (delEntryInfo.getEntryID().empty())
|
||||
{
|
||||
parentDirLock = {&store, getParentInfo()->getEntryID(), true};
|
||||
}
|
||||
else if (delEntryInfo.getEntryID() < getParentInfo()->getEntryID())
|
||||
{
|
||||
delDirLock = {&store, delEntryInfo.getEntryID(), true};
|
||||
parentDirLock = {&store, getParentInfo()->getEntryID(), true};
|
||||
}
|
||||
else if (delEntryInfo.getEntryID() == getParentInfo()->getEntryID())
|
||||
{
|
||||
delDirLock = {&store, delEntryInfo.getEntryID(), true};
|
||||
}
|
||||
else
|
||||
{
|
||||
parentDirLock = {&store, getParentInfo()->getEntryID(), true};
|
||||
delDirLock = {&store, delEntryInfo.getEntryID(), true};
|
||||
}
|
||||
|
||||
ParentNameLock parentNameLock(&store, getParentInfo()->getEntryID(), getDelDirName());
|
||||
|
||||
return std::make_tuple(
|
||||
std::move(hashLock),
|
||||
std::move(parentDirLock),
|
||||
std::move(delDirLock),
|
||||
std::move(parentNameLock));
|
||||
}
|
||||
|
||||
std::unique_ptr<RmDirMsgEx::ResponseState> RmDirMsgEx::rmDir(ResponseContext& ctx,
|
||||
const bool isSecondary)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
ModificationEventFlusher* modEventFlusher = Program::getApp()->getModificationEventFlusher();
|
||||
bool modEventLoggingEnabled = modEventFlusher->isLoggingEnabled();
|
||||
|
||||
FhgfsOpsErr retVal;
|
||||
|
||||
EntryInfo* parentInfo = this->getParentInfo();
|
||||
const std::string& delDirName = this->getDelDirName();
|
||||
|
||||
EntryInfo outDelEntryInfo;
|
||||
|
||||
// reference parent
|
||||
DirInode* parentDir = metaStore->referenceDir(parentInfo->getEntryID(),
|
||||
parentInfo->getIsBuddyMirrored(), true);
|
||||
if(!parentDir)
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
|
||||
|
||||
DirEntry removeDirEntry(delDirName);
|
||||
bool getEntryRes = parentDir->getDirDentry(delDirName, removeDirEntry);
|
||||
if(!getEntryRes)
|
||||
retVal = FhgfsOpsErr_PATHNOTEXISTS;
|
||||
else
|
||||
{
|
||||
int additionalFlags = 0;
|
||||
|
||||
const std::string& parentEntryID = parentInfo->getEntryID();
|
||||
removeDirEntry.getEntryInfo(parentEntryID, additionalFlags, &outDelEntryInfo);
|
||||
|
||||
App* app = Program::getApp();
|
||||
NumNodeID ownerNodeID = outDelEntryInfo.getOwnerNodeID();
|
||||
|
||||
// no-comms path: the dir inode is owned by us
|
||||
if ((!isMirrored() && ownerNodeID == app->getLocalNode().getNumID()) ||
|
||||
(isMirrored() &&
|
||||
ownerNodeID.val() == app->getMetaBuddyGroupMapper()->getLocalGroupID()))
|
||||
retVal = app->getMetaStore()->removeDirInode(outDelEntryInfo.getEntryID(),
|
||||
outDelEntryInfo.getIsBuddyMirrored());
|
||||
else if (!isSecondary)
|
||||
retVal = rmRemoteDirInode(&outDelEntryInfo);
|
||||
else
|
||||
retVal = FhgfsOpsErr_SUCCESS;
|
||||
|
||||
if(retVal == FhgfsOpsErr_SUCCESS)
|
||||
{ // local removal succeeded => remove meta dir
|
||||
retVal = parentDir->removeDir(delDirName, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (retVal == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
fixInodeTimestamp(*parentDir, dirTimestamps);
|
||||
|
||||
// clean-up
|
||||
metaStore->releaseDir(parentInfo->getEntryID() );
|
||||
|
||||
if (!isSecondary && retVal == FhgfsOpsErr_SUCCESS && app->getFileEventLogger() && getFileEvent())
|
||||
{
|
||||
EventContext eventCtx = makeEventContext(&outDelEntryInfo, outDelEntryInfo.getParentEntryID(),
|
||||
getMsgHeaderUserID(), "", 0, isSecondary);
|
||||
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
|
||||
if (retVal == FhgfsOpsErr_SUCCESS && modEventLoggingEnabled)
|
||||
{
|
||||
const std::string& entryID = outDelEntryInfo.getEntryID();
|
||||
modEventFlusher->add(ModificationEvent_DIRREMOVED, entryID);
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(retVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inode of this directory
|
||||
*/
|
||||
FhgfsOpsErr RmDirMsgEx::rmRemoteDirInode(EntryInfo* delEntryInfo)
|
||||
{
|
||||
const char* logContext = "RmDirMsg (rm dir inode)";
|
||||
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
|
||||
|
||||
App* app = Program::getApp();
|
||||
NumNodeID ownerNodeID = delEntryInfo->getOwnerNodeID();
|
||||
|
||||
LOG_DEBUG(logContext, Log_DEBUG,
|
||||
"Removing dir inode from metadata node: " + ownerNodeID.str() + "; "
|
||||
"dirname: " + delEntryInfo->getFileName() + "; isBuddyMirrored: " +
|
||||
StringTk::intToStr(delEntryInfo->getIsBuddyMirrored()) );
|
||||
|
||||
// prepare request
|
||||
|
||||
RmLocalDirMsg rmMsg(delEntryInfo);
|
||||
|
||||
RequestResponseArgs rrArgs(NULL, &rmMsg, NETMSGTYPE_RmLocalDirResp);
|
||||
|
||||
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes() );
|
||||
rrNode.setTargetStates(app->getMetaStateStore() );
|
||||
|
||||
if (delEntryInfo->getIsBuddyMirrored())
|
||||
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
|
||||
|
||||
do // (this loop just exists to enable the "break"-jump, so it's not really a loop)
|
||||
{
|
||||
// send request to other mds and receive response
|
||||
|
||||
FhgfsOpsErr requestRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
|
||||
if(requestRes != FhgfsOpsErr_SUCCESS)
|
||||
{ // communication error
|
||||
LogContext(logContext).log(Log_WARNING,
|
||||
"Communication with metadata server failed. "
|
||||
"ownerNodeID: " + ownerNodeID.str() + "; "
|
||||
"dirID: " + delEntryInfo->getEntryID() + "; " +
|
||||
"dirname: " + delEntryInfo->getFileName() );
|
||||
retVal = requestRes;
|
||||
break;
|
||||
}
|
||||
|
||||
// correct response type received
|
||||
const auto rmRespMsg = (const RmLocalDirRespMsg*)rrArgs.outRespMsg.get();
|
||||
|
||||
retVal = rmRespMsg->getResult();
|
||||
if(unlikely( (retVal != FhgfsOpsErr_SUCCESS) && (retVal != FhgfsOpsErr_NOTEMPTY) ) )
|
||||
{ // error: dir inode not removed
|
||||
std::string errString = boost::lexical_cast<std::string>(retVal);
|
||||
|
||||
LogContext(logContext).log(Log_DEBUG,
|
||||
"Metadata server was unable to remove dir inode. "
|
||||
"ownerNodeID: " + ownerNodeID.str() + "; "
|
||||
"dirID: " + delEntryInfo->getEntryID() + "; " +
|
||||
"dirname: " + delEntryInfo->getFileName() + "; " +
|
||||
"error: " + errString);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// success: dir inode removed
|
||||
LOG_DEBUG(logContext, Log_DEBUG,
|
||||
"Metadata server removed dir inode: "
|
||||
"ownerNodeID: " + ownerNodeID.str() + "; "
|
||||
"dirID: " + delEntryInfo->getEntryID() + "; " +
|
||||
"dirname: " + delEntryInfo->getFileName() );
|
||||
|
||||
} while(false);
|
||||
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void RmDirMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_RmDirResp);
|
||||
}
|
||||
42
meta/source/net/message/storage/creating/RmDirMsgEx.h
Normal file
42
meta/source/net/message/storage/creating/RmDirMsgEx.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/creating/RmDirMsg.h>
|
||||
#include <common/net/message/storage/creating/RmDirRespMsg.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
|
||||
class RmDirMsgEx : public MirroredMessage<RmDirMsg,
|
||||
std::tuple<HashDirLock, FileIDLock, FileIDLock, ParentNameLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<RmDirRespMsg, NETMSGTYPE_RmDir> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock, FileIDLock, ParentNameLock>
|
||||
lock(EntryLockStore& store) override;
|
||||
|
||||
static FhgfsOpsErr rmRemoteDirInode(EntryInfo* delEntryInfo);
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override
|
||||
{
|
||||
return rmDir(ctx, isSecondary);
|
||||
}
|
||||
|
||||
bool isMirrored() override { return getParentInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<ResponseState> rmDir(ResponseContext& ctx, const bool isSecondary);
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<RmDirRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "RmDirMsgEx/forward"; }
|
||||
};
|
||||
|
||||
56
meta/source/net/message/storage/creating/RmLocalDirMsgEx.cpp
Normal file
56
meta/source/net/message/storage/creating/RmLocalDirMsgEx.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/creating/RmLocalDirRespMsg.h>
|
||||
#include <common/toolkit/MetaStorageTk.h>
|
||||
|
||||
#include "RmLocalDirMsgEx.h"
|
||||
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock> RmLocalDirMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
HashDirLock hashLock;
|
||||
|
||||
// if we are currently under modsync, we must lock the hash dir from which we are removing the
|
||||
// inode. otherwise bulk sync may interfere with mod sync and cause the resync to fail.
|
||||
// do not lock the hash dir if we are removing the inode from the same meta node as the dentry,
|
||||
// RmDir will have already locked the hash dir.
|
||||
if (!rctx->isLocallyGenerated() && resyncJob && resyncJob->isRunning())
|
||||
hashLock = {&store, MetaStorageTk::getMetaInodeHash(getDelEntryInfo()->getEntryID())};
|
||||
|
||||
return std::make_tuple(
|
||||
std::move(hashLock),
|
||||
FileIDLock(&store, getDelEntryInfo()->getEntryID(), true));
|
||||
}
|
||||
|
||||
bool RmLocalDirMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
rctx = &ctx;
|
||||
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> RmLocalDirMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
return rmDir();
|
||||
}
|
||||
|
||||
std::unique_ptr<RmLocalDirMsgEx::ResponseState> RmLocalDirMsgEx::rmDir()
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
EntryInfo* delEntryInfo = this->getDelEntryInfo();
|
||||
|
||||
LOG_DEBUG("RmLocalDirMsgEx (rmDir)", Log_DEBUG,
|
||||
"Removing local dir inode: " + delEntryInfo->getFileName() + "; isBuddyMirrored: " +
|
||||
StringTk::intToStr(delEntryInfo->getIsBuddyMirrored()) );
|
||||
|
||||
FhgfsOpsErr res = metaStore->removeDirInode(delEntryInfo->getEntryID(),
|
||||
delEntryInfo->getIsBuddyMirrored());
|
||||
|
||||
return boost::make_unique<ResponseState>(res);
|
||||
}
|
||||
|
||||
void RmLocalDirMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_RmLocalDirResp);
|
||||
}
|
||||
38
meta/source/net/message/storage/creating/RmLocalDirMsgEx.h
Normal file
38
meta/source/net/message/storage/creating/RmLocalDirMsgEx.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/creating/RmLocalDirMsg.h>
|
||||
#include <common/net/message/storage/creating/RmLocalDirRespMsg.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
class RmLocalDirMsgEx : public MirroredMessage<RmLocalDirMsg, std::tuple<HashDirLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<RmLocalDirRespMsg, NETMSGTYPE_RmLocalDir> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock> lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getDelEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
ResponseContext* rctx;
|
||||
|
||||
std::unique_ptr<ResponseState> rmDir();
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<RmLocalDirRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "RmLocalDirMsgEx/forward"; }
|
||||
};
|
||||
|
||||
294
meta/source/net/message/storage/creating/UnlinkFileMsgEx.cpp
Normal file
294
meta/source/net/message/storage/creating/UnlinkFileMsgEx.cpp
Normal file
@@ -0,0 +1,294 @@
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileInodeMsg.h>
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileInodeRespMsg.h>
|
||||
#include <components/FileEventLogger.h>
|
||||
#include <net/msghelpers/MsgHelperUnlink.h>
|
||||
#include <program/Program.h>
|
||||
#include "UnlinkFileMsgEx.h"
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock, ParentNameLock, FileIDLock> UnlinkFileMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
HashDirLock hashLock;
|
||||
FileIDLock inodeLock;
|
||||
|
||||
// we also have to lock the inode attached to the dentry - if we delete the inode, we must
|
||||
// exclude concurrent actions on the same inode. if we cannot look up a file inode for the
|
||||
// dentry, nothing bad happens.
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
auto dir = metaStore->referenceDir(getParentInfo()->getEntryID(),
|
||||
getParentInfo()->getIsBuddyMirrored(), false);
|
||||
|
||||
DirEntry dentry(getDelFileName());
|
||||
bool dentryExists = dir->getFileDentry(getDelFileName(), dentry);
|
||||
|
||||
if (dentryExists)
|
||||
{
|
||||
dentry.getEntryInfo(getParentInfo()->getEntryID(), 0, &fileInfo);
|
||||
|
||||
// lock hash dir where we are going to remove (or update) file inode
|
||||
// need to take it only if it is a non-inlined inode and resynch is running
|
||||
if (resyncJob && resyncJob->isRunning() && !dentry.getIsInodeInlined())
|
||||
hashLock = {&store, MetaStorageTk::getMetaInodeHash(dentry.getID())};
|
||||
}
|
||||
|
||||
FileIDLock dirLock(&store, getParentInfo()->getEntryID(), true);
|
||||
ParentNameLock dentryLock(&store, getParentInfo()->getEntryID(), getDelFileName());
|
||||
|
||||
if (dentryExists)
|
||||
inodeLock = {&store, dentry.getID(), true};
|
||||
|
||||
metaStore->releaseDir(dir->getID());
|
||||
return std::make_tuple(std::move(hashLock), std::move(dirLock), std::move(dentryLock), std::move(inodeLock));
|
||||
}
|
||||
|
||||
bool UnlinkFileMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
#ifdef BEEGFS_DEBUG
|
||||
const char* logContext = "UnlinkFileMsg incoming";
|
||||
|
||||
const std::string& removeName = getDelFileName();
|
||||
EntryInfo* parentInfo = getParentInfo();
|
||||
LOG_DEBUG(logContext, Log_DEBUG, "ParentID: " + parentInfo->getEntryID() + "; "
|
||||
"deleteName: " + removeName);
|
||||
#endif // BEEGFS_DEBUG
|
||||
|
||||
// update operation counters (here on top because we have an early sock release in this msg)
|
||||
updateNodeOp(ctx, MetaOpCounter_UNLINK);
|
||||
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> UnlinkFileMsgEx::executeLocally(
|
||||
ResponseContext& ctx, bool isSecondary)
|
||||
{
|
||||
const char* logContext = "Unlink File Msg";
|
||||
App* app = Program::getApp();
|
||||
MetaStore* metaStore = app->getMetaStore();
|
||||
|
||||
// reference parent dir
|
||||
DirInode* dir = metaStore->referenceDir(getParentInfo()->getEntryID(),
|
||||
getParentInfo()->getIsBuddyMirrored(), true);
|
||||
|
||||
if (!dir)
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
|
||||
|
||||
DirEntry dentryToRemove(getDelFileName());
|
||||
if (!dir->getFileDentry(getDelFileName(), dentryToRemove))
|
||||
{
|
||||
metaStore->releaseDir(dir->getID());
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
|
||||
}
|
||||
|
||||
// On meta-mirrored setup, ensure the dentry we just loaded i.e. 'dentryToRemove' has the same entryID as 'fileInfo'.
|
||||
// This check is crucial to detect a very rare race condition where dentry gets unlinked
|
||||
// because the parent directory was not locked during UnlinkFileMsgEx::lock(),
|
||||
// and then reappears because the same file is being created again. If the entryIDs
|
||||
// don't match, it means the dentry has changed, and the exclusive lock taken on the
|
||||
// entryID before won't be effective in preventing future races of ongoing unlink
|
||||
// operation with other filesystem operations (e.g. close()) which might happen on this file.
|
||||
// Therefore, we release the directory from the meta store and return an error saying path
|
||||
// not exists.
|
||||
if (isMirrored() && (dentryToRemove.getEntryID() != this->fileInfo.getEntryID()))
|
||||
{
|
||||
metaStore->releaseDir(dir->getID());
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
|
||||
}
|
||||
|
||||
// re-fetch entryInfo
|
||||
dentryToRemove.getEntryInfo(getParentInfo()->getEntryID(), 0, &fileInfo);
|
||||
|
||||
// check whether local node/group owns file's inode (dentry's owner may/maynot be same)
|
||||
NumNodeID ownerNodeID = fileInfo.getOwnerNodeID();
|
||||
if ( (!isMirrored() && ownerNodeID == app->getLocalNode().getNumID()) ||
|
||||
(isMirrored() &&
|
||||
ownerNodeID.val() == app->getMetaBuddyGroupMapper()->getLocalGroupID()) )
|
||||
{
|
||||
// File inode is on the same metadata node/buddy group as the dentry.
|
||||
if (isSecondary)
|
||||
return executeSecondary(ctx, *dir);
|
||||
else
|
||||
return executePrimary(ctx, *dir);
|
||||
}
|
||||
else
|
||||
{ // Handle case where file inode is on a different metadata node/buddy group than dentry.
|
||||
// Step 1: Remove file's dentry (local operation)
|
||||
// Note: Dentry was already loaded and validated earlier
|
||||
FhgfsOpsErr unlinkRes = FhgfsOpsErr_INTERNAL;
|
||||
unlinkRes = dir->unlinkDirEntry(getDelFileName(), &dentryToRemove, DirEntry_UNLINK_FILENAME);
|
||||
metaStore->releaseDir(dir->getID());
|
||||
if (unlinkRes != FhgfsOpsErr_SUCCESS)
|
||||
return boost::make_unique<ResponseState>(unlinkRes);
|
||||
|
||||
// Step 2: Remove file's inode (remote operation)
|
||||
if (!isSecondary)
|
||||
{
|
||||
unsigned preUnlinkHardlinkCount = 0;
|
||||
|
||||
UnlinkLocalFileInodeMsg unlinkInodeMsg(&fileInfo);
|
||||
RequestResponseArgs rrArgs(NULL, &unlinkInodeMsg, NETMSGTYPE_UnlinkLocalFileInodeResp);
|
||||
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes());
|
||||
rrNode.setTargetStates(app->getMetaStateStore());
|
||||
if (fileInfo.getIsBuddyMirrored())
|
||||
{
|
||||
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
FhgfsOpsErr resp = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
if (unlikely(resp != FhgfsOpsErr_SUCCESS))
|
||||
{
|
||||
LogContext(logContext).log(Log_WARNING,
|
||||
"Communication with metadata server failed. "
|
||||
"nodeID: " + ownerNodeID.str() + "; " +
|
||||
"entryID: " + fileInfo.getEntryID().c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
// response received
|
||||
const auto respMsg = (UnlinkLocalFileInodeRespMsg*) rrArgs.outRespMsg.get();
|
||||
FhgfsOpsErr res = respMsg->getResult();
|
||||
if (res != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
// error: either inode file doesn't exists or some other error happened
|
||||
LogContext(logContext).log(Log_WARNING, "unlink file inode failed! "
|
||||
"nodeID: " + ownerNodeID.str() + "; " +
|
||||
"entryID: " + fileInfo.getEntryID().c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
// since dentry has been removed successfully so all good for user - no need
|
||||
// to return an error if unlinking remote inode fails due to some reasons.
|
||||
// we still need to remove dentry from secondary buddy so should not overwrite
|
||||
// local dentry removal success with some remote error
|
||||
unlinkRes = FhgfsOpsErr_SUCCESS;
|
||||
preUnlinkHardlinkCount = respMsg->getPreUnlinkHardlinkCount();
|
||||
} while (false);
|
||||
|
||||
if (unlinkRes == FhgfsOpsErr_SUCCESS && app->getFileEventLogger() && getFileEvent())
|
||||
{
|
||||
// Determine remaining hardlinks after unlink operation
|
||||
unsigned remainingLinks = (preUnlinkHardlinkCount > 0) ? preUnlinkHardlinkCount - 1 : 0;
|
||||
|
||||
EventContext eventCtx = makeEventContext(&fileInfo, getParentInfo()->getEntryID(),
|
||||
getMsgHeaderUserID(), "", remainingLinks, isSecondary);
|
||||
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(unlinkRes);
|
||||
}
|
||||
|
||||
// Should never reach this point but added to please the compiler
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_SUCCESS);
|
||||
}
|
||||
|
||||
std::unique_ptr<UnlinkFileMsgEx::ResponseState> UnlinkFileMsgEx::executePrimary(
|
||||
ResponseContext& ctx, DirInode& dir)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
unsigned preUnlinkHardlinkCount = 0; // number of hardlink(s) before unlink happens
|
||||
|
||||
// Two alternatives:
|
||||
// 1) early response before chunk files unlink.
|
||||
// 2) normal response after chunk files unlink (incl. chunk files error).
|
||||
if (app->getConfig()->getTuneEarlyUnlinkResponse() && !isMirrored())
|
||||
{
|
||||
// alternative 1: response before chunk files unlink
|
||||
std::unique_ptr<FileInode> unlinkedInode;
|
||||
|
||||
FhgfsOpsErr unlinkMetaRes = MsgHelperUnlink::unlinkMetaFile(dir,
|
||||
getDelFileName(), &unlinkedInode, preUnlinkHardlinkCount);
|
||||
|
||||
app->getMetaStore()->releaseDir(dir.getID());
|
||||
earlyComplete(ctx, ResponseState(unlinkMetaRes));
|
||||
|
||||
/* note: if the file is still opened or if there were hardlinks then unlinkedInode will be
|
||||
NULL even on FhgfsOpsErr_SUCCESS */
|
||||
if ((unlinkMetaRes == FhgfsOpsErr_SUCCESS) && unlinkedInode)
|
||||
MsgHelperUnlink::unlinkChunkFiles(unlinkedInode.release(), getMsgHeaderUserID() );
|
||||
|
||||
if (unlinkMetaRes == FhgfsOpsErr_SUCCESS && app->getFileEventLogger() && getFileEvent())
|
||||
{
|
||||
// Determine remaining hardlinks after unlink operation
|
||||
unsigned remainingLinks = (preUnlinkHardlinkCount > 0) ? preUnlinkHardlinkCount - 1 : 0;
|
||||
|
||||
EventContext eventCtx = makeEventContext(&fileInfo, getParentInfo()->getEntryID(),
|
||||
// This is not the secondary node if executePrimary() was called.
|
||||
getMsgHeaderUserID(), "", remainingLinks, false);
|
||||
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// alternative 2: response after chunk files unlink
|
||||
std::unique_ptr<FileInode> unlinkedInode;
|
||||
FhgfsOpsErr unlinkRes = MsgHelperUnlink::unlinkMetaFile(dir,
|
||||
getDelFileName(), &unlinkedInode, preUnlinkHardlinkCount);
|
||||
|
||||
if ((unlinkRes == FhgfsOpsErr_SUCCESS) && shouldFixTimestamps())
|
||||
{
|
||||
fixInodeTimestamp(dir, dirTimestamps);
|
||||
if (!unlinkedInode)
|
||||
{
|
||||
auto [file, referenceRes] = app->getMetaStore()->referenceFile(&fileInfo);
|
||||
if (file)
|
||||
{
|
||||
fixInodeTimestamp(*file, fileTimestamps, &fileInfo);
|
||||
app->getMetaStore()->releaseFile(dir.getID(), file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* note: if the file is still opened or if there are/were hardlinks then unlinkedInode will be
|
||||
NULL even on FhgfsOpsErr_SUCCESS */
|
||||
if ((unlinkRes == FhgfsOpsErr_SUCCESS) && unlinkedInode)
|
||||
MsgHelperUnlink::unlinkChunkFiles(unlinkedInode.release(), getMsgHeaderUserID());
|
||||
|
||||
app->getMetaStore()->releaseDir(dir.getID());
|
||||
if ((unlinkRes == FhgfsOpsErr_SUCCESS) && app->getFileEventLogger() && getFileEvent())
|
||||
{
|
||||
// Determine remaining hardlinks after unlink operation
|
||||
unsigned remainingLinks = (preUnlinkHardlinkCount > 0) ? preUnlinkHardlinkCount - 1 : 0;
|
||||
|
||||
EventContext eventCtx = makeEventContext(&fileInfo, getParentInfo()->getEntryID(),
|
||||
// This is not the secondary node if executePrimary() was called.
|
||||
getMsgHeaderUserID(), "", remainingLinks, false);
|
||||
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(unlinkRes);
|
||||
}
|
||||
|
||||
std::unique_ptr<UnlinkFileMsgEx::ResponseState> UnlinkFileMsgEx::executeSecondary(
|
||||
ResponseContext& ctx, DirInode& dir)
|
||||
{
|
||||
MetaStore* const metaStore = Program::getApp()->getMetaStore();
|
||||
std::unique_ptr<FileInode> unlinkedInode;
|
||||
unsigned preUnlinkHardlinkCount; // Not used here!
|
||||
|
||||
FhgfsOpsErr unlinkMetaRes = MsgHelperUnlink::unlinkMetaFile(dir,
|
||||
getDelFileName(), &unlinkedInode, preUnlinkHardlinkCount);
|
||||
|
||||
if ((unlinkMetaRes == FhgfsOpsErr_SUCCESS) && shouldFixTimestamps())
|
||||
{
|
||||
fixInodeTimestamp(dir, dirTimestamps);
|
||||
if (!unlinkedInode)
|
||||
{
|
||||
auto [file, referenceRes] = metaStore->referenceFile(&fileInfo);
|
||||
if (file)
|
||||
{
|
||||
fixInodeTimestamp(*file, fileTimestamps, &fileInfo);
|
||||
metaStore->releaseFile(dir.getID(), file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
metaStore->releaseDir(dir.getID());
|
||||
return boost::make_unique<ResponseState>(unlinkMetaRes);
|
||||
}
|
||||
|
||||
void UnlinkFileMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_UnlinkFileResp);
|
||||
}
|
||||
45
meta/source/net/message/storage/creating/UnlinkFileMsgEx.h
Normal file
45
meta/source/net/message/storage/creating/UnlinkFileMsgEx.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/creating/UnlinkFileMsg.h>
|
||||
#include <common/net/message/storage/creating/UnlinkFileRespMsg.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
class UnlinkFileMsgEx : public MirroredMessage<UnlinkFileMsg,
|
||||
std::tuple<HashDirLock, FileIDLock, ParentNameLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<UnlinkFileRespMsg, NETMSGTYPE_UnlinkFile> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock, ParentNameLock, FileIDLock> lock(EntryLockStore& store) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
bool isMirrored() override { return getParentInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
/**
|
||||
* Execute unlink operation on primary and secondary metadata nodes.
|
||||
* Primary handles metadata removal, chunk cleanup, and event logging.
|
||||
* Secondary handles only metadata operations.
|
||||
*
|
||||
* @param ctx Response context
|
||||
* @param dir Parent directory reference. Functions guarantee directory reference
|
||||
* release before return, including error paths.
|
||||
* @return Response state containing operation result.
|
||||
*/
|
||||
std::unique_ptr<ResponseState> executePrimary(ResponseContext& ctx, DirInode& dir);
|
||||
std::unique_ptr<ResponseState> executeSecondary(ResponseContext& ctx, DirInode& dir);
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<UnlinkFileRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "UnlinkFileMsgEx/forward"; }
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/toolkit/MetaStorageTk.h>
|
||||
#include <net/msghelpers/MsgHelperUnlink.h>
|
||||
#include "UnlinkLocalFileInodeMsgEx.h"
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock> UnlinkLocalFileInodeMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
// we must not lock hash dir and inode if it is owned by current node. if it is a locally
|
||||
// generated message which is sent by unlinkmsg and renamev2msg sends then aforementioned
|
||||
// locks are already held by them
|
||||
if (rctx->isLocallyGenerated())
|
||||
return {};
|
||||
|
||||
HashDirLock hashLock;
|
||||
|
||||
if (resyncJob && resyncJob->isRunning())
|
||||
HashDirLock hashLock = {&store, MetaStorageTk::getMetaInodeHash(getDelEntryInfo()->getEntryID())};
|
||||
|
||||
return std::make_tuple(
|
||||
std::move(hashLock),
|
||||
FileIDLock(&store, getDelEntryInfo()->getEntryID(), true));
|
||||
}
|
||||
|
||||
bool UnlinkLocalFileInodeMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
rctx = &ctx;
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> UnlinkLocalFileInodeMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
// Create a copy of the passed-in entryInfo and use it!
|
||||
// Reason:
|
||||
// Some fields of EntryInfo, such as parentEntryID and isInlined, can be modified
|
||||
// by MetaStore::unlinkInodeLater() during unlink processing. Using a copy ensures
|
||||
// that the original (and unmodified) entryInfo is forwarded to the secondary buddy
|
||||
// to avoid different states on meta-buddies.
|
||||
EntryInfo entryInfo;
|
||||
entryInfo.set(getDelEntryInfo());
|
||||
|
||||
UnlinkLocalFileInodeResponseState resp;
|
||||
unsigned outLinkCount = 0;
|
||||
|
||||
std::unique_ptr<FileInode> unlinkedInode;
|
||||
FhgfsOpsErr unlinkInodeRes = MsgHelperUnlink::unlinkFileInode(&entryInfo, &unlinkedInode, outLinkCount);
|
||||
resp.setResult(unlinkInodeRes);
|
||||
resp.setPreUnlinkHardlinkCount(outLinkCount);
|
||||
|
||||
if ((unlinkInodeRes == FhgfsOpsErr_SUCCESS) && unlinkedInode && !isSecondary)
|
||||
{
|
||||
MsgHelperUnlink::unlinkChunkFiles(unlinkedInode.release(), getMsgHeaderUserID());
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(std::move(resp));
|
||||
}
|
||||
|
||||
void UnlinkLocalFileInodeMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_UnlinkLocalFileInodeResp);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileInodeMsg.h>
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileInodeRespMsg.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
class UnlinkLocalFileInodeResponseState : public MirroredMessageResponseState
|
||||
{
|
||||
public:
|
||||
UnlinkLocalFileInodeResponseState() : result(FhgfsOpsErr_INTERNAL), preUnlinkHardlinkCount(0) {}
|
||||
|
||||
UnlinkLocalFileInodeResponseState(FhgfsOpsErr result, unsigned linkCount) :
|
||||
result(result), preUnlinkHardlinkCount(linkCount) {}
|
||||
|
||||
explicit UnlinkLocalFileInodeResponseState(Deserializer& des)
|
||||
{
|
||||
serialize(this, des);
|
||||
}
|
||||
|
||||
UnlinkLocalFileInodeResponseState(UnlinkLocalFileInodeResponseState&& other) :
|
||||
result(other.result), preUnlinkHardlinkCount(other.preUnlinkHardlinkCount) {}
|
||||
|
||||
void sendResponse(NetMessage::ResponseContext& ctx) override
|
||||
{
|
||||
UnlinkLocalFileInodeRespMsg resp(result, preUnlinkHardlinkCount);
|
||||
ctx.sendResponse(resp);
|
||||
}
|
||||
|
||||
void setResult(FhgfsOpsErr res) { this->result = res; }
|
||||
void setPreUnlinkHardlinkCount(unsigned linkCount) { this->preUnlinkHardlinkCount = linkCount; }
|
||||
bool changesObservableState() const override { return true; }
|
||||
|
||||
protected:
|
||||
uint32_t serializerTag() const override { return NETMSGTYPE_UnlinkLocalFileInodeResp; }
|
||||
|
||||
template<typename This, typename Ctx>
|
||||
static void serialize(This obj, Ctx& ctx)
|
||||
{
|
||||
ctx
|
||||
% obj->result
|
||||
% obj->preUnlinkHardlinkCount;
|
||||
}
|
||||
|
||||
void serializeContents(Serializer& ser) const override
|
||||
{
|
||||
serialize(this, ser);
|
||||
}
|
||||
|
||||
private:
|
||||
FhgfsOpsErr result;
|
||||
// The preUnlinkHardlinkCount represents the number of hardlinks that existed before
|
||||
// the actual unlink happens. This is mainly needed for event logging purposes.
|
||||
unsigned preUnlinkHardlinkCount;
|
||||
};
|
||||
|
||||
class UnlinkLocalFileInodeMsgEx : public MirroredMessage<UnlinkLocalFileInodeMsg,
|
||||
std::tuple<HashDirLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef UnlinkLocalFileInodeResponseState ResponseState;
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock> lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getDelEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
ResponseContext* rctx;
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<UnlinkLocalFileInodeRespMsg&>(resp).getResult();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override
|
||||
{
|
||||
return "UnlinkLocalFileInodeMsgEx/forward";
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/listing/ListDirFromOffsetRespMsg.h>
|
||||
#include "ListDirFromOffsetMsgEx.h"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
|
||||
bool ListDirFromOffsetMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
#ifdef BEEGFS_DEBUG
|
||||
const char* logContext = "ListDirFromOffsetMsgEx incoming";
|
||||
#endif // BEEGFS_DEBUG
|
||||
|
||||
EntryInfo* entryInfo = this->getEntryInfo();
|
||||
|
||||
LOG_DEBUG(logContext, Log_SPAM,
|
||||
std::string("serverOffset: ") + StringTk::int64ToStr(getServerOffset() ) + "; " +
|
||||
std::string("maxOutNames: ") + StringTk::int64ToStr(getMaxOutNames() ) + "; " +
|
||||
std::string("filterDots: ") + StringTk::uintToStr(getFilterDots() ) + "; " +
|
||||
std::string("parentEntryID: ") + entryInfo->getParentEntryID() + "; " +
|
||||
std::string("buddyMirrored: ") + (entryInfo->getIsBuddyMirrored() ? "true" : "false") + "; " +
|
||||
std::string("entryID: ") + entryInfo->getEntryID() );
|
||||
|
||||
StringList names;
|
||||
UInt8List entryTypes;
|
||||
StringList entryIDs;
|
||||
Int64List serverOffsets;
|
||||
int64_t newServerOffset = getServerOffset(); // init to something useful
|
||||
|
||||
FhgfsOpsErr listRes = listDirIncremental(entryInfo, &names, &entryTypes, &entryIDs,
|
||||
&serverOffsets, &newServerOffset);
|
||||
|
||||
LOG_DEBUG(logContext, Log_SPAM,
|
||||
std::string("newServerOffset: ") + StringTk::int64ToStr(newServerOffset) + "; " +
|
||||
std::string("names.size: ") + StringTk::int64ToStr(names.size() ) + "; " +
|
||||
std::string("listRes: ") + boost::lexical_cast<std::string>(listRes));
|
||||
|
||||
ctx.sendResponse(
|
||||
ListDirFromOffsetRespMsg(
|
||||
listRes, &names, &entryTypes, &entryIDs, &serverOffsets, newServerOffset) );
|
||||
|
||||
Program::getApp()->getNodeOpStats()->updateNodeOp(ctx.getSocket()->getPeerIP(),
|
||||
MetaOpCounter_READDIR, getMsgHeaderUserID() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FhgfsOpsErr ListDirFromOffsetMsgEx::listDirIncremental(EntryInfo* entryInfo, StringList* outNames,
|
||||
UInt8List* outEntryTypes, StringList* outEntryIDs, Int64List* outServerOffsets,
|
||||
int64_t* outNewOffset)
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
// reference dir
|
||||
DirInode* dir = metaStore->referenceDir(entryInfo->getEntryID(), entryInfo->getIsBuddyMirrored(),
|
||||
true);
|
||||
if(!dir)
|
||||
return FhgfsOpsErr_PATHNOTEXISTS;
|
||||
|
||||
// query contents
|
||||
ListIncExOutArgs outArgs(outNames, outEntryTypes, outEntryIDs, outServerOffsets, outNewOffset);
|
||||
|
||||
FhgfsOpsErr listRes = dir->listIncrementalEx(
|
||||
getServerOffset(), getMaxOutNames(), getFilterDots(), outArgs);
|
||||
|
||||
// clean-up
|
||||
metaStore->releaseDir(entryInfo->getEntryID() );
|
||||
|
||||
return listRes;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <storage/MetaStore.h>
|
||||
#include <common/storage/EntryInfo.h>
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/listing/ListDirFromOffsetMsg.h>
|
||||
|
||||
|
||||
class ListDirFromOffsetMsgEx : public ListDirFromOffsetMsg
|
||||
{
|
||||
public:
|
||||
virtual bool processIncoming(ResponseContext& ctx);
|
||||
|
||||
private:
|
||||
FhgfsOpsErr listDirIncremental(EntryInfo* entryInfo, StringList* outNames,
|
||||
UInt8List* outEntryTypes, StringList* outEntryIDs, Int64List* outServerOffsets,
|
||||
int64_t* outNewOffset);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
#include "FindLinkOwnerMsgEx.h"
|
||||
|
||||
#include <program/Program.h>
|
||||
|
||||
bool FindLinkOwnerMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
MetaStore *metaStore = Program::getApp()->getMetaStore();
|
||||
std::string entryID = this->getEntryID();
|
||||
|
||||
int result = 1;
|
||||
std::string parentEntryID;
|
||||
NumNodeID parentNodeID;
|
||||
|
||||
MetaFileHandle file;
|
||||
DirInode *dir = NULL;
|
||||
// we don't know if the ID belongs to a file or a directory, so we try the file first and if that
|
||||
// does not work, we try directory
|
||||
|
||||
/* TODO: Used by fhgfs-ctl ModeReverseLookup, but this mode does not support to send the
|
||||
* parentID. With the 2014.01 format this should be possible for most files/chunks, though.
|
||||
*/
|
||||
|
||||
// TODO: buddy mirroring => but this is not used anymore, so maybe it's better to just delete it
|
||||
file = metaStore->referenceLoadedFile(parentEntryID, false, entryID);
|
||||
if (file != NULL)
|
||||
{
|
||||
// file->getParentInfo(&parentEntryID, &parentNodeID);
|
||||
result = 0;
|
||||
metaStore->releaseFile(parentEntryID, file);
|
||||
goto send_response;
|
||||
}
|
||||
// TODO: buddy mirroring => but this is not used anymore, so maybe it's better to just delete it
|
||||
dir = metaStore->referenceDir(entryID, false, true);
|
||||
if (dir != NULL)
|
||||
{
|
||||
dir->getParentInfo(&parentEntryID, &parentNodeID);
|
||||
result = 0;
|
||||
metaStore->releaseDir(entryID);
|
||||
goto send_response;
|
||||
}
|
||||
|
||||
send_response:
|
||||
ctx.sendResponse(FindLinkOwnerRespMsg(result, parentNodeID, parentEntryID) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
11
meta/source/net/message/storage/lookup/FindLinkOwnerMsgEx.h
Normal file
11
meta/source/net/message/storage/lookup/FindLinkOwnerMsgEx.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/lookup/FindLinkOwnerMsg.h>
|
||||
#include <common/net/message/storage/lookup/FindLinkOwnerRespMsg.h>
|
||||
|
||||
class FindLinkOwnerMsgEx : public FindLinkOwnerMsg
|
||||
{
|
||||
public:
|
||||
virtual bool processIncoming(ResponseContext& ctx);
|
||||
};
|
||||
|
||||
120
meta/source/net/message/storage/lookup/FindOwnerMsgEx.cpp
Normal file
120
meta/source/net/message/storage/lookup/FindOwnerMsgEx.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/lookup/FindOwnerRespMsg.h>
|
||||
#include "FindOwnerMsgEx.h"
|
||||
|
||||
|
||||
bool FindOwnerMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
#ifdef BEEGFS_DEBUG
|
||||
const char* logContext = "FindOwnerMsg incoming";
|
||||
#endif // BEEGFS_DEBUG
|
||||
|
||||
LOG_DEBUG(logContext, Log_SPAM, "Path: '" + getPath().str() + "'");
|
||||
|
||||
EntryInfoWithDepth ownerInfo;
|
||||
FhgfsOpsErr findRes;
|
||||
|
||||
if(!getSearchDepth() )
|
||||
{ // looking for the root node
|
||||
NumNodeID rootNodeID = Program::getApp()->getRootDir()->getOwnerNodeID();
|
||||
|
||||
if (rootNodeID)
|
||||
{
|
||||
ownerInfo.set(rootNodeID, "", META_ROOTDIR_ID_STR, "/", DirEntryType_DIRECTORY, 0, 0);
|
||||
|
||||
if (Program::getApp()->getRootDir()->getIsBuddyMirrored())
|
||||
ownerInfo.setBuddyMirroredFlag(true);
|
||||
|
||||
findRes = FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
else
|
||||
{ // we don't know the root node
|
||||
findRes = FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // a normal path lookup
|
||||
findRes = findOwner(&ownerInfo);
|
||||
}
|
||||
|
||||
ctx.sendResponse(FindOwnerRespMsg(findRes, &ownerInfo) );
|
||||
|
||||
App* app = Program::getApp();
|
||||
app->getNodeOpStats()->updateNodeOp(ctx.getSocket()->getPeerIP(), MetaOpCounter_FINDOWNER,
|
||||
getMsgHeaderUserID() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Note: This does not work for finding the root dir owner (because it relies on the existence
|
||||
* of a parent dir).
|
||||
*/
|
||||
FhgfsOpsErr FindOwnerMsgEx::findOwner(EntryInfoWithDepth* outInfo)
|
||||
{
|
||||
FhgfsOpsErr findRes = FhgfsOpsErr_INTERNAL;
|
||||
|
||||
App* app = Program::getApp();
|
||||
MetaStore* metaStore = app->getMetaStore();
|
||||
|
||||
unsigned searchDepth = this->getSearchDepth();
|
||||
unsigned currentDepth = this->getCurrentDepth();
|
||||
EntryInfo *currentEntryInfo = this->getEntryInfo();
|
||||
std::string currentEntryID = currentEntryInfo->getEntryID();
|
||||
bool currentEntryIsBuddyMirrored = currentEntryInfo->getIsBuddyMirrored();
|
||||
|
||||
const Path path(getPath());
|
||||
|
||||
// search
|
||||
while(currentDepth < searchDepth)
|
||||
{
|
||||
DirInode* currentDir = metaStore->referenceDir(currentEntryID, currentEntryIsBuddyMirrored,
|
||||
true);
|
||||
if(!currentDir)
|
||||
{ // someone said we own the dir with this ID, but we do not => it was deleted
|
||||
// note: this might also be caused by a change of ownership, but a feature like that is
|
||||
// currently not implemented
|
||||
return (findRes == FhgfsOpsErr_SUCCESS) ? FhgfsOpsErr_SUCCESS : FhgfsOpsErr_PATHNOTEXISTS;
|
||||
}
|
||||
|
||||
EntryInfo subInfo;
|
||||
auto [getEntryRes, isFileOpen] = metaStore->getEntryData(currentDir, path[currentDepth],
|
||||
&subInfo, NULL);
|
||||
|
||||
if (getEntryRes == FhgfsOpsErr_SUCCESS || getEntryRes == FhgfsOpsErr_DYNAMICATTRIBSOUTDATED)
|
||||
{ // next entry exists and is a dir or a file
|
||||
currentDepth++;
|
||||
|
||||
*outInfo = subInfo;
|
||||
outInfo->setEntryDepth(currentDepth);
|
||||
|
||||
findRes = FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
else
|
||||
{ // entry does not exist
|
||||
findRes = FhgfsOpsErr_PATHNOTEXISTS;
|
||||
}
|
||||
|
||||
metaStore->releaseDir(currentEntryID);
|
||||
|
||||
if(findRes != FhgfsOpsErr_SUCCESS)
|
||||
break;
|
||||
|
||||
// is next entry owned by a different node?
|
||||
if (subInfo.getIsBuddyMirrored())
|
||||
{
|
||||
if(subInfo.getOwnerNodeID()
|
||||
!= NumNodeID(app->getMetaBuddyGroupMapper()->getLocalGroupID() ) )
|
||||
break;
|
||||
}
|
||||
else
|
||||
if (subInfo.getOwnerNodeID() != app->getLocalNode().getNumID())
|
||||
break;
|
||||
|
||||
// prepare for next round
|
||||
currentEntryID = outInfo->getEntryID();
|
||||
}
|
||||
|
||||
return findRes;
|
||||
}
|
||||
22
meta/source/net/message/storage/lookup/FindOwnerMsgEx.h
Normal file
22
meta/source/net/message/storage/lookup/FindOwnerMsgEx.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/lookup/FindOwnerMsg.h>
|
||||
#include <common/storage/StorageDefinitions.h>
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/toolkit/MetadataTk.h>
|
||||
#include <common/Common.h>
|
||||
#include <storage/MetaStore.h>
|
||||
|
||||
// Get the meta-data server of a file
|
||||
|
||||
class FindOwnerMsgEx : public FindOwnerMsg
|
||||
{
|
||||
public:
|
||||
virtual bool processIncoming(ResponseContext& ctx);
|
||||
|
||||
private:
|
||||
FhgfsOpsErr findOwner(EntryInfoWithDepth* outInfo);
|
||||
|
||||
};
|
||||
|
||||
|
||||
550
meta/source/net/message/storage/lookup/LookupIntentMsgEx.cpp
Normal file
550
meta/source/net/message/storage/lookup/LookupIntentMsgEx.cpp
Normal file
@@ -0,0 +1,550 @@
|
||||
#include <common/net/message/control/GenericResponseMsg.h>
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/creating/MkFileMsg.h>
|
||||
#include <common/net/message/storage/creating/MkFileRespMsg.h>
|
||||
#include <common/net/message/storage/lookup/LookupIntentRespMsg.h>
|
||||
#include <common/storage/striping/Raid0Pattern.h>
|
||||
#include <common/toolkit/SessionTk.h>
|
||||
#include <components/FileEventLogger.h>
|
||||
#include <net/msghelpers/MsgHelperMkFile.h>
|
||||
#include <net/msghelpers/MsgHelperOpen.h>
|
||||
#include <net/msghelpers/MsgHelperStat.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <session/SessionStore.h>
|
||||
#include <storage/DentryStoreData.h>
|
||||
#include <common/storage/StoragePool.h>
|
||||
#include "LookupIntentMsgEx.h"
|
||||
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock> LookupIntentMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
ParentNameLock dentryLock;
|
||||
FileIDLock entryIDLockForDir;
|
||||
FileIDLock entryIDLockForFile;
|
||||
enum DirEntryType entryType = getEntryInfo()->getEntryType();
|
||||
|
||||
if (getIntentFlags() & LOOKUPINTENTMSG_FLAG_CREATE)
|
||||
{
|
||||
entryIDLockForDir = {&store, getParentInfo()->getEntryID(), true};
|
||||
dentryLock = {&store, getParentInfo()->getEntryID(), getEntryName()};
|
||||
entryIDLockForFile = {&store, entryID, true};
|
||||
}
|
||||
else
|
||||
{
|
||||
//XXX Note: If you are addding new flag for lookupintent, make sure to
|
||||
//check if shared lock for parent dir and file is sufficient.
|
||||
//Otherwise add another else if condition.
|
||||
// For other lookup flag, we don't have entryID yet. So rather than
|
||||
// taking lock on the emptry EntryID(which can serialize lookup on all
|
||||
// files even if from different parent directory), lookup the entryID
|
||||
// while holding parent lock. And take the lock on the actual entryID.
|
||||
entryIDLockForDir = {&store, getParentInfo()->getEntryID(), false};
|
||||
|
||||
lookupRes = lookup(getParentInfo()->getEntryID(), getEntryName(), isMirrored(),
|
||||
&diskEntryInfo, &inodeData, inodeDataOutdated);
|
||||
if (lookupRes == FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
entryID = diskEntryInfo.getEntryID();
|
||||
entryType = diskEntryInfo.getEntryType();
|
||||
}
|
||||
|
||||
// for revalidate entryInfo is valid, so use the info from it
|
||||
if (entryID.empty() && (getIntentFlags() & LOOKUPINTENTMSG_FLAG_REVALIDATE))
|
||||
{
|
||||
entryID = getEntryInfo()->getEntryID();
|
||||
entryType = getEntryInfo()->getEntryType();
|
||||
}
|
||||
|
||||
if (getIntentFlags() & LOOKUPINTENTMSG_FLAG_OPEN)
|
||||
{
|
||||
entryIDLockForFile = {&store, entryID, true};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DirEntryType_ISDIR(entryType) &&
|
||||
(entryID < getParentInfo()->getEntryID()))
|
||||
{
|
||||
// Release parent lock because child lock needs to be taken first.
|
||||
entryIDLockForDir = {};
|
||||
|
||||
// Take the lock in reverse order
|
||||
entryIDLockForFile = {&store, entryID, false};
|
||||
entryIDLockForDir = {&store, getParentInfo()->getEntryID(), false};
|
||||
}
|
||||
else
|
||||
{
|
||||
entryIDLockForFile = {&store, entryID, false};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_tuple(std::move(entryIDLockForDir), std::move(dentryLock),
|
||||
std::move(entryIDLockForFile));
|
||||
}
|
||||
|
||||
bool LookupIntentMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
|
||||
const std::string& parentEntryID = getParentInfo()->getEntryID();
|
||||
const std::string& entryName = getEntryName();
|
||||
|
||||
inodeDataOutdated = false; // true if the file/inode is currently open (referenced)
|
||||
|
||||
lookupRes = FhgfsOpsErr_INTERNAL;
|
||||
|
||||
LOG_DBG(GENERAL, DEBUG, "", parentEntryID, entryName, getParentInfo()->getIsBuddyMirrored());
|
||||
|
||||
// sanity checks
|
||||
if (unlikely(parentEntryID.empty() || entryName.empty()))
|
||||
{
|
||||
LOG(GENERAL, WARNING, "Sanity check failed", parentEntryID, entryName);
|
||||
|
||||
ctx.sendResponse(LookupIntentRespMsg(FhgfsOpsErr_INTERNAL));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getIntentFlags() & LOOKUPINTENTMSG_FLAG_CREATE)
|
||||
entryID = StorageTk::generateFileID(app->getLocalNode().getNumID());
|
||||
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> LookupIntentMsgEx::executeLocally(
|
||||
ResponseContext& ctx, bool isSecondary)
|
||||
{
|
||||
LookupIntentResponseState response;
|
||||
|
||||
int createFlag = getIntentFlags() & LOOKUPINTENTMSG_FLAG_CREATE;
|
||||
FhgfsOpsErr createRes = FhgfsOpsErr_INTERNAL;
|
||||
|
||||
// Note: Actually we should first do a lookup. However, a successful create also implies a
|
||||
// failed Lookup, so we can take a shortcut.
|
||||
|
||||
// lookup-create
|
||||
if (createFlag)
|
||||
{
|
||||
LOG_DBG(GENERAL, SPAM, "Create");
|
||||
|
||||
createRes = create(getParentInfo(), getEntryName(), &diskEntryInfo, &inodeData, isSecondary);
|
||||
switch (createRes)
|
||||
{
|
||||
case FhgfsOpsErr_SUCCESS:
|
||||
// Successful Create, which implies Lookup-before-create would have been failed.
|
||||
response.setLookupResult(FhgfsOpsErr_PATHNOTEXISTS);
|
||||
response.setEntryInfo(diskEntryInfo);
|
||||
response.addResponseCreate(createRes);
|
||||
break;
|
||||
|
||||
case FhgfsOpsErr_EXISTS:
|
||||
// NOTE: we need to do a Lookup to get required lookup data
|
||||
if (getIntentFlags() & LOOKUPINTENTMSG_FLAG_CREATEEXCLUSIVE)
|
||||
response.addResponseCreate(FhgfsOpsErr_EXISTS);
|
||||
else
|
||||
response.addResponseCreate(FhgfsOpsErr_SUCCESS);
|
||||
break;
|
||||
case FhgfsOpsErr_DQUOT:
|
||||
response.addResponseCreate(FhgfsOpsErr_DQUOT);
|
||||
break;
|
||||
|
||||
default:
|
||||
response.addResponseCreate(FhgfsOpsErr_INTERNAL);
|
||||
}
|
||||
|
||||
// note: don't quit here on error because caller might still have requested stat info
|
||||
}
|
||||
|
||||
// lookup
|
||||
if ((!createFlag) || (createRes == FhgfsOpsErr_EXISTS) )
|
||||
{
|
||||
LOG_DBG(GENERAL, SPAM, "Lookup");
|
||||
|
||||
// Lookup will happen for secondary node or create failed or lookup
|
||||
// failed earlier.
|
||||
if (isSecondary || (createRes == FhgfsOpsErr_EXISTS) ||
|
||||
lookupRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
// lock is not taken on secondary, so we need to perform lookup()
|
||||
// for secondary here.
|
||||
lookupRes = lookup(getParentInfo()->getEntryID(), getEntryName(), isMirrored(),
|
||||
&diskEntryInfo, &inodeData, inodeDataOutdated);
|
||||
|
||||
}
|
||||
// Lookup was already done in lock() function after taking lock on parent.
|
||||
response.setLookupResult(lookupRes);
|
||||
response.setEntryInfo(diskEntryInfo);
|
||||
|
||||
if (unlikely(lookupRes != FhgfsOpsErr_SUCCESS && createFlag))
|
||||
{
|
||||
// so createFlag is set, so createRes is either Success or Exists, but now lookup fails
|
||||
// create/unlink race?
|
||||
|
||||
StatData statData;
|
||||
statData.setAllFake(); // set arbitrary stat values (receiver won't use the values)
|
||||
|
||||
response.addResponseStat(lookupRes, statData);
|
||||
|
||||
return boost::make_unique<ResponseState>(std::move(response));
|
||||
}
|
||||
}
|
||||
|
||||
// lookup-revalidate
|
||||
if (getIntentFlags() & LOOKUPINTENTMSG_FLAG_REVALIDATE)
|
||||
{
|
||||
LOG_DBG(GENERAL, SPAM, "Revalidate");
|
||||
|
||||
auto revalidateRes = revalidate(&diskEntryInfo, inodeData.getMetaVersion());
|
||||
response.addResponseRevalidate(revalidateRes);
|
||||
|
||||
if (revalidateRes != FhgfsOpsErr_SUCCESS)
|
||||
return boost::make_unique<ResponseState>(std::move(response));
|
||||
}
|
||||
|
||||
// lookup-stat
|
||||
// note: we do stat before open to avoid the dyn attribs refresh if the file is not opened by
|
||||
// someone else currently.
|
||||
if ((getIntentFlags() & LOOKUPINTENTMSG_FLAG_STAT) &&
|
||||
(lookupRes == FhgfsOpsErr_SUCCESS || createRes == FhgfsOpsErr_SUCCESS))
|
||||
{
|
||||
LOG_DBG(GENERAL, SPAM, "Stat");
|
||||
|
||||
// check if lookup and create failed (we don't have an entryID to stat then)
|
||||
if (diskEntryInfo.getEntryID().empty())
|
||||
return boost::make_unique<ResponseState>(std::move(response));
|
||||
|
||||
if ((diskEntryInfo.getFeatureFlags() & ENTRYINFO_FEATURE_INLINED) && !inodeDataOutdated)
|
||||
{
|
||||
// For inlined inodes with up-to-date inode data, use stat data fetched during lookup()
|
||||
response.addResponseStat(FhgfsOpsErr_SUCCESS, *inodeData.getInodeStatData());
|
||||
}
|
||||
else
|
||||
{ // read stat data separately
|
||||
StatData statData;
|
||||
FhgfsOpsErr statRes = stat(&diskEntryInfo, true, statData);
|
||||
|
||||
response.addResponseStat(statRes, statData);
|
||||
|
||||
if (statRes != FhgfsOpsErr_SUCCESS)
|
||||
return boost::make_unique<ResponseState>(std::move(response));
|
||||
}
|
||||
}
|
||||
|
||||
// lookup-open
|
||||
if(getIntentFlags() & LOOKUPINTENTMSG_FLAG_OPEN)
|
||||
{
|
||||
LOG_DBG(GENERAL, SPAM, "Open");
|
||||
|
||||
std::string fileHandleID;
|
||||
PathInfo pathInfo;
|
||||
Raid0Pattern dummyPattern(1, UInt16Vector() );
|
||||
|
||||
// don't open if create failed
|
||||
if ((createRes != FhgfsOpsErr_SUCCESS) && (getIntentFlags() & LOOKUPINTENTMSG_FLAG_CREATE))
|
||||
return boost::make_unique<ResponseState>(std::move(response));
|
||||
|
||||
// if it's not a regular file, we don't open that
|
||||
if (!DirEntryType_ISREGULARFILE(diskEntryInfo.getEntryType()))
|
||||
return boost::make_unique<ResponseState>(std::move(response));
|
||||
|
||||
// check if lookup and/or create failed (we don't have an entryID to open then)
|
||||
if (diskEntryInfo.getEntryID().empty())
|
||||
return boost::make_unique<ResponseState>(std::move(response));
|
||||
|
||||
StripePattern* pattern = NULL;
|
||||
|
||||
FhgfsOpsErr openRes = open(&diskEntryInfo, &fileHandleID, &pattern, &pathInfo, isSecondary);
|
||||
|
||||
if(openRes != FhgfsOpsErr_SUCCESS)
|
||||
{ // open failed => use dummy pattern for response
|
||||
response.addResponseOpen(openRes, std::move(fileHandleID),
|
||||
std::unique_ptr<StripePattern>(dummyPattern.clone()), pathInfo);
|
||||
|
||||
return boost::make_unique<ResponseState>(std::move(response));
|
||||
}
|
||||
|
||||
response.addResponseOpen(openRes, std::move(fileHandleID),
|
||||
std::unique_ptr<StripePattern>(pattern->clone()), pathInfo);
|
||||
}
|
||||
|
||||
updateNodeOp(ctx, getOpCounterType());
|
||||
|
||||
return boost::make_unique<ResponseState>(std::move(response));
|
||||
}
|
||||
|
||||
FhgfsOpsErr LookupIntentMsgEx::lookup(const std::string& parentEntryID,
|
||||
const std::string& entryName, bool isBuddyMirrored, EntryInfo* outEntryInfo,
|
||||
FileInodeStoreData* outInodeStoreData, bool& outInodeDataOutdated)
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
DirInode* parentDir = metaStore->referenceDir(parentEntryID, isBuddyMirrored, false);
|
||||
if (unlikely(!parentDir))
|
||||
{ // out of memory
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
auto [lookupRes1, isFileOpen] = metaStore->getEntryData(parentDir, entryName, outEntryInfo,
|
||||
outInodeStoreData);
|
||||
|
||||
if (lookupRes1 == FhgfsOpsErr_DYNAMICATTRIBSOUTDATED)
|
||||
{
|
||||
lookupRes1 = FhgfsOpsErr_SUCCESS;
|
||||
outInodeDataOutdated = isFileOpen;
|
||||
|
||||
// If the file inode is not inlined and the intent includes the creation flag,
|
||||
// we need to use a different overload of getEntryData() to correctly retrieve the
|
||||
// inode disk data for non-inlined inode(s) and prevent potential crashes due to
|
||||
// race conditions between create and hardlink creation.
|
||||
// If create flag is set, fetch full inode disk data using non-inlined inode.
|
||||
int createFlag = getIntentFlags() & LOOKUPINTENTMSG_FLAG_CREATE;
|
||||
if (!outEntryInfo->getIsInlined() && createFlag)
|
||||
{
|
||||
FhgfsOpsErr getRes = metaStore->getEntryData(outEntryInfo, outInodeStoreData);
|
||||
if (getRes != FhgfsOpsErr_SUCCESS)
|
||||
lookupRes1 = getRes;
|
||||
}
|
||||
}
|
||||
|
||||
metaStore->releaseDir(parentEntryID);
|
||||
return lookupRes1;
|
||||
}
|
||||
|
||||
/**
|
||||
* compare entryInfo on disk with EntryInfo send by the client
|
||||
* @return FhgfsOpsErr_SUCCESS if revalidation successful, FhgfsOpsErr_PATHNOTEXISTS otherwise, FhgfsOpsErr_METAVERSIONMISMATCH in case of version change.
|
||||
*/
|
||||
FhgfsOpsErr LookupIntentMsgEx::revalidate(EntryInfo* diskEntryInfo, uint32_t metaVersion)
|
||||
{
|
||||
EntryInfo* clientEntryInfo = this->getEntryInfo();
|
||||
uint32_t clientMetaVersion = this->getMetaVersion();
|
||||
|
||||
|
||||
if ( (diskEntryInfo->getEntryID() == clientEntryInfo->getEntryID() ) &&
|
||||
(diskEntryInfo->getOwnerNodeID() == clientEntryInfo->getOwnerNodeID() ) )
|
||||
{
|
||||
if (clientMetaVersion != metaVersion)
|
||||
return FhgfsOpsErr_METAVERSIONMISMATCH;
|
||||
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
return FhgfsOpsErr_PATHNOTEXISTS;
|
||||
}
|
||||
|
||||
FhgfsOpsErr LookupIntentMsgEx::create(EntryInfo* parentInfo, const std::string& entryName,
|
||||
EntryInfo *outEntryInfo, FileInodeStoreData* outInodeData, bool isSecondary)
|
||||
{
|
||||
MkFileDetails mkDetails(entryName, getUserID(), getGroupID(), getMode(), getUmask(),
|
||||
TimeAbs().getTimeval()->tv_sec);
|
||||
StripePattern* pattern = nullptr;
|
||||
std::unique_ptr<RemoteStorageTarget> rstInfo;
|
||||
FhgfsOpsErr res;
|
||||
|
||||
DirInode* dir = Program::getApp()->getMetaStore()->referenceDir(
|
||||
parentInfo->getEntryID(), parentInfo->getIsBuddyMirrored(), true);
|
||||
if (!dir)
|
||||
return FhgfsOpsErr_PATHNOTEXISTS;
|
||||
|
||||
// create new RST object and set remote storage target info from parentDir if available
|
||||
if (dir->getIsRstAvailable())
|
||||
{
|
||||
rstInfo = std::make_unique<RemoteStorageTarget>();
|
||||
rstInfo->set(dir->getRemoteStorageTargetInfo());
|
||||
}
|
||||
|
||||
if (isSecondary)
|
||||
{
|
||||
mkDetails.setNewEntryID(getNewEntryID().c_str());
|
||||
mkDetails.createTime = fileTimestamps.creationTimeSecs;
|
||||
pattern = getNewStripePattern()->clone();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!entryID.empty())
|
||||
mkDetails.setNewEntryID(entryID.c_str());
|
||||
|
||||
pattern = dir->createFileStripePattern(&getPreferredTargets(), 0, 0, StoragePoolId(0));
|
||||
}
|
||||
|
||||
if (!pattern)
|
||||
{
|
||||
LogContext("Lookup Create").logErr(
|
||||
"StripePattern is NULL. Can't proceed. Filename: " + getEntryName());
|
||||
|
||||
res = FhgfsOpsErr_INTERNAL;
|
||||
goto releasedir_and_return;
|
||||
}
|
||||
|
||||
if (isMsgHeaderFeatureFlagSet(LOOKUPINTENTMSG_FLAG_USE_QUOTA) &&
|
||||
Program::getApp()->getConfig()->getQuotaEnableEnforcement())
|
||||
{
|
||||
const char* logContext = "Quota Enforcement for create";
|
||||
StoragePoolPtr storagePool =
|
||||
Program::getApp()->getStoragePoolStore()->getPool(pattern->getStoragePoolId());
|
||||
|
||||
if (!storagePool)
|
||||
{
|
||||
LOG(QUOTA, WARNING, "Requested storage pool doesn't exist on metadata server.",
|
||||
("StoragePoolId", pattern->getStoragePoolId()));
|
||||
|
||||
res = FhgfsOpsErr_UNKNOWNPOOL;
|
||||
goto releasedir_and_return;
|
||||
}
|
||||
|
||||
UInt16Set targetIds = storagePool->getTargets();
|
||||
for (auto targetId : targetIds)
|
||||
{
|
||||
ExceededQuotaStorePtr exceededQuotaStore = Program::getApp()->getExceededQuotaStores()->get(targetId);
|
||||
if (exceededQuotaStore && exceededQuotaStore->someQuotaExceeded())
|
||||
{
|
||||
QuotaExceededErrorType quotaExceeded = exceededQuotaStore->isQuotaExceeded(mkDetails.userID, mkDetails.groupID);
|
||||
if (quotaExceeded != QuotaExceededErrorType_NOT_EXCEEDED)
|
||||
{
|
||||
LogContext(logContext).log(Log_NOTICE,
|
||||
QuotaData::QuotaExceededErrorTypeToString(quotaExceeded) + " "
|
||||
"UID: " + StringTk::uintToStr(mkDetails.userID) + "; "
|
||||
"GID: " + StringTk::uintToStr(mkDetails.groupID));
|
||||
res = FhgfsOpsErr_DQUOT;
|
||||
goto releasedir_and_return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res = MsgHelperMkFile::mkFile(*dir, &mkDetails, &getPreferredTargets(), 0, 0,
|
||||
pattern, rstInfo.get(), outEntryInfo, outInodeData);
|
||||
|
||||
if (res == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
{
|
||||
fixInodeTimestamp(*dir, dirTimestamps);
|
||||
|
||||
if (!isSecondary)
|
||||
fileTimestamps = outInodeData->getInodeStatData()->getMirroredTimestamps();
|
||||
}
|
||||
|
||||
if (!isSecondary && res == FhgfsOpsErr_SUCCESS && Program::getApp()->getFileEventLogger()
|
||||
&& getFileEvent())
|
||||
{
|
||||
EventContext eventCtx = makeEventContext(outEntryInfo, outEntryInfo->getParentEntryID(),
|
||||
getMsgHeaderUserID(), "", outInodeData->getInodeStatData()->getNumHardlinks(), isSecondary);
|
||||
logEvent(Program::getApp()->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
|
||||
releasedir_and_return:
|
||||
Program::getApp()->getMetaStore()->releaseDir(dir->getID());
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
FhgfsOpsErr LookupIntentMsgEx::stat(EntryInfo* entryInfo, bool loadFromDisk, StatData& outStatData)
|
||||
{
|
||||
Node& localNode = Program::getApp()->getLocalNode();
|
||||
MirrorBuddyGroupMapper* metaBuddyGroupMapper = Program::getApp()->getMetaBuddyGroupMapper();
|
||||
|
||||
FhgfsOpsErr statRes = FhgfsOpsErr_NOTOWNER;
|
||||
|
||||
// check if we can stat on this machine or if entry is owned by another server
|
||||
NumNodeID expectedOwner;
|
||||
if (entryInfo->getIsBuddyMirrored())
|
||||
expectedOwner = NumNodeID(metaBuddyGroupMapper->getLocalGroupID());
|
||||
else
|
||||
expectedOwner = localNode.getNumID();
|
||||
|
||||
if(entryInfo->getOwnerNodeID() == expectedOwner)
|
||||
statRes = MsgHelperStat::stat(entryInfo, loadFromDisk, getMsgHeaderUserID(), outStatData);
|
||||
|
||||
return statRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param outPattern only set if success is returned; points to referenced open file, so it does
|
||||
* not need to be free'd/deleted.
|
||||
*/
|
||||
FhgfsOpsErr LookupIntentMsgEx::open(EntryInfo* entryInfo, std::string* outFileHandleID,
|
||||
StripePattern** outPattern, PathInfo* outPathInfo, bool isSecondary)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
SessionStore* sessions = entryInfo->getIsBuddyMirrored()
|
||||
? app->getMirroredSessions()
|
||||
: app->getSessions();
|
||||
|
||||
MetaFileHandle inode;
|
||||
|
||||
bool useQuota = isMsgHeaderFeatureFlagSet(LOOKUPINTENTMSG_FLAG_USE_QUOTA);
|
||||
|
||||
FhgfsOpsErr openRes = MsgHelperOpen::openFile(
|
||||
entryInfo, getAccessFlags(), useQuota, /* bypassFileAccessCheck */ false,
|
||||
getMsgHeaderUserID(), inode, isSecondary);
|
||||
|
||||
if (openRes == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
fixInodeTimestamp(*inode, fileTimestamps, entryInfo);
|
||||
|
||||
if(openRes != FhgfsOpsErr_SUCCESS)
|
||||
return openRes; // error occurred
|
||||
|
||||
// open successful => insert session
|
||||
|
||||
SessionFile* sessionFile = new SessionFile(std::move(inode), getAccessFlags(), entryInfo);
|
||||
|
||||
Session* session = sessions->referenceSession(getSessionID(), true);
|
||||
|
||||
*outPattern = sessionFile->getInode()->getStripePattern();
|
||||
sessionFile->getInode()->getPathInfo(outPathInfo);
|
||||
|
||||
unsigned sessionFileID;
|
||||
|
||||
if (!hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
|
||||
{
|
||||
sessionFileID = session->getFiles()->addSession(sessionFile);
|
||||
newOwnerFD = sessionFileID;
|
||||
}
|
||||
else
|
||||
{
|
||||
sessionFileID = newOwnerFD;
|
||||
bool addRes = session->getFiles()->addSession(sessionFile, sessionFileID);
|
||||
|
||||
if (!addRes)
|
||||
LOG(GENERAL, NOTICE, "Couldn't add sessionFile on secondary",
|
||||
("sessionID", this->getSessionID()), sessionFileID);
|
||||
}
|
||||
|
||||
sessions->releaseSession(session);
|
||||
|
||||
*outFileHandleID = SessionTk::generateFileHandleID(sessionFileID, entryInfo->getEntryID() );
|
||||
|
||||
return openRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide which type of client stats op counter we increase for this msg (based on given msg flags).
|
||||
*/
|
||||
MetaOpCounterTypes LookupIntentMsgEx::getOpCounterType()
|
||||
{
|
||||
/* note: as introducting a speparate opCounter type for each flag would have been too much,
|
||||
we assign prioritiess here as follows: create > open > revalidate > stat > simple_no_flags */
|
||||
|
||||
/* NOTE: Those if's are rather slow, maybe we should create a table that has the values?
|
||||
* Problem is that the table has to be filled for flag combinations, which is also ugly
|
||||
*/
|
||||
|
||||
if(this->getIntentFlags() & LOOKUPINTENTMSG_FLAG_CREATE)
|
||||
return MetaOpCounter_LOOKUPINTENT_CREATE;
|
||||
|
||||
if(this->getIntentFlags() & LOOKUPINTENTMSG_FLAG_OPEN)
|
||||
return MetaOpCounter_LOOKUPINTENT_OPEN;
|
||||
|
||||
if(this->getIntentFlags() & LOOKUPINTENTMSG_FLAG_REVALIDATE)
|
||||
return MetaOpCounter_LOOKUPINTENT_REVALIDATE;
|
||||
|
||||
if(this->getIntentFlags() & LOOKUPINTENTMSG_FLAG_STAT)
|
||||
return MetaOpCounter_LOOKUPINTENT_STAT;
|
||||
|
||||
return MetaOpCounter_LOOKUPINTENT_SIMPLE;
|
||||
|
||||
}
|
||||
|
||||
void LookupIntentMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
addBuddyInfo(entryID, inodeData.getStripePattern());
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_LookupIntentResp);
|
||||
}
|
||||
222
meta/source/net/message/storage/lookup/LookupIntentMsgEx.h
Normal file
222
meta/source/net/message/storage/lookup/LookupIntentMsgEx.h
Normal file
@@ -0,0 +1,222 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/lookup/LookupIntentMsg.h>
|
||||
#include <common/net/message/storage/lookup/LookupIntentRespMsg.h>
|
||||
#include <common/nodes/OpCounterTypes.h>
|
||||
#include <common/storage/StorageDefinitions.h>
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/toolkit/MetadataTk.h>
|
||||
#include <common/Common.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
class LookupIntentResponseState : public MirroredMessageResponseState
|
||||
{
|
||||
public:
|
||||
LookupIntentResponseState() :
|
||||
responseFlags(0), lookupResult(FhgfsOpsErr_INTERNAL), statResult(FhgfsOpsErr_INTERNAL),
|
||||
revalidateResult(FhgfsOpsErr_INTERNAL), createResult(FhgfsOpsErr_INTERNAL),
|
||||
openResult(FhgfsOpsErr_INTERNAL)
|
||||
{}
|
||||
|
||||
explicit LookupIntentResponseState(Deserializer& des)
|
||||
{
|
||||
serialize(this, des);
|
||||
}
|
||||
|
||||
LookupIntentResponseState(LookupIntentResponseState&& other) :
|
||||
responseFlags(other.responseFlags),
|
||||
lookupResult(other.lookupResult),
|
||||
statResult(other.statResult),
|
||||
statData(other.statData),
|
||||
revalidateResult(other.revalidateResult),
|
||||
createResult(other.createResult),
|
||||
openResult(other.openResult),
|
||||
fileHandleID(std::move(other.fileHandleID)),
|
||||
entryInfo(std::move(other.entryInfo)),
|
||||
pattern(std::move(other.pattern)),
|
||||
pathInfo(std::move(other.pathInfo))
|
||||
{}
|
||||
|
||||
void sendResponse(NetMessage::ResponseContext& ctx) override
|
||||
{
|
||||
LookupIntentRespMsg resp(lookupResult);
|
||||
|
||||
if (responseFlags & LOOKUPINTENTRESPMSG_FLAG_STAT)
|
||||
resp.addResponseStat(statResult, &statData);
|
||||
|
||||
if (responseFlags & LOOKUPINTENTRESPMSG_FLAG_REVALIDATE)
|
||||
resp.addResponseRevalidate(revalidateResult);
|
||||
|
||||
if (responseFlags & LOOKUPINTENTRESPMSG_FLAG_CREATE)
|
||||
resp.addResponseCreate(createResult);
|
||||
|
||||
if (responseFlags & LOOKUPINTENTRESPMSG_FLAG_OPEN)
|
||||
resp.addResponseOpen(openResult, fileHandleID, pattern.get(), &pathInfo);
|
||||
|
||||
resp.setEntryInfo(&entryInfo);
|
||||
|
||||
ctx.sendResponse(resp);
|
||||
}
|
||||
|
||||
bool changesObservableState() const override
|
||||
{
|
||||
if ((responseFlags & LOOKUPINTENTRESPMSG_FLAG_CREATE)
|
||||
&& createResult == FhgfsOpsErr_SUCCESS)
|
||||
return true;
|
||||
if ((responseFlags & LOOKUPINTENTRESPMSG_FLAG_OPEN) && openResult == FhgfsOpsErr_SUCCESS)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t serializerTag() const override { return NETMSGTYPE_LookupIntent; }
|
||||
|
||||
template<typename This, typename Ctx>
|
||||
static void serialize(This* obj, Ctx& ctx)
|
||||
{
|
||||
ctx
|
||||
% obj->responseFlags
|
||||
% serdes::as<int32_t>(obj->lookupResult);
|
||||
|
||||
if (obj->responseFlags & LOOKUPINTENTRESPMSG_FLAG_STAT)
|
||||
ctx
|
||||
% obj->statData.serializeAs(StatDataFormat_NET)
|
||||
% serdes::as<int32_t>(obj->statResult);
|
||||
|
||||
ctx
|
||||
% serdes::as<int32_t>(obj->revalidateResult)
|
||||
% serdes::as<int32_t>(obj->createResult);
|
||||
|
||||
if (obj->responseFlags & LOOKUPINTENTRESPMSG_FLAG_OPEN)
|
||||
ctx
|
||||
% serdes::as<int32_t>(obj->openResult)
|
||||
% obj->fileHandleID
|
||||
% obj->entryInfo
|
||||
% obj->pattern
|
||||
% obj->pathInfo;
|
||||
}
|
||||
|
||||
void serializeContents(Serializer& ser) const override
|
||||
{
|
||||
serialize(this, ser);
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t responseFlags;
|
||||
|
||||
FhgfsOpsErr lookupResult;
|
||||
|
||||
FhgfsOpsErr statResult;
|
||||
StatData statData;
|
||||
|
||||
FhgfsOpsErr revalidateResult;
|
||||
|
||||
FhgfsOpsErr createResult;
|
||||
|
||||
FhgfsOpsErr openResult;
|
||||
std::string fileHandleID;
|
||||
|
||||
EntryInfo entryInfo;
|
||||
std::unique_ptr<StripePattern> pattern;
|
||||
PathInfo pathInfo;
|
||||
|
||||
public:
|
||||
void setLookupResult(FhgfsOpsErr lookupRes) { lookupResult = lookupRes; }
|
||||
|
||||
void addResponseRevalidate(FhgfsOpsErr revalidateResult)
|
||||
{
|
||||
responseFlags |= LOOKUPINTENTRESPMSG_FLAG_REVALIDATE;
|
||||
this->revalidateResult = revalidateResult;
|
||||
}
|
||||
|
||||
void addResponseCreate(FhgfsOpsErr createResult)
|
||||
{
|
||||
responseFlags |= LOOKUPINTENTRESPMSG_FLAG_CREATE;
|
||||
this->createResult = createResult;
|
||||
}
|
||||
|
||||
void addResponseOpen(FhgfsOpsErr openResult, std::string fileHandleID,
|
||||
std::unique_ptr<StripePattern> pattern, const PathInfo& pathInfo)
|
||||
{
|
||||
responseFlags |= LOOKUPINTENTRESPMSG_FLAG_OPEN;
|
||||
this->openResult = openResult;
|
||||
this->fileHandleID = std::move(fileHandleID);
|
||||
this->pattern = std::move(pattern);
|
||||
this->pathInfo = pathInfo;
|
||||
}
|
||||
|
||||
void addResponseStat(FhgfsOpsErr statResult, const StatData& statData)
|
||||
{
|
||||
responseFlags |= LOOKUPINTENTRESPMSG_FLAG_STAT;
|
||||
this->statResult = statResult;
|
||||
this->statData = statData;
|
||||
}
|
||||
|
||||
void setEntryInfo(EntryInfo value) { entryInfo = std::move(value); }
|
||||
};
|
||||
|
||||
/**
|
||||
* This combines the normal lookup of a directory entry with intents, i.e. options to create,
|
||||
* open and stat the entry in a single message.
|
||||
*
|
||||
* Note: The intent options currently work only for files.
|
||||
*/
|
||||
class LookupIntentMsgEx : public MirroredMessage<LookupIntentMsg,
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef LookupIntentResponseState ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock> lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getParentInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
const char* mirrorLogContext() const override { return "LookupIntentMsgEx/forward"; }
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
auto& respMsg = static_cast<LookupIntentRespMsg&>(resp);
|
||||
|
||||
// since we only forward for open and create, we need only check those two.
|
||||
if ((respMsg.getResponseFlags() & LOOKUPINTENTRESPMSG_FLAG_CREATE)
|
||||
&& respMsg.getCreateResult() != FhgfsOpsErr_SUCCESS)
|
||||
return respMsg.getCreateResult();
|
||||
|
||||
if ((respMsg.getResponseFlags() & LOOKUPINTENTRESPMSG_FLAG_OPEN)
|
||||
&& respMsg.getOpenResult() != FhgfsOpsErr_SUCCESS)
|
||||
return respMsg.getOpenResult();
|
||||
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
private:
|
||||
FhgfsOpsErr lookup(const std::string& parentEntryID, const std::string& entryName,
|
||||
bool isBuddyMirrored, EntryInfo* outEntryInfo, FileInodeStoreData* outInodeStoreData,
|
||||
bool& outInodeDataOutdated);
|
||||
FhgfsOpsErr revalidate(EntryInfo* diskEntryInfo, uint32_t metaVersion);
|
||||
FhgfsOpsErr create(EntryInfo* parentInfo, const std::string& entryName,
|
||||
EntryInfo* outEntryInfo, FileInodeStoreData* outInodeData, bool isSecondary);
|
||||
FhgfsOpsErr stat(EntryInfo* entryInfo, bool loadFromDisk, StatData& outStatData);
|
||||
FhgfsOpsErr open(EntryInfo* entryInfo, std::string* outFileHandleID,
|
||||
StripePattern** outPattern, PathInfo* outPathInfo, bool isSecondary);
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
MetaOpCounterTypes getOpCounterType();
|
||||
|
||||
FileInodeStoreData inodeData;
|
||||
std::string entryID;
|
||||
FhgfsOpsErr lookupRes;
|
||||
bool inodeDataOutdated;
|
||||
EntryInfo diskEntryInfo;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "GetMetaResyncStatsMsgEx.h"
|
||||
|
||||
#include <common/net/message/storage/mirroring/GetMetaResyncStatsRespMsg.h>
|
||||
#include <components/buddyresyncer/BuddyResyncer.h>
|
||||
#include <program/Program.h>
|
||||
|
||||
bool GetMetaResyncStatsMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
BuddyResyncer* resyncer = Program::getApp()->getBuddyResyncer();
|
||||
|
||||
MetaBuddyResyncJobStatistics stats;
|
||||
|
||||
BuddyResyncJob* job = resyncer->getResyncJob();
|
||||
if (job)
|
||||
stats = job->getJobStats();
|
||||
|
||||
ctx.sendResponse(GetMetaResyncStatsRespMsg(&stats));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/mirroring/GetMetaResyncStatsMsg.h>
|
||||
|
||||
class GetMetaResyncStatsMsgEx : public GetMetaResyncStatsMsg
|
||||
{
|
||||
public:
|
||||
virtual bool processIncoming(ResponseContext& ctx);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
#include "ResyncRawInodesMsgEx.h"
|
||||
|
||||
#include <common/app/log/Logger.h>
|
||||
#include <common/net/message/storage/mirroring/ResyncRawInodesRespMsg.h>
|
||||
#include <app/App.h>
|
||||
#include <components/buddyresyncer/SyncCandidate.h>
|
||||
#include <net/message/storage/mirroring/SetMetadataMirroringMsgEx.h>
|
||||
#include <net/msghelpers/MsgHelperXAttr.h>
|
||||
#include <program/Program.h>
|
||||
#include <toolkit/XAttrTk.h>
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
bool ResyncRawInodesMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
LOG_DBG(MIRRORING, DEBUG, "", basePath, hasXAttrs, wholeDirectory);
|
||||
|
||||
const FhgfsOpsErr resyncRes = resyncStream(ctx);
|
||||
|
||||
ctx.sendResponse(ResyncRawInodesRespMsg(resyncRes));
|
||||
return resyncRes == FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
FhgfsOpsErr ResyncRawInodesMsgEx::resyncStream(ResponseContext& ctx)
|
||||
{
|
||||
if (hasXAttrs && !Program::getApp()->getConfig()->getStoreClientXAttrs())
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Primary has indicated xattr resync, but xattrs are disabled in config.");
|
||||
return FhgfsOpsErr_NOTSUPP;
|
||||
}
|
||||
|
||||
const auto& rootInfo = Program::getApp()->getMetaRoot();
|
||||
auto* const metaBGM = Program::getApp()->getMetaBuddyGroupMapper();
|
||||
auto* const rootDir = Program::getApp()->getRootDir();
|
||||
|
||||
// if the local root is not buddyMirrored yet, set local buddy mirroring for the root inode.
|
||||
if (rootInfo.getID().val() == metaBGM->getLocalGroupID() &&
|
||||
!rootDir->getIsBuddyMirrored())
|
||||
{
|
||||
const auto setMirrorRes = SetMetadataMirroringMsgEx::setMirroring();
|
||||
if (setMirrorRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Failed to set meta mirroring on the root directory", setMirrorRes);
|
||||
return setMirrorRes;
|
||||
}
|
||||
}
|
||||
|
||||
// if our path is a directory, we must create it now, otherwise, the directory may not be
|
||||
// created at all. for example the #fSiDs# directory in an empty content directory with no
|
||||
// orphaned fsids would not be created.
|
||||
if (wholeDirectory)
|
||||
{
|
||||
const auto mkRes = Program::getApp()->getMetaStore()->beginResyncFor(
|
||||
META_BUDDYMIRROR_SUBDIR_NAME / basePath, true);
|
||||
if (mkRes.first != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Failed to create metadata directory.", basePath,
|
||||
("mkRes", mkRes.first));
|
||||
return mkRes.first;
|
||||
}
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
const auto resyncPartRes = resyncSingle(ctx);
|
||||
if (resyncPartRes == FhgfsOpsErr_AGAIN)
|
||||
continue;
|
||||
else if (resyncPartRes == FhgfsOpsErr_SUCCESS)
|
||||
break;
|
||||
|
||||
return resyncPartRes;
|
||||
}
|
||||
|
||||
const FhgfsOpsErr result = wholeDirectory
|
||||
? removeUntouchedInodes()
|
||||
: FhgfsOpsErr_SUCCESS;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
FhgfsOpsErr ResyncRawInodesMsgEx::resyncSingle(ResponseContext& ctx)
|
||||
{
|
||||
uint32_t packetLength;
|
||||
|
||||
ctx.getSocket()->recvExact(&packetLength, sizeof(packetLength), 0);
|
||||
packetLength = LE_TO_HOST_32(packetLength);
|
||||
|
||||
if (packetLength == 0)
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
|
||||
std::unique_ptr<char[]> packetData(new char[packetLength]);
|
||||
|
||||
ctx.getSocket()->recvExact(packetData.get(), packetLength, 0);
|
||||
|
||||
Deserializer des(packetData.get(), packetLength);
|
||||
|
||||
MetaSyncFileType packetType;
|
||||
std::string relPath;
|
||||
|
||||
des
|
||||
% packetType
|
||||
% relPath;
|
||||
if (!des.good())
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Received bad data from primary.");
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
if (wholeDirectory)
|
||||
inodesWritten.push_back(relPath);
|
||||
|
||||
FhgfsOpsErr result;
|
||||
|
||||
switch (packetType)
|
||||
{
|
||||
case MetaSyncFileType::Inode:
|
||||
case MetaSyncFileType::Directory:
|
||||
result = resyncInode(ctx, basePath / relPath, des,
|
||||
packetType == MetaSyncFileType::Directory);
|
||||
break;
|
||||
|
||||
case MetaSyncFileType::Dentry:
|
||||
result = resyncDentry(ctx, basePath / relPath, des);
|
||||
break;
|
||||
|
||||
default:
|
||||
result = FhgfsOpsErr_INVAL;
|
||||
}
|
||||
|
||||
ctx.sendResponse(ResyncRawInodesRespMsg(result));
|
||||
|
||||
// if the resync has failed, we have to return the result twice - once as an ACK for the packet,
|
||||
// and another time to terminate the stream. mod sync could do without the termination, but
|
||||
// bulk resync can't.
|
||||
if (result == FhgfsOpsErr_SUCCESS)
|
||||
return FhgfsOpsErr_AGAIN;
|
||||
else
|
||||
return result;
|
||||
}
|
||||
|
||||
FhgfsOpsErr ResyncRawInodesMsgEx::resyncInode(ResponseContext& ctx, const Path& path,
|
||||
Deserializer& data, const bool isDirectory, const bool recvXAttrs)
|
||||
{
|
||||
std::map<std::string, std::vector<char>> content;
|
||||
bool isDeletion;
|
||||
|
||||
// Decide how to correctly deserialize incoming data based on 'recvXAttrs' flag:
|
||||
//
|
||||
// Note: After switching data structure used to encode/serialize inode data from std::vector to
|
||||
// std::map to accomodate new xattr introduced that stores Remote Storage Targets (RST) info in
|
||||
// inodes, we need to handle both formats (See PR#3905 for more details):
|
||||
// - true (default): Represents inode data with base meta xattr (META_XATTR_NAME) plus any
|
||||
// user-defined xattrs as key-value pairs in map format.
|
||||
// - false: Represents standalone dentry data still using the original vector format for
|
||||
// compatibility, deserialized as single value.
|
||||
if (!recvXAttrs)
|
||||
{
|
||||
content.try_emplace(META_XATTR_NAME);
|
||||
data
|
||||
% content[META_XATTR_NAME]
|
||||
% isDeletion;
|
||||
}
|
||||
else
|
||||
{
|
||||
data
|
||||
% content
|
||||
% isDeletion;
|
||||
}
|
||||
|
||||
if (!data.good())
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Received bad data from primary.");
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
if (isDeletion)
|
||||
{
|
||||
const bool rmRes = isDirectory
|
||||
? StorageTk::removeDirRecursive((META_BUDDYMIRROR_SUBDIR_NAME / path).str())
|
||||
: unlink((META_BUDDYMIRROR_SUBDIR_NAME / path).str().c_str()) == 0;
|
||||
|
||||
if (rmRes || errno == ENOENT)
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
|
||||
LOG(MIRRORING, ERR, "Failed to remove raw meta inode.", path, sysErr);
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
if (!isDirectory && wholeDirectory)
|
||||
{
|
||||
const auto unlinkRes = Program::getApp()->getMetaStore()->unlinkRawMetadata(
|
||||
META_BUDDYMIRROR_SUBDIR_NAME / path);
|
||||
if (unlinkRes != FhgfsOpsErr_SUCCESS && unlinkRes != FhgfsOpsErr_PATHNOTEXISTS)
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Could not unlink raw metadata", path, unlinkRes);
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
auto inode = Program::getApp()->getMetaStore()->beginResyncFor(
|
||||
META_BUDDYMIRROR_SUBDIR_NAME / path, isDirectory);
|
||||
if (inode.first)
|
||||
return inode.first;
|
||||
|
||||
if (!isDirectory)
|
||||
{
|
||||
for (const auto& attr : content)
|
||||
{
|
||||
const auto setContentRes = inode.second.setContent(
|
||||
attr.first.c_str(), attr.second.data(), attr.second.size());
|
||||
|
||||
if (setContentRes)
|
||||
return setContentRes;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasXAttrs || !recvXAttrs)
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
|
||||
const auto xattrRes = resyncInodeXAttrs(ctx, inode.second);
|
||||
if (xattrRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Syncing XAttrs failed.", path, xattrRes);
|
||||
return xattrRes;
|
||||
}
|
||||
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
FhgfsOpsErr ResyncRawInodesMsgEx::resyncDentry(ResponseContext& ctx, const Path& path,
|
||||
Deserializer& data)
|
||||
{
|
||||
bool linksToFsID;
|
||||
|
||||
data % linksToFsID;
|
||||
if (!data.good())
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Received bad data from primary.");
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
// dentries with independent contents (dir dentries, dentries to non-inlined files) can be
|
||||
// treated like inodes for the purpose of resync. don't sync xattrs though, because dentries
|
||||
// should never have them. set recvXAttrs=false to indicate independent dentry data.
|
||||
if (!linksToFsID)
|
||||
return resyncInode(ctx, path, data, false, false);
|
||||
|
||||
std::string targetID;
|
||||
bool isDeletion;
|
||||
|
||||
data
|
||||
% targetID
|
||||
% isDeletion;
|
||||
if (!data.good())
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Received bad data from primary.");
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
const FhgfsOpsErr rmRes = Program::getApp()->getMetaStore()->unlinkRawMetadata(
|
||||
META_BUDDYMIRROR_SUBDIR_NAME / path);
|
||||
if (rmRes != FhgfsOpsErr_SUCCESS && rmRes != FhgfsOpsErr_PATHNOTEXISTS)
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Could not unlink old dentry.", path, rmRes);
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
if (isDeletion)
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
|
||||
const Path& idPath = path.dirname() / META_DIRENTRYID_SUB_STR / targetID;
|
||||
const int linkRes = ::link(
|
||||
(META_BUDDYMIRROR_SUBDIR_NAME / idPath).str().c_str(),
|
||||
(META_BUDDYMIRROR_SUBDIR_NAME / path).str().c_str());
|
||||
if (linkRes < 0)
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Could not link dentry to fsid.", path, idPath, sysErr);
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
FhgfsOpsErr ResyncRawInodesMsgEx::resyncInodeXAttrs(ResponseContext& ctx, IncompleteInode& inode)
|
||||
{
|
||||
std::string name;
|
||||
std::vector<char> value;
|
||||
|
||||
while (true)
|
||||
{
|
||||
auto readRes = MsgHelperXAttr::StreamXAttrState::readNextXAttr(ctx.getSocket(), name, value);
|
||||
if (readRes == FhgfsOpsErr_SUCCESS)
|
||||
break;
|
||||
else if (readRes != FhgfsOpsErr_AGAIN)
|
||||
return readRes;
|
||||
|
||||
auto setRes = inode.setXattr((XAttrTk::UserXAttrPrefix + name).c_str(), &value[0],
|
||||
value.size());
|
||||
if (setRes != FhgfsOpsErr_SUCCESS)
|
||||
return setRes;
|
||||
}
|
||||
|
||||
return inode.clearUnsetXAttrs();
|
||||
}
|
||||
|
||||
FhgfsOpsErr ResyncRawInodesMsgEx::removeUntouchedInodes()
|
||||
{
|
||||
std::sort(inodesWritten.begin(), inodesWritten.end());
|
||||
|
||||
const Path dirPath(META_BUDDYMIRROR_SUBDIR_NAME / basePath);
|
||||
|
||||
std::unique_ptr<DIR, StorageTk::CloseDirDeleter> dir(::opendir(dirPath.str().c_str()));
|
||||
|
||||
if (!dir)
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Could not open meta directory.", dirPath, sysErr);
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
int dirFD = ::dirfd(dir.get());
|
||||
if (dirFD < 0)
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Could not get directory fd.", sysErr);
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
struct dirent* found;
|
||||
|
||||
#if USE_READDIR_P
|
||||
struct dirent entry;
|
||||
int err = readdir_r(dir.get(), &entry, &found);
|
||||
#else
|
||||
errno = 0;
|
||||
found = readdir(dir.get());
|
||||
int err = found ? 0 : errno;
|
||||
#endif
|
||||
if (err > 0)
|
||||
{
|
||||
LOG(MIRRORING, ERR, "readdir() failed.", sysErr(err));
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
break;
|
||||
|
||||
if (strcmp(found->d_name, ".") == 0 || strcmp(found->d_name, "..") == 0)
|
||||
continue;
|
||||
|
||||
bool written = std::binary_search(
|
||||
inodesWritten.begin(), inodesWritten.end(),
|
||||
found->d_name);
|
||||
if (written)
|
||||
continue;
|
||||
|
||||
const int unlinkRes = ::unlinkat(dirFD, found->d_name, 0);
|
||||
if (unlinkRes == 0 || errno == ENOENT)
|
||||
continue;
|
||||
|
||||
if (errno != EISDIR)
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Could not remove file", basePath, found->d_name, sysErr);
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
const bool rmRes = StorageTk::removeDirRecursive((dirPath / found->d_name).str());
|
||||
if (!rmRes)
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Could not remove file", found->d_name, sysErr);
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/NetMessage.h>
|
||||
#include <common/storage/Path.h>
|
||||
#include <storage/IncompleteInode.h>
|
||||
|
||||
class ResyncRawInodesMsgEx : public NetMessageSerdes<ResyncRawInodesMsgEx>
|
||||
{
|
||||
public:
|
||||
ResyncRawInodesMsgEx(Path basePath, bool hasXAttrs, bool wholeDirectory):
|
||||
BaseType(NETMSGTYPE_ResyncRawInodes),
|
||||
basePath(std::move(basePath)),
|
||||
hasXAttrs(hasXAttrs),
|
||||
wholeDirectory(wholeDirectory)
|
||||
{}
|
||||
|
||||
ResyncRawInodesMsgEx(): BaseType(NETMSGTYPE_ResyncRawInodes) {}
|
||||
|
||||
bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
template<typename This, typename Ctx>
|
||||
static void serialize(This obj, Ctx& ctx)
|
||||
{
|
||||
ctx
|
||||
% obj->basePath
|
||||
% obj->hasXAttrs
|
||||
% obj->wholeDirectory;
|
||||
}
|
||||
|
||||
private:
|
||||
Path basePath;
|
||||
bool hasXAttrs;
|
||||
bool wholeDirectory;
|
||||
|
||||
std::vector<std::string> inodesWritten;
|
||||
|
||||
FhgfsOpsErr resyncStream(ResponseContext& ctx);
|
||||
|
||||
FhgfsOpsErr resyncSingle(ResponseContext& ctx);
|
||||
|
||||
FhgfsOpsErr resyncInode(ResponseContext& ctx, const Path& path, Deserializer& data,
|
||||
const bool isDirectory, const bool recvXAttrs = true);
|
||||
FhgfsOpsErr resyncDentry(ResponseContext& ctx, const Path& path, Deserializer& data);
|
||||
|
||||
FhgfsOpsErr resyncInodeXAttrs(ResponseContext& ctx, IncompleteInode& inode);
|
||||
|
||||
FhgfsOpsErr removeUntouchedInodes();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#include "ResyncSessionStoreMsgEx.h"
|
||||
|
||||
#include <app/App.h>
|
||||
#include <program/Program.h>
|
||||
#include <session/SessionStore.h>
|
||||
#include <common/net/message/storage/mirroring/ResyncSessionStoreRespMsg.h>
|
||||
|
||||
bool ResyncSessionStoreMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
Config* config = app->getConfig();
|
||||
|
||||
FhgfsOpsErr receiveRes = receiveStoreBuf(ctx.getSocket(), config->getConnMsgShortTimeout());
|
||||
|
||||
if (receiveRes == FhgfsOpsErr_OUTOFMEM)
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Failed to allocate receive buffer for session store resync - out of memory.");
|
||||
return false;
|
||||
}
|
||||
else if (receiveRes == FhgfsOpsErr_COMMUNICATION)
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Failed to receive session store buffer during resync.");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto sessionStoreBuf = getSessionStoreBuf();
|
||||
|
||||
SessionStore* sessionStore = Program::getApp()->getMirroredSessions();
|
||||
|
||||
const bool clearRes = sessionStore->clear();
|
||||
if (!clearRes)
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Failed to clear session store.");
|
||||
ctx.sendResponse(ResyncSessionStoreRespMsg(FhgfsOpsErr_INTERNAL));
|
||||
return true;
|
||||
}
|
||||
|
||||
const bool deserRes = sessionStore->deserializeFromBuf(sessionStoreBuf.first,
|
||||
sessionStoreBuf.second, *Program::getApp()->getMetaStore());
|
||||
if (!deserRes)
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Failed to deserialize session store data from primary.");
|
||||
ctx.sendResponse(ResyncSessionStoreRespMsg(FhgfsOpsErr_INTERNAL));
|
||||
return true;
|
||||
}
|
||||
|
||||
ctx.sendResponse(ResyncSessionStoreRespMsg(FhgfsOpsErr_SUCCESS));
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/mirroring/ResyncSessionStoreMsg.h>
|
||||
|
||||
class ResyncSessionStoreMsgEx : public ResyncSessionStoreMsg
|
||||
{
|
||||
public:
|
||||
virtual bool processIncoming(ResponseContext& ctx);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
#include <common/net/message/storage/mirroring/SetMetadataMirroringRespMsg.h>
|
||||
#include <common/toolkit/MetaStorageTk.h>
|
||||
#include <storage/DirInode.h>
|
||||
#include <storage/MetaStore.h>
|
||||
|
||||
#include <program/Program.h>
|
||||
|
||||
#include "SetMetadataMirroringMsgEx.h"
|
||||
|
||||
|
||||
bool SetMetadataMirroringMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
MirrorBuddyGroupMapper* metaBuddyGroupMapper = app->getMetaBuddyGroupMapper();
|
||||
|
||||
// verify that the current node is in a group, and is the primary for its group
|
||||
bool localNodeIsPrimary;
|
||||
uint16_t buddyGroupID = metaBuddyGroupMapper->getBuddyGroupID(app->getLocalNodeNumID().val(),
|
||||
&localNodeIsPrimary);
|
||||
|
||||
if (buddyGroupID == 0)
|
||||
{
|
||||
LogContext(__func__).logErr("This node is not part of a buddy group.");
|
||||
ctx.sendResponse(SetMetadataMirroringRespMsg(FhgfsOpsErr_INTERNAL));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!localNodeIsPrimary)
|
||||
{
|
||||
LogContext(__func__).logErr("This node is not the primary root node.");
|
||||
ctx.sendResponse(SetMetadataMirroringRespMsg(FhgfsOpsErr_INTERNAL));
|
||||
return true;
|
||||
}
|
||||
|
||||
// verify owner of root dir
|
||||
if (app->getLocalNodeNumID() != app->getRootDir()->getOwnerNodeID())
|
||||
{
|
||||
LogContext(__func__).logErr("This node does not own the root directory.");
|
||||
ctx.sendResponse(SetMetadataMirroringRespMsg(FhgfsOpsErr_NOTOWNER));
|
||||
return true;
|
||||
}
|
||||
|
||||
FhgfsOpsErr setRes = setMirroring();
|
||||
|
||||
ctx.sendResponse(SetMetadataMirroringRespMsg(setRes) );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FhgfsOpsErr SetMetadataMirroringMsgEx::setMirroring()
|
||||
{
|
||||
// no two threads must be allowed to run this code at the same time. this could happen during
|
||||
// bulk resync.
|
||||
static Mutex setMirrorMtx;
|
||||
std::unique_lock<Mutex> setMirrorMtxLock(setMirrorMtx);
|
||||
|
||||
// more than one thread may have called this method. if so, report success to the ones who waited
|
||||
if (Program::getApp()->getRootDir()->getIsBuddyMirrored())
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
|
||||
App* app = Program::getApp();
|
||||
MirrorBuddyGroupMapper* metaBuddyGroupMapper = app->getMetaBuddyGroupMapper();
|
||||
|
||||
NumNodeID localNodeNumID = app->getLocalNodeNumID();
|
||||
|
||||
// get buddy group for this node
|
||||
bool localNodeIsPrimary;
|
||||
uint16_t buddyGroupID = metaBuddyGroupMapper->getBuddyGroupID(localNodeNumID.val(),
|
||||
&localNodeIsPrimary);
|
||||
|
||||
// move inode of root directory to mirrored dir
|
||||
FhgfsOpsErr mvInodeRes;
|
||||
FhgfsOpsErr mvDirRes;
|
||||
|
||||
mvInodeRes = moveRootInode(false);
|
||||
|
||||
if (mvInodeRes != FhgfsOpsErr_SUCCESS)
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
|
||||
// move root directory to mirrored dir
|
||||
mvDirRes = moveRootDirectory(false);
|
||||
if (mvDirRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
// get inode back
|
||||
moveRootInode(true);
|
||||
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
// update buddy mirror info and write to disk
|
||||
// NOTE: this must happen after the data has been moved, because buddy mirror flag changes save
|
||||
// path inside of DirInode object
|
||||
DirInode* dir = app->getRootDir();
|
||||
|
||||
const FhgfsOpsErr setMirrorRes = dir->setAndStoreIsBuddyMirrored(true);
|
||||
if (setMirrorRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LOG(MIRRORING, ERR, "Could not set mirror state on root inode", setMirrorRes);
|
||||
const FhgfsOpsErr revertSetRes = dir->setAndStoreIsBuddyMirrored(false);
|
||||
if (revertSetRes != FhgfsOpsErr_SUCCESS)
|
||||
LOG(MIRRORING, ERR, "Could not revert mirror setting either, your filesystem is now corrupt",
|
||||
revertSetRes);
|
||||
|
||||
return FhgfsOpsErr_SAVEERROR;
|
||||
}
|
||||
|
||||
bool setOwnerRes = dir->setOwnerNodeID(NumNodeID(buddyGroupID) );
|
||||
|
||||
if (!setOwnerRes)
|
||||
{
|
||||
// get inode back
|
||||
moveRootInode(true);
|
||||
|
||||
// get dir back
|
||||
moveRootDirectory(true);
|
||||
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
// update root Node in meta store
|
||||
app->getMetaRoot().set(NumNodeID(buddyGroupID), true);
|
||||
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
FhgfsOpsErr SetMetadataMirroringMsgEx::moveRootInode(bool revertMove)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
|
||||
Path oldPath( app->getMetaPath() + "/"
|
||||
+ MetaStorageTk::getMetaInodePath(app->getInodesPath()->str(),
|
||||
META_ROOTDIR_ID_STR));
|
||||
|
||||
Path newPath( app->getMetaPath() + "/"
|
||||
+ MetaStorageTk::getMetaInodePath(app->getBuddyMirrorInodesPath()->str(),
|
||||
META_ROOTDIR_ID_STR));
|
||||
|
||||
int renameRes;
|
||||
if ( !revertMove )
|
||||
{
|
||||
StorageTk::createPathOnDisk(newPath, true);
|
||||
renameRes = rename(oldPath.str().c_str(), newPath.str().c_str());
|
||||
}
|
||||
else
|
||||
renameRes = rename(newPath.str().c_str(), oldPath.str().c_str());
|
||||
|
||||
if ( renameRes )
|
||||
{
|
||||
LogContext(__func__).logErr(
|
||||
"Unable to move root inode; error: " + System::getErrString());
|
||||
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
FhgfsOpsErr SetMetadataMirroringMsgEx::moveRootDirectory(bool revertMove)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
|
||||
Path oldPath(app->getMetaPath() + "/"
|
||||
+ MetaStorageTk::getMetaDirEntryPath(app->getDentriesPath()->str(),
|
||||
META_ROOTDIR_ID_STR));
|
||||
Path newPath(app->getMetaPath() + "/"
|
||||
+ MetaStorageTk::getMetaDirEntryPath(app->getBuddyMirrorDentriesPath()->str(),
|
||||
META_ROOTDIR_ID_STR));
|
||||
|
||||
int renameRes;
|
||||
if ( !revertMove )
|
||||
{
|
||||
StorageTk::createPathOnDisk(newPath, true);
|
||||
renameRes = rename(oldPath.str().c_str(), newPath.str().c_str());
|
||||
}
|
||||
else
|
||||
renameRes = rename(newPath.str().c_str(), oldPath.str().c_str());
|
||||
|
||||
if (renameRes)
|
||||
{
|
||||
LogContext(__func__).logErr(
|
||||
"Unable to move root directory; error: " + System::getErrString());
|
||||
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
return FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/mirroring/SetMetadataMirroringMsg.h>
|
||||
|
||||
|
||||
class SetMetadataMirroringMsgEx : public SetMetadataMirroringMsg
|
||||
{
|
||||
public:
|
||||
virtual bool processIncoming(ResponseContext& ctx);
|
||||
|
||||
static FhgfsOpsErr setMirroring();
|
||||
|
||||
private:
|
||||
static FhgfsOpsErr moveRootInode(bool revertMove);
|
||||
static FhgfsOpsErr moveRootDirectory(bool revertMove);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
#include <common/components/worker/queue/MultiWorkQueue.h>
|
||||
#include <common/net/message/storage/mirroring/StorageResyncStartedRespMsg.h>
|
||||
#include <common/threading/Barrier.h>
|
||||
#include <common/threading/PThread.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
|
||||
#include <program/Program.h>
|
||||
#include <components/worker/BarrierWork.h>
|
||||
|
||||
#include "StorageResyncStartedMsgEx.h"
|
||||
|
||||
bool StorageResyncStartedMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
NumNodeID nodeID = Program::getApp()->getLocalNodeNumID();
|
||||
|
||||
uint16_t targetID = getValue();
|
||||
if (targetID != nodeID.val())
|
||||
return false;
|
||||
|
||||
// Make sure all workers have processed all messages that were received before this one.
|
||||
// This ensures that no mirrored messages are in flight while resync starts.
|
||||
pauseWorkers();
|
||||
|
||||
// we may not have received a heartbeat from the mgmtd yet that could have told us that the
|
||||
// root inode is mirrored. since un-mirroring the root inode is currently not possible, we may
|
||||
// assume that the root inode is very much supposed to be mirrored.
|
||||
// we only need this info if we are currently the secondary of the root buddy group, but we
|
||||
// can safely set it on all nodes until we add an option to disable meta mirroring for the
|
||||
// root inode again.
|
||||
Program::getApp()->getRootDir()->setIsBuddyMirroredFlag(true);
|
||||
|
||||
// clear session store here to get rid of all inodes that will go away during resync.
|
||||
Program::getApp()->getMirroredSessions()->clear();
|
||||
|
||||
// we can now be sure that no mirrored dir inode is still referenced by an operation:
|
||||
// * the session store is cleared, so no references from there
|
||||
// * we have received this message, so must be in NeedsResync state
|
||||
// -> no mirrored operations will be addressed to this node
|
||||
// * we hold no references ourselves
|
||||
//
|
||||
// since resync changes mirrored dir inodes, we must invalidate all inodes that are currently
|
||||
// loaded (which will be at least root and mdisposal, plus any cached dir inodes) to ensure that
|
||||
// future operations will use the correct resynced data
|
||||
Program::getApp()->getMetaStore()->invalidateMirroredDirInodes();
|
||||
|
||||
ctx.sendResponse(StorageResyncStartedRespMsg());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void StorageResyncStartedMsgEx::pauseWorkers()
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
WorkerList* workers = app->getWorkers();
|
||||
MultiWorkQueue* workQueue = app->getWorkQueue();
|
||||
pthread_t threadID = PThread::getCurrentThreadID();
|
||||
|
||||
// Stop all worker threads except our own
|
||||
Barrier workerBarrier(workers->size());
|
||||
for (WorkerListIter workerIt = workers->begin(); workerIt != workers->end(); ++workerIt)
|
||||
{
|
||||
// don't enqueue it in the worker that processes this message (this would deadlock)
|
||||
if (!PThread::threadIDEquals((*workerIt)->getID(), threadID))
|
||||
{
|
||||
PersonalWorkQueue* personalQ = (*workerIt)->getPersonalWorkQueue();
|
||||
workQueue->addPersonalWork(new BarrierWork(&workerBarrier), personalQ);
|
||||
}
|
||||
}
|
||||
|
||||
// Stall our own worker until all the other threads are blocked.
|
||||
workerBarrier.wait();
|
||||
|
||||
// Continue all workers
|
||||
workerBarrier.wait();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/mirroring/StorageResyncStartedMsg.h>
|
||||
|
||||
class StorageResyncStartedMsgEx : public StorageResyncStartedMsg
|
||||
{
|
||||
public:
|
||||
StorageResyncStartedMsgEx() : StorageResyncStartedMsg()
|
||||
{ }
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx);
|
||||
|
||||
private:
|
||||
void pauseWorkers();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/moving/MovingDirInsertMsg.h>
|
||||
#include <common/net/message/storage/moving/MovingDirInsertRespMsg.h>
|
||||
#include <common/storage/striping/Raid0Pattern.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include "MovingDirInsertMsgEx.h"
|
||||
|
||||
|
||||
bool MovingDirInsertMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
rctx = &ctx;
|
||||
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> MovingDirInsertMsgEx::executeLocally(
|
||||
ResponseContext& ctx, bool isSecondary)
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
FhgfsOpsErr retVal;
|
||||
|
||||
EntryInfo* toDirInfo = this->getToDirInfo();
|
||||
|
||||
// reference parent
|
||||
DirInode* parentDir = metaStore->referenceDir(toDirInfo->getEntryID(),
|
||||
toDirInfo->getIsBuddyMirrored(), true);
|
||||
if(!parentDir)
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
|
||||
|
||||
/* create dir-entry and add information about its inode from the given buffer */
|
||||
|
||||
const std::string& newName = this->getNewName();
|
||||
const char* buf = this->getSerialBuf();
|
||||
Deserializer des(buf, getSerialBufLen());
|
||||
DirEntry newDirEntry(newName);
|
||||
|
||||
newDirEntry.deserializeDentry(des);
|
||||
if (!des.good())
|
||||
{
|
||||
LogContext("File rename").logErr("Bug: Deserialization of remote buffer failed. Are all "
|
||||
"meta servers running the same version?" );
|
||||
|
||||
retVal = FhgfsOpsErr_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (newDirEntry.getEntryID() == parentDir->getID() )
|
||||
{ // attempt to rename a dir into itself
|
||||
retVal = FhgfsOpsErr_INVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
retVal = parentDir->makeDirEntry(newDirEntry);
|
||||
|
||||
if (retVal == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
fixInodeTimestamp(*parentDir, dirTimestamps);
|
||||
|
||||
if (retVal != FhgfsOpsErr_SUCCESS && retVal != FhgfsOpsErr_EXISTS)
|
||||
retVal = FhgfsOpsErr_INTERNAL;
|
||||
|
||||
out:
|
||||
metaStore->releaseDir(toDirInfo->getEntryID());
|
||||
return boost::make_unique<ResponseState>(retVal);
|
||||
}
|
||||
|
||||
void MovingDirInsertMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_MovingDirInsertResp);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/moving/MovingDirInsertMsg.h>
|
||||
#include <common/net/message/storage/moving/MovingDirInsertRespMsg.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
// Move directory to another meta-data server
|
||||
|
||||
class MovingDirInsertMsgEx : public MirroredMessage<MovingDirInsertMsg,
|
||||
std::tuple<FileIDLock, ParentNameLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<MovingDirInsertRespMsg, NETMSGTYPE_MovingDirInsert>
|
||||
ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock> lock(EntryLockStore& store) override
|
||||
{
|
||||
// we must not lock the directory if it is owned by the current node. if it is, the
|
||||
// current message was also sent by the local node, specifically by a RmDirMsgEx, which
|
||||
// also locks the directory for write
|
||||
if (rctx->isLocallyGenerated())
|
||||
return {};
|
||||
|
||||
FileIDLock dirLock(&store, getToDirInfo()->getEntryID(), true);
|
||||
ParentNameLock nameLock(&store, getToDirInfo()->getEntryID(), getNewName());
|
||||
|
||||
return std::make_tuple(std::move(dirLock), std::move(nameLock));
|
||||
}
|
||||
|
||||
bool isMirrored() override { return getToDirInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
ResponseContext* rctx;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return static_cast<MovingDirInsertRespMsg&>(resp).getResult();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "MovingDirInsertMsgEx/forward"; }
|
||||
};
|
||||
|
||||
173
meta/source/net/message/storage/moving/MovingFileInsertMsgEx.cpp
Normal file
173
meta/source/net/message/storage/moving/MovingFileInsertMsgEx.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/moving/MovingFileInsertMsg.h>
|
||||
#include <common/net/message/storage/moving/MovingFileInsertRespMsg.h>
|
||||
#include <common/storage/striping/Raid0Pattern.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <net/msghelpers/MsgHelperUnlink.h>
|
||||
#include <net/msghelpers/MsgHelperXAttr.h>
|
||||
|
||||
#include "MovingFileInsertMsgEx.h"
|
||||
|
||||
std::tuple<FileIDLock, FileIDLock, FileIDLock, ParentNameLock> MovingFileInsertMsgEx::lock(
|
||||
EntryLockStore& store)
|
||||
{
|
||||
// we must not lock the directory if it is owned by the current node. if it is, the
|
||||
// current message was also sent by the local node, specifically by a rename msg, which
|
||||
// also locks the directory for write
|
||||
if (rctx->isLocallyGenerated())
|
||||
return {};
|
||||
|
||||
FileIDLock dirLock(&store, getToDirInfo()->getEntryID(), true);
|
||||
|
||||
ParentNameLock nameLock(&store, getToDirInfo()->getEntryID(), getNewName());
|
||||
|
||||
FileIDLock newLock;
|
||||
FileIDLock unlinkedLock;
|
||||
|
||||
auto dir = Program::getApp()->getMetaStore()->referenceDir(getToDirInfo()->getEntryID(),
|
||||
getToDirInfo()->getIsBuddyMirrored(), true);
|
||||
if (dir)
|
||||
{
|
||||
FileInode newInode;
|
||||
Deserializer des(getSerialBuf(), getSerialBufLen());
|
||||
newInode.deserializeMetaData(des);
|
||||
if (des.good())
|
||||
{
|
||||
std::string unlinkedID = newInode.getEntryID();
|
||||
|
||||
EntryInfo unlinkedInfo;
|
||||
dir->getFileEntryInfo(getNewName(), unlinkedInfo);
|
||||
if (DirEntryType_ISFILE(unlinkedInfo.getEntryType()))
|
||||
unlinkedID = unlinkedInfo.getEntryID();
|
||||
|
||||
if (newInode.getEntryID() < unlinkedID)
|
||||
{
|
||||
newLock = {&store, newInode.getEntryID(), true};
|
||||
unlinkedLock = {&store, unlinkedID, true};
|
||||
}
|
||||
else if (newInode.getEntryID() == unlinkedID)
|
||||
{
|
||||
newLock = {&store, newInode.getEntryID(), true};
|
||||
}
|
||||
else
|
||||
{
|
||||
unlinkedLock = {&store, unlinkedID, true};
|
||||
newLock = {&store, newInode.getEntryID(), true};
|
||||
}
|
||||
}
|
||||
|
||||
Program::getApp()->getMetaStore()->releaseDir(dir->getID());
|
||||
}
|
||||
|
||||
return std::make_tuple(
|
||||
std::move(newLock),
|
||||
std::move(unlinkedLock),
|
||||
std::move(dirLock),
|
||||
std::move(nameLock));
|
||||
}
|
||||
|
||||
bool MovingFileInsertMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
rctx = &ctx;
|
||||
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> MovingFileInsertMsgEx::executeLocally(
|
||||
ResponseContext& ctx, bool isSecondary)
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
EntryInfo* fromFileInfo = this->getFromFileInfo();
|
||||
EntryInfo* toDirInfo = this->getToDirInfo();
|
||||
std::string newName = this->getNewName();
|
||||
|
||||
EntryInfo overWrittenEntryInfo;
|
||||
std::unique_ptr<FileInode> unlinkInode;
|
||||
unsigned inodeBufLen;
|
||||
std::unique_ptr<char[]> inodeBuf;
|
||||
|
||||
DirInode* toDir = metaStore->referenceDir(toDirInfo->getEntryID(),
|
||||
toDirInfo->getIsBuddyMirrored(), true);
|
||||
if (!toDir)
|
||||
return boost::make_unique<MovingFileInsertResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
|
||||
|
||||
FhgfsOpsErr moveRes = metaStore->moveRemoteFileInsert(
|
||||
fromFileInfo, *toDir, newName, getSerialBuf(), getSerialBufLen(), &unlinkInode,
|
||||
&overWrittenEntryInfo, newFileInfo);
|
||||
if (moveRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
metaStore->releaseDir(toDir->getID());
|
||||
return boost::make_unique<MovingFileInsertResponseState>(moveRes);
|
||||
}
|
||||
|
||||
std::string xattrName;
|
||||
CharVector xattrValue;
|
||||
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
|
||||
|
||||
while (isMsgHeaderFeatureFlagSet(MOVINGFILEINSERTMSG_FLAG_HAS_XATTRS))
|
||||
{
|
||||
retVal = MsgHelperXAttr::StreamXAttrState::readNextXAttr(ctx.getSocket(), xattrName,
|
||||
xattrValue);
|
||||
if (retVal == FhgfsOpsErr_SUCCESS)
|
||||
break;
|
||||
else if (retVal != FhgfsOpsErr_AGAIN)
|
||||
goto xattr_error;
|
||||
|
||||
retVal = MsgHelperXAttr::setxattr(&newFileInfo, xattrName, xattrValue, 0);
|
||||
if (retVal != FhgfsOpsErr_SUCCESS)
|
||||
goto xattr_error;
|
||||
|
||||
xattrNames.push_back(xattrName);
|
||||
}
|
||||
|
||||
if(unlinkInode)
|
||||
{
|
||||
inodeBuf.reset(new (std::nothrow) char[META_SERBUF_SIZE]);
|
||||
if (unlikely(!inodeBuf) )
|
||||
{ // out of memory, we are going to leak an inode and chunks
|
||||
inodeBufLen = 0;
|
||||
LOG(GENERAL, ERR, "Malloc failed, leaking chunks", ("inodeID", unlinkInode->getEntryID()));
|
||||
}
|
||||
else
|
||||
{
|
||||
Serializer ser(inodeBuf.get(), META_SERBUF_SIZE);
|
||||
unlinkInode->serializeMetaData(ser);
|
||||
inodeBufLen = ser.size();
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // no file overwritten
|
||||
inodeBufLen = 0;
|
||||
}
|
||||
|
||||
if (shouldFixTimestamps())
|
||||
{
|
||||
fixInodeTimestamp(*toDir, dirTimestamps);
|
||||
auto [newFile, referenceRes] = metaStore->referenceFile(&newFileInfo);
|
||||
if (newFile)
|
||||
{
|
||||
fixInodeTimestamp(*newFile, fileTimestamps, &newFileInfo);
|
||||
metaStore->releaseFile(toDir->getID(), newFile);
|
||||
}
|
||||
}
|
||||
|
||||
metaStore->releaseDir(toDir->getID());
|
||||
|
||||
return boost::make_unique<MovingFileInsertResponseState>(FhgfsOpsErr_SUCCESS, inodeBufLen,
|
||||
std::move(inodeBuf), overWrittenEntryInfo);
|
||||
|
||||
xattr_error:
|
||||
unsigned outNumHardlinks; // Not used here!
|
||||
MsgHelperUnlink::unlinkMetaFile(*toDir, newName, NULL, outNumHardlinks);
|
||||
metaStore->releaseDir(toDir->getID());
|
||||
|
||||
return boost::make_unique<MovingFileInsertResponseState>(retVal);
|
||||
}
|
||||
|
||||
void MovingFileInsertMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_MovingFileInsertResp);
|
||||
}
|
||||
114
meta/source/net/message/storage/moving/MovingFileInsertMsgEx.h
Normal file
114
meta/source/net/message/storage/moving/MovingFileInsertMsgEx.h
Normal file
@@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/moving/MovingFileInsertMsg.h>
|
||||
#include <common/net/message/storage/moving/MovingFileInsertRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include <net/msghelpers/MsgHelperXAttr.h>
|
||||
|
||||
// this class is used on the server where the file is moved to
|
||||
|
||||
class MovingFileInsertResponseState : public MirroredMessageResponseState
|
||||
{
|
||||
public:
|
||||
explicit MovingFileInsertResponseState(FhgfsOpsErr result)
|
||||
: result(result), inodeBufLen(0)
|
||||
{
|
||||
}
|
||||
|
||||
explicit MovingFileInsertResponseState(Deserializer& des)
|
||||
{
|
||||
des
|
||||
% serdes::as<int32_t>(result)
|
||||
% inodeBufLen;
|
||||
|
||||
if (inodeBufLen > META_SERBUF_SIZE)
|
||||
des.setBad();
|
||||
else
|
||||
{
|
||||
inodeBuf.reset(new char[inodeBufLen]);
|
||||
des.getBlock(inodeBuf.get(), inodeBufLen);
|
||||
}
|
||||
|
||||
des % overWrittenEntryInfo;
|
||||
}
|
||||
|
||||
MovingFileInsertResponseState(FhgfsOpsErr result, unsigned inodeBufLen,
|
||||
std::unique_ptr<char[]> inodeBuf, EntryInfo entryInfo)
|
||||
: result(result), inodeBufLen(inodeBufLen), inodeBuf(std::move(inodeBuf)),
|
||||
overWrittenEntryInfo(entryInfo)
|
||||
{
|
||||
}
|
||||
|
||||
void sendResponse(NetMessage::ResponseContext& ctx) override
|
||||
{
|
||||
ctx.sendResponse(MovingFileInsertRespMsg(result, inodeBufLen, inodeBuf.get(), overWrittenEntryInfo));
|
||||
}
|
||||
|
||||
bool changesObservableState() const override
|
||||
{
|
||||
return result == FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t serializerTag() const override { return NETMSGTYPE_MovingFileInsert; }
|
||||
|
||||
void serializeContents(Serializer& ser) const override
|
||||
{
|
||||
ser
|
||||
% serdes::as<int32_t>(result)
|
||||
% inodeBufLen;
|
||||
|
||||
ser.putBlock(inodeBuf.get(), inodeBufLen);
|
||||
}
|
||||
|
||||
private:
|
||||
FhgfsOpsErr result;
|
||||
unsigned inodeBufLen;
|
||||
std::unique_ptr<char[]> inodeBuf;
|
||||
EntryInfo overWrittenEntryInfo;
|
||||
};
|
||||
|
||||
class MovingFileInsertMsgEx : public MirroredMessage<MovingFileInsertMsg,
|
||||
std::tuple<FileIDLock, FileIDLock, FileIDLock, ParentNameLock>>
|
||||
{
|
||||
public:
|
||||
typedef MovingFileInsertResponseState ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<FileIDLock, FileIDLock, FileIDLock, ParentNameLock>
|
||||
lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getToDirInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
ResponseContext* rctx;
|
||||
|
||||
StringVector xattrNames;
|
||||
EntryInfo newFileInfo;
|
||||
MsgHelperXAttr::StreamXAttrState streamState;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
void prepareMirrorRequestArgs(RequestResponseArgs& args) override
|
||||
{
|
||||
if (isMsgHeaderFeatureFlagSet(MOVINGFILEINSERTMSG_FLAG_HAS_XATTRS))
|
||||
{
|
||||
streamState = {newFileInfo, xattrNames};
|
||||
registerStreamoutHook(args, streamState);
|
||||
}
|
||||
}
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return static_cast<MovingFileInsertRespMsg&>(resp).getResult();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "MovingFileInsertMsgEx/forward"; }
|
||||
};
|
||||
|
||||
896
meta/source/net/message/storage/moving/RenameV2MsgEx.cpp
Normal file
896
meta/source/net/message/storage/moving/RenameV2MsgEx.cpp
Normal file
@@ -0,0 +1,896 @@
|
||||
#include <common/components/streamlistenerv2/IncomingPreprocessedMsgWork.h>
|
||||
#include <common/net/message/control/GenericResponseMsg.h>
|
||||
#include <common/net/message/storage/attribs/GetEntryInfoMsg.h>
|
||||
#include <common/net/message/storage/attribs/GetEntryInfoRespMsg.h>
|
||||
#include <common/net/message/storage/attribs/UpdateDirParentMsg.h>
|
||||
#include <common/net/message/storage/attribs/UpdateDirParentRespMsg.h>
|
||||
#include <common/net/message/storage/moving/MovingDirInsertMsg.h>
|
||||
#include <common/net/message/storage/moving/MovingDirInsertRespMsg.h>
|
||||
#include <common/net/message/storage/moving/MovingFileInsertMsg.h>
|
||||
#include <common/net/message/storage/moving/MovingFileInsertRespMsg.h>
|
||||
#include <common/net/message/storage/moving/RenameRespMsg.h>
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileMsg.h>
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileRespMsg.h>
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileInodeMsg.h>
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileInodeRespMsg.h>
|
||||
#include <common/net/message/storage/attribs/StatMsg.h>
|
||||
#include <common/net/message/storage/attribs/StatRespMsg.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <common/toolkit/MetadataTk.h>
|
||||
#include <components/FileEventLogger.h>
|
||||
#include <components/ModificationEventFlusher.h>
|
||||
#include <net/msghelpers/MsgHelperUnlink.h>
|
||||
#include <net/msghelpers/MsgHelperXAttr.h>
|
||||
#include <net/msghelpers/MsgHelperStat.h>
|
||||
#include <program/Program.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include "RenameV2MsgEx.h"
|
||||
|
||||
#include <boost/scoped_array.hpp>
|
||||
|
||||
namespace {
|
||||
struct DirHandle {
|
||||
MetaStore* metaStore;
|
||||
const EntryInfo* ei;
|
||||
|
||||
DirHandle(MetaStore* metaStore, const EntryInfo* ei): metaStore(metaStore), ei(ei) {}
|
||||
|
||||
DirHandle(const DirHandle&) = delete;
|
||||
DirHandle(DirHandle&&) = delete;
|
||||
|
||||
DirHandle& operator=(const DirHandle&) = delete;
|
||||
DirHandle& operator=(DirHandle&&) = delete;
|
||||
|
||||
~DirHandle() {
|
||||
metaStore->releaseDir(ei->getEntryID());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
RenameV2Locks RenameV2MsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
// if the directory could not be referenced it does not exist on the current node. this will
|
||||
// cause the operation to fail lateron during executeLocally() when we reference the same
|
||||
// directory again. since we cannot do anything without having access to the source directory,
|
||||
// and since no directory with the same id as the source directory can appear after the source
|
||||
// directory has been removed, we can safely unlock everything right here and continue without
|
||||
// blocking other workers on the (probably live) target directory.
|
||||
DirInode* fromDir = metaStore->referenceDir(getFromDirInfo()->getEntryID(),
|
||||
getFromDirInfo()->getIsBuddyMirrored(), true);
|
||||
if (!fromDir)
|
||||
return {};
|
||||
|
||||
const DirHandle _from(metaStore, getFromDirInfo());
|
||||
|
||||
DirInode* toDir = metaStore->referenceDir(getToDirInfo()->getEntryID(),
|
||||
getToDirInfo()->getIsBuddyMirrored(), true);
|
||||
|
||||
if (!toDir)
|
||||
return {};
|
||||
|
||||
const DirHandle _to(metaStore, getToDirInfo());
|
||||
|
||||
for (;;) {
|
||||
RenameV2Locks result;
|
||||
|
||||
EntryInfo fromFileInfo;
|
||||
EntryInfo toFileInfo;
|
||||
|
||||
fromDir->getFileEntryInfo(getOldName(), fromFileInfo);
|
||||
bool toFileExists = toDir->getFileEntryInfo(getNewName(), toFileInfo);
|
||||
|
||||
if (toFileExists && DirEntryType_ISFILE(toFileInfo.getEntryType()))
|
||||
{
|
||||
// lock hash dir of tofile inode only if:
|
||||
// 1) its a file and
|
||||
// 2) its a non-inlined inode and
|
||||
// 3) resynch job is running
|
||||
if (resyncJob && resyncJob->isRunning() && !toFileInfo.getIsInlined())
|
||||
result.toFileHashLock = {&store, MetaStorageTk::getMetaInodeHash(toFileInfo.getEntryID())};
|
||||
}
|
||||
|
||||
{
|
||||
std::map<std::string, FileIDLock*> lockOrder;
|
||||
|
||||
lockOrder.insert(std::make_pair(getFromDirInfo()->getEntryID(), &result.fromDirLock));
|
||||
lockOrder.insert(std::make_pair(getToDirInfo()->getEntryID(), &result.toDirLock));
|
||||
if (DirEntryType_ISDIR(fromFileInfo.getEntryType()))
|
||||
lockOrder.insert(std::make_pair(fromFileInfo.getEntryID(), &result.fromFileLockD));
|
||||
|
||||
for (auto it = lockOrder.begin(); it != lockOrder.end(); ++it)
|
||||
*it->second = {&store, it->first, true};
|
||||
}
|
||||
|
||||
// we might have locked fromFileLockD before fromDirLock due to ordering. resolve the source
|
||||
// once more and check that we still refer to the same id, otherwise retry until we have the
|
||||
// correct inode.
|
||||
// if the name went away we don't have to retry (it can't be created while the dir is locked),
|
||||
// but retrying is simpler to do.
|
||||
EntryInfo fromFileInfoCheck;
|
||||
fromDir->getFileEntryInfo(getOldName(), fromFileInfoCheck);
|
||||
if (fromFileInfo.getEntryID() != fromFileInfoCheck.getEntryID())
|
||||
continue;
|
||||
|
||||
// take care about lock ordering! see MirroredMessage::lock()
|
||||
// since directories are locked for read, and by the same id as the (parent,name) tuples,the
|
||||
// same ordering applies.
|
||||
if (getFromDirInfo()->getEntryID() < getToDirInfo()->getEntryID())
|
||||
{
|
||||
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
|
||||
result.toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
|
||||
}
|
||||
else if (getFromDirInfo()->getEntryID() == getToDirInfo()->getEntryID())
|
||||
{
|
||||
if (getOldName() < getNewName())
|
||||
{
|
||||
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
|
||||
result.toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
|
||||
}
|
||||
else if (getOldName() == getNewName())
|
||||
{
|
||||
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
|
||||
}
|
||||
else
|
||||
{
|
||||
result.toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
|
||||
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
|
||||
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
|
||||
}
|
||||
|
||||
if (DirEntryType_ISFILE(fromFileInfo.getEntryType()) && fromFileInfo.getIsInlined())
|
||||
{
|
||||
if (DirEntryType_ISFILE(toFileInfo.getEntryType()) && toFileInfo.getIsInlined())
|
||||
{
|
||||
if (fromFileInfo.getEntryID() < toFileInfo.getEntryID())
|
||||
{
|
||||
result.fromFileLockF = {&store, fromFileInfo.getEntryID(), true};
|
||||
result.unlinkedFileLock = {&store, toFileInfo.getEntryID(), true};
|
||||
}
|
||||
else if (fromFileInfo.getEntryID() == toFileInfo.getEntryID())
|
||||
{
|
||||
result.fromFileLockF = {&store, fromFileInfo.getEntryID(), true};
|
||||
}
|
||||
else
|
||||
{
|
||||
result.unlinkedFileLock = {&store, toFileInfo.getEntryID(), true};
|
||||
result.fromFileLockF = {&store, fromFileInfo.getEntryID(), true};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.fromFileLockF = {&store, fromFileInfo.getEntryID(), true};
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
bool RenameV2MsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
LOG_DEBUG(__func__, Log_DEBUG, "FromDirID: " + getFromDirInfo()->getEntryID() + "; "
|
||||
"oldName: '" + getOldName() + "'; "
|
||||
"ToDirID: " + getToDirInfo()->getEntryID() + "; "
|
||||
"newName: '" + getNewName() + "'");
|
||||
|
||||
BaseType::processIncoming(ctx);
|
||||
|
||||
// update operation counters
|
||||
updateNodeOp(ctx, MetaOpCounter_RENAME);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks existence of the from-part and calls movingPerform().
|
||||
*/
|
||||
std::unique_ptr<MirroredMessageResponseState> RenameV2MsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
MetaStore* metaStore = app->getMetaStore();
|
||||
|
||||
// reference fromParent
|
||||
DirInode* fromParent = metaStore->referenceDir(getFromDirInfo()->getEntryID(),
|
||||
getFromDirInfo()->getIsBuddyMirrored(), true);
|
||||
|
||||
if (unlikely(!fromParent))
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
|
||||
|
||||
std::string unlinkedEntryID; // ID of potential overwritten destination file for cleanup
|
||||
EntryInfo srcEntryInfo; // EntryInfo of source file/directory being renamed
|
||||
unsigned srcEntryLinkCount = 0; // Hardlink count needed for event logging
|
||||
|
||||
ModificationEventFlusher* modEventFlusher = app->getModificationEventFlusher();
|
||||
const bool modEventLoggingEnabled = modEventFlusher->isLoggingEnabled();
|
||||
|
||||
const bool fileEventLogEnabled = !isSecondary && getFileEvent() && app->getFileEventLogger();
|
||||
|
||||
if (modEventLoggingEnabled || fileEventLogEnabled)
|
||||
{
|
||||
fromParent->getEntryInfo(getOldName(), srcEntryInfo);
|
||||
|
||||
// Fetch link count early before rename occurs:
|
||||
// - Inlined inode data might move to a different metadata node post-rename
|
||||
// - Early fetch avoids a StatMsg round trip to retrieve link count from the new node
|
||||
FhgfsOpsErr res;
|
||||
std::tie(res, srcEntryLinkCount) = getLinkCountForMovedEntry(&srcEntryInfo);
|
||||
if (res != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LogContext("RenameV2MsgEx::executeLocally").logErr(
|
||||
"Failed to get link count for entry: " + srcEntryInfo.getEntryID());
|
||||
// don't return an error to client
|
||||
}
|
||||
}
|
||||
|
||||
FhgfsOpsErr renameRes = movingPerform(*fromParent, getOldName(),
|
||||
getEntryType(), getToDirInfo(), getNewName(), unlinkedEntryID);
|
||||
|
||||
if ((renameRes == FhgfsOpsErr_SUCCESS) && shouldFixTimestamps())
|
||||
fixInodeTimestamp(*fromParent, fromDirTimestamps);
|
||||
|
||||
metaStore->releaseDir(getFromDirInfo()->getEntryID());
|
||||
|
||||
if ((renameRes == FhgfsOpsErr_SUCCESS) && fileEventLogEnabled)
|
||||
{
|
||||
EventContext eventCtx = makeEventContext(
|
||||
&srcEntryInfo,
|
||||
getFromDirInfo()->getEntryID(),
|
||||
getMsgHeaderUserID(),
|
||||
getToDirInfo()->getEntryID(),
|
||||
srcEntryLinkCount,
|
||||
isSecondary
|
||||
);
|
||||
|
||||
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
|
||||
// clean-up
|
||||
if ((renameRes == FhgfsOpsErr_SUCCESS) && modEventLoggingEnabled)
|
||||
{
|
||||
if (DirEntryType_ISDIR(getEntryType()))
|
||||
modEventFlusher->add(ModificationEvent_DIRMOVED, srcEntryInfo.getEntryID());
|
||||
else
|
||||
{
|
||||
modEventFlusher->add(ModificationEvent_FILEMOVED, srcEntryInfo.getEntryID());
|
||||
if (!unlinkedEntryID.empty())
|
||||
modEventFlusher->add(ModificationEvent_FILEREMOVED, unlinkedEntryID);
|
||||
}
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(renameRes);
|
||||
}
|
||||
|
||||
FhgfsOpsErr RenameV2MsgEx::movingPerform(DirInode& fromParent, const std::string& oldName,
|
||||
DirEntryType entryType, EntryInfo* toDirInfo, const std::string& newName, std::string& unlinkedEntryID)
|
||||
{
|
||||
const char* logContext = "RenameV2MsgEx::movingPerform";
|
||||
IGNORE_UNUSED_VARIABLE(logContext);
|
||||
App* app = Program::getApp();
|
||||
|
||||
// is this node the owner of the fromParent dir?
|
||||
MirrorBuddyGroupMapper* metaBuddyGroupMapper = app->getMetaBuddyGroupMapper();
|
||||
NumNodeID expectedOwnerID = fromParent.getIsBuddyMirrored() ?
|
||||
NumNodeID(metaBuddyGroupMapper->getLocalGroupID() ) : app->getLocalNode().getNumID();
|
||||
|
||||
if (fromParent.getOwnerNodeID() != expectedOwnerID)
|
||||
return FhgfsOpsErr_NOTOWNER;
|
||||
|
||||
if (unlikely(entryType == DirEntryType_INVALID) )
|
||||
{
|
||||
LOG_DEBUG(logContext, Log_SPAM, "Received an invalid entry type!");
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_INTERNAL;
|
||||
|
||||
if (fromParent.getID() == toDirInfo->getEntryID())
|
||||
{ // simple rename (<= not a move && everything local)
|
||||
LOG_DEBUG(logContext, Log_SPAM, "Method: rename in same dir"); // debug in
|
||||
retVal = renameInSameDir(fromParent, oldName, newName, unlinkedEntryID);
|
||||
}
|
||||
else
|
||||
if (entryType == DirEntryType_DIRECTORY)
|
||||
retVal = renameDir(fromParent, oldName, toDirInfo, newName);
|
||||
else
|
||||
retVal = renameFile(fromParent, oldName, toDirInfo, newName, unlinkedEntryID);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void RenameV2MsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_RenameResp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a directory
|
||||
*/
|
||||
FhgfsOpsErr RenameV2MsgEx::renameDir(DirInode& fromParent, const std::string& oldName,
|
||||
EntryInfo* toDirInfo, const std::string& newName)
|
||||
{
|
||||
const char* logContext = "RenameV2MsgEx::renameDir";
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_INTERNAL;
|
||||
|
||||
DirEntry fromDirEntry(oldName);
|
||||
bool dirEntryCopyRes = fromParent.getDirDentry(oldName, fromDirEntry);
|
||||
if (!dirEntryCopyRes)
|
||||
{
|
||||
LOG_DEBUG("RenameV2MsgEx::movingPerform", Log_SPAM, "getDirEntryCopy() failed");
|
||||
return FhgfsOpsErr_NOTADIR;
|
||||
}
|
||||
|
||||
// when we we verified the 'oldName' is really a directory
|
||||
LOG_DEBUG(logContext, Log_SPAM, "Method: remote dir move."); // debug in
|
||||
|
||||
if (fromParent.getIsBuddyMirrored() && hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
|
||||
{
|
||||
retVal = FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
// prepare local part of the move operation
|
||||
boost::scoped_array<char> serialBuf(new char[META_SERBUF_SIZE]);
|
||||
Serializer ser(serialBuf.get(), META_SERBUF_SIZE);
|
||||
|
||||
/* Put all meta data of this dentry into the given buffer. The buffer then will be
|
||||
* used on the remote side to fill the new dentry */
|
||||
fromDirEntry.serializeDentry(ser);
|
||||
|
||||
if (!ser.good())
|
||||
LogContext(logContext).logErr("dentry too large: " + oldName);
|
||||
else
|
||||
retVal = remoteDirInsert(toDirInfo, newName, serialBuf.get(), ser.size());
|
||||
}
|
||||
|
||||
// finish local part of the move operation
|
||||
if (retVal == FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
DirEntry* rmDirEntry;
|
||||
|
||||
retVal = fromParent.removeDir(oldName, &rmDirEntry);
|
||||
|
||||
if (retVal == FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
std::string parentID = fromParent.getID();
|
||||
EntryInfo removedInfo;
|
||||
|
||||
rmDirEntry->getEntryInfo(parentID, 0, &removedInfo);
|
||||
|
||||
if (!hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
|
||||
updateRenamedDirInode(&removedInfo, toDirInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogContext(logContext).log(Log_CRITICAL,
|
||||
std::string("Failed to remove fromDir: ") + oldName +
|
||||
" Error: " + boost::lexical_cast<std::string>(retVal));
|
||||
}
|
||||
|
||||
SAFE_DELETE(rmDirEntry);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a file
|
||||
*/
|
||||
FhgfsOpsErr RenameV2MsgEx::renameFile(DirInode& fromParent, const std::string& oldName,
|
||||
EntryInfo* toDirInfo, const std::string& newName, std::string& unlinkedEntryID)
|
||||
{
|
||||
const char* logContext = "RenameV2MsgEx::renameFile";
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_INTERNAL;
|
||||
|
||||
EntryInfo fromFileInfo;
|
||||
bool getRes = fromParent.getFileEntryInfo(oldName, fromFileInfo);
|
||||
if (!getRes)
|
||||
{
|
||||
LOG_DEBUG(logContext, Log_SPAM, "Error: fromDir does not exist.");
|
||||
return FhgfsOpsErr_PATHNOTEXISTS;
|
||||
}
|
||||
|
||||
// when we are here we verified the file to be renamed is really a file
|
||||
|
||||
if (fromParent.getIsBuddyMirrored() && hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
|
||||
{
|
||||
retVal = FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the buffer is used to transfer all data of the dir-entry
|
||||
boost::scoped_array<char> serialBuf(new char[META_SERBUF_SIZE]);
|
||||
size_t usedSerialBufLen;
|
||||
|
||||
retVal = metaStore->moveRemoteFileBegin(
|
||||
fromParent, &fromFileInfo, serialBuf.get(), META_SERBUF_SIZE, &usedSerialBufLen);
|
||||
if (retVal != FhgfsOpsErr_SUCCESS)
|
||||
return retVal;
|
||||
|
||||
LOG_DEBUG(logContext, Log_SPAM, "Method: remote file move."); // debug in
|
||||
|
||||
StringVector xattrNames;
|
||||
if (Program::getApp()->getConfig()->getStoreClientXAttrs() && fromFileInfo.getIsInlined())
|
||||
{
|
||||
FhgfsOpsErr listXAttrRes;
|
||||
|
||||
std::tie(listXAttrRes, xattrNames) = MsgHelperXAttr::listxattr(&fromFileInfo);
|
||||
if (listXAttrRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
metaStore->moveRemoteFileComplete(fromParent, fromFileInfo.getEntryID());
|
||||
return FhgfsOpsErr_TOOBIG;
|
||||
}
|
||||
}
|
||||
|
||||
// Do the remote operation (insert and possible unlink of an existing toFile)
|
||||
retVal = remoteFileInsertAndUnlink(&fromFileInfo, toDirInfo, newName, serialBuf.get(),
|
||||
usedSerialBufLen, std::move(xattrNames), unlinkedEntryID);
|
||||
}
|
||||
|
||||
// finish local part of the owned file move operation (+ remove local file-link)
|
||||
if (retVal == FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
FhgfsOpsErr unlinkRes;
|
||||
|
||||
if (fromFileInfo.getIsInlined())
|
||||
{
|
||||
// We are not interested in the inode here , as we are not going to delete storage chunks.
|
||||
EntryInfo entryInfo;
|
||||
unsigned outNumHardlinks; // Not used here!
|
||||
unlinkRes = metaStore->unlinkFile(fromParent, oldName, &entryInfo, NULL, outNumHardlinks);
|
||||
}
|
||||
else
|
||||
{
|
||||
// only unlink dentry-by-filename for nonInlined inode(s)
|
||||
DirEntry oldDentry(oldName);
|
||||
if (fromParent.getDentry(oldName, oldDentry))
|
||||
unlinkRes = fromParent.unlinkDirEntry(oldName, &oldDentry, DirEntry_UNLINK_FILENAME);
|
||||
else
|
||||
unlinkRes = FhgfsOpsErr_PATHNOTEXISTS;
|
||||
}
|
||||
|
||||
if (unlikely (unlinkRes) )
|
||||
{
|
||||
LogContext(logContext).logErr(std::string("Error: Failed to unlink fromFile: ") +
|
||||
"DirID: " + fromFileInfo.getParentEntryID() + " "
|
||||
"entryName: " + oldName + ". " +
|
||||
"Remote toFile was successfully created.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!fromParent.getIsBuddyMirrored() || !hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
|
||||
metaStore->moveRemoteFileComplete(fromParent, fromFileInfo.getEntryID() );
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames a directory or a file (no moving between different directories involved).
|
||||
*/
|
||||
FhgfsOpsErr RenameV2MsgEx::renameInSameDir(DirInode& fromParent, const std::string& oldName,
|
||||
const std::string& toName, std::string& unlinkedEntryID)
|
||||
{
|
||||
const char* logContext = "RenameV2MsgEx::renameInSameDir";
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
/* we are passing here the very same fromParent pointer also a toParent pointer, which is
|
||||
* essential in order not to dead-lock */
|
||||
|
||||
std::unique_ptr<FileInode> unlinkInode; // inode belong to a possibly existing toName file
|
||||
DirEntry* overWrittenEntry = NULL;
|
||||
bool wasInlined; // to determine if overwritten inode was inlined or not
|
||||
|
||||
FhgfsOpsErr renameRes = metaStore->renameInSameDir(fromParent, oldName, toName,
|
||||
&unlinkInode, overWrittenEntry, wasInlined);
|
||||
|
||||
if (renameRes == FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
if (shouldFixTimestamps())
|
||||
{
|
||||
DirEntry dentry(toName);
|
||||
if (fromParent.getDentry(toName, dentry))
|
||||
{
|
||||
EntryInfo info;
|
||||
dentry.getEntryInfo(fromParent.getID(), 0, &info);
|
||||
|
||||
if (DirEntryType_ISDIR(info.getEntryType()))
|
||||
{
|
||||
auto dir = metaStore->referenceDir(info.getEntryID(), info.getIsBuddyMirrored(),
|
||||
true);
|
||||
if (dir)
|
||||
{
|
||||
fixInodeTimestamp(*dir, renamedInodeTimestamps);
|
||||
metaStore->releaseDir(dir->getID());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto [file, referenceRes] = metaStore->referenceFile(&info);
|
||||
if (file)
|
||||
{
|
||||
fixInodeTimestamp(*file, renamedInodeTimestamps, &info);
|
||||
metaStore->releaseFile(fromParent.getID(), file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (overWrittenEntry && !overWrittenEntry->getIsInodeInlined())
|
||||
{
|
||||
EntryInfo overWrittenEntryInfo;
|
||||
overWrittenEntry->getEntryInfo(fromParent.getID(), 0, &overWrittenEntryInfo);
|
||||
|
||||
if (wasInlined)
|
||||
{
|
||||
// if overwritten file previously had an inlined inode which got de-inlined because
|
||||
// it was IN_USE during rename operation - in this case, its inode should already be
|
||||
// linked to disposal directory and will be removed upon close()
|
||||
return renameRes;
|
||||
}
|
||||
else if (!hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
|
||||
{
|
||||
// if overwritten file had a non-inlined inode - update hardlink count and remove
|
||||
// inode and chunk files if link count becomes zero
|
||||
unlinkRemoteFileInode(&overWrittenEntryInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// handle unlinkInode for inlined inode(s)
|
||||
if (unlinkInode && !hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
|
||||
{
|
||||
unlinkedEntryID = unlinkInode->getEntryID();
|
||||
|
||||
FhgfsOpsErr chunkUnlinkRes = MsgHelperUnlink::unlinkChunkFiles(
|
||||
unlinkInode.release(), getMsgHeaderUserID() );
|
||||
|
||||
if (chunkUnlinkRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LogContext(logContext).logErr(std::string("Rename succeeded, but unlinking storage ") +
|
||||
"chunk files of the overwritten targetFileName (" + toName + ") failed. " +
|
||||
"Entry-ID: " + unlinkedEntryID);
|
||||
|
||||
// we can't do anything about it, so we won't even inform the user
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return renameRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: This method not only sends the insertion message, but also unlinks an overwritten local
|
||||
* file if it is contained in the response.
|
||||
*
|
||||
* @param serialBuf the inode values serialized into this buffer.
|
||||
*/
|
||||
FhgfsOpsErr RenameV2MsgEx::remoteFileInsertAndUnlink(EntryInfo* fromFileInfo, EntryInfo* toDirInfo,
|
||||
std::string newName, char* serialBuf, size_t serialBufLen, StringVector xattrs,
|
||||
std::string& unlinkedEntryID)
|
||||
{
|
||||
LogContext log("RenameV2MsgEx::remoteFileInsert");
|
||||
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
|
||||
|
||||
App* app = Program::getApp();
|
||||
NumNodeID toNodeID = toDirInfo->getOwnerNodeID();
|
||||
|
||||
LOG_DEBUG_CONTEXT(log, Log_DEBUG,
|
||||
"Inserting remote parentID: " + toDirInfo->getEntryID() + "; "
|
||||
"newName: '" + newName + "'; "
|
||||
"entryID: '" + fromFileInfo->getEntryID() + "'; "
|
||||
"node: " + toNodeID.str() );
|
||||
|
||||
// prepare request
|
||||
|
||||
MovingFileInsertMsg insertMsg(fromFileInfo, toDirInfo, newName, serialBuf, serialBufLen);
|
||||
|
||||
RequestResponseArgs rrArgs(NULL, &insertMsg, NETMSGTYPE_MovingFileInsertResp);
|
||||
|
||||
RequestResponseNode rrNode(toNodeID, app->getMetaNodes() );
|
||||
rrNode.setTargetStates(app->getMetaStateStore() );
|
||||
if (toDirInfo->getIsBuddyMirrored())
|
||||
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
|
||||
|
||||
// send request and receive response
|
||||
|
||||
MsgHelperXAttr::StreamXAttrState streamState(*fromFileInfo, std::move(xattrs));
|
||||
insertMsg.registerStreamoutHook(rrArgs, streamState);
|
||||
|
||||
FhgfsOpsErr requestRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
|
||||
if(requestRes != FhgfsOpsErr_SUCCESS)
|
||||
{ // communication error
|
||||
log.log(Log_WARNING,
|
||||
"Communication with metadata sever failed. nodeID: " + toNodeID.str() );
|
||||
return requestRes;
|
||||
}
|
||||
|
||||
// correct response type received
|
||||
const auto insertRespMsg = (MovingFileInsertRespMsg*)rrArgs.outRespMsg.get();
|
||||
retVal = insertRespMsg->getResult();
|
||||
|
||||
// handle unlink of chunk files for inlined inode(s)
|
||||
// handle unlink of inode for non-inlined inode(s)
|
||||
EntryInfo* overWrittenInfo = insertRespMsg->getOverWrittenEntryInfo();
|
||||
if (overWrittenInfo->getEntryType() != DirEntryType_INVALID)
|
||||
{
|
||||
if (overWrittenInfo->getIsInlined())
|
||||
{
|
||||
unsigned unlinkedInodeBufLen = insertRespMsg->getInodeBufLen();
|
||||
if (unlinkedInodeBufLen)
|
||||
{
|
||||
const char* unlinkedInodeBuf = insertRespMsg->getInodeBuf();
|
||||
FileInode* toUnlinkInode = new FileInode();
|
||||
|
||||
Deserializer des(unlinkedInodeBuf, unlinkedInodeBufLen);
|
||||
toUnlinkInode->deserializeMetaData(des);
|
||||
if(unlikely(!des.good()))
|
||||
{ // deserialization of received inode failed (should never happen)
|
||||
log.logErr("Failed to deserialize unlinked file inode. nodeID: " + toNodeID.str() );
|
||||
delete(toUnlinkInode);
|
||||
}
|
||||
else if (toUnlinkInode->getIsInlined())
|
||||
{
|
||||
MsgHelperUnlink::unlinkChunkFiles(
|
||||
toUnlinkInode, getMsgHeaderUserID() ); // destructs toUnlinkInode
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unlinkRemoteFileInode(overWrittenInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (retVal != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
// error: remote file not inserted
|
||||
LOG_DEBUG_CONTEXT(log, Log_NOTICE,
|
||||
"Metadata server was unable to insert file. "
|
||||
"nodeID: " + toNodeID.str() + "; "
|
||||
"Error: " + boost::lexical_cast<std::string>(retVal));
|
||||
}
|
||||
else
|
||||
{
|
||||
// success: remote file inserted
|
||||
LOG_DEBUG_CONTEXT(log, Log_DEBUG, "Metadata server inserted file. "
|
||||
"nodeID: " + toNodeID.str() );
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a remote directory dir-entry
|
||||
*/
|
||||
FhgfsOpsErr RenameV2MsgEx::remoteDirInsert(EntryInfo* toDirInfo, const std::string& newName,
|
||||
char* serialBuf, size_t serialBufLen)
|
||||
{
|
||||
LogContext log("RenameV2MsgEx::remoteDirInsert");
|
||||
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
|
||||
|
||||
App* app = Program::getApp();
|
||||
NumNodeID toNodeID = toDirInfo->getOwnerNodeID();
|
||||
|
||||
LOG_DEBUG_CONTEXT(log, Log_DEBUG,
|
||||
"Inserting remote parentID: " + toDirInfo->getEntryID() + "; "
|
||||
"newName: '" + newName + "'; "
|
||||
"node: " + toNodeID.str() );
|
||||
|
||||
// prepare request
|
||||
|
||||
MovingDirInsertMsg insertMsg(toDirInfo, newName, serialBuf, serialBufLen);
|
||||
|
||||
RequestResponseArgs rrArgs(NULL, &insertMsg, NETMSGTYPE_MovingDirInsertResp);
|
||||
|
||||
RequestResponseNode rrNode(toNodeID, app->getMetaNodes() );
|
||||
rrNode.setTargetStates(app->getMetaStateStore() );
|
||||
if (toDirInfo->getIsBuddyMirrored())
|
||||
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
|
||||
|
||||
|
||||
// send request and receive response
|
||||
|
||||
FhgfsOpsErr requestRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
|
||||
if(requestRes != FhgfsOpsErr_SUCCESS)
|
||||
{ // communication error
|
||||
log.log(Log_WARNING, "Communication with metadata server failed. nodeID: " + toNodeID.str() );
|
||||
return requestRes;
|
||||
}
|
||||
|
||||
// correct response type received
|
||||
const auto insertRespMsg = (const MovingDirInsertRespMsg*)rrArgs.outRespMsg.get();
|
||||
|
||||
retVal = insertRespMsg->getResult();
|
||||
if(retVal != FhgfsOpsErr_SUCCESS)
|
||||
{ // error: remote dir not inserted
|
||||
LOG_DEBUG_CONTEXT(log, Log_NOTICE,
|
||||
"Metdata server was unable to insert directory. "
|
||||
"nodeID: " + toNodeID.str() + "; "
|
||||
"Error: " + boost::lexical_cast<std::string>(retVal));
|
||||
}
|
||||
else
|
||||
{
|
||||
// success: remote dir inserted
|
||||
LOG_DEBUG_CONTEXT(log, Log_DEBUG,
|
||||
"Metadata server inserted directory. "
|
||||
"nodeID: " + toNodeID.str() );
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new parent information to the inode of renamedDirEntryInfo
|
||||
*/
|
||||
FhgfsOpsErr RenameV2MsgEx::updateRenamedDirInode(EntryInfo* renamedDirEntryInfo,
|
||||
EntryInfo* toDirInfo)
|
||||
{
|
||||
LogContext log("RenameV2MsgEx::updateRenamedDirInode");
|
||||
|
||||
const std::string& parentEntryID = toDirInfo->getEntryID();
|
||||
renamedDirEntryInfo->setParentEntryID(parentEntryID);
|
||||
|
||||
App* app = Program::getApp();
|
||||
NumNodeID toNodeID = renamedDirEntryInfo->getOwnerNodeID();
|
||||
|
||||
LOG_DEBUG_CONTEXT(log, Log_DEBUG,
|
||||
"Update remote inode: " + renamedDirEntryInfo->getEntryID() + "; "
|
||||
"node: " + toNodeID.str() );
|
||||
|
||||
// prepare request
|
||||
|
||||
UpdateDirParentMsg updateMsg(renamedDirEntryInfo, toDirInfo->getOwnerNodeID() );
|
||||
|
||||
RequestResponseArgs rrArgs(NULL, &updateMsg, NETMSGTYPE_UpdateDirParentResp);
|
||||
|
||||
RequestResponseNode rrNode(toNodeID, app->getMetaNodes() );
|
||||
rrNode.setTargetStates(app->getMetaStateStore() );
|
||||
if (renamedDirEntryInfo->getIsBuddyMirrored())
|
||||
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
|
||||
|
||||
// send request and receive response
|
||||
|
||||
FhgfsOpsErr requestRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
|
||||
if(requestRes != FhgfsOpsErr_SUCCESS)
|
||||
{ // communication error
|
||||
log.log(Log_WARNING,
|
||||
"Communication with metadata server failed. nodeID: " + toNodeID.str() );
|
||||
return requestRes;
|
||||
}
|
||||
|
||||
// correct response type received
|
||||
const auto updateRespMsg = (const UpdateDirParentRespMsg*)rrArgs.outRespMsg.get();
|
||||
|
||||
FhgfsOpsErr retVal = (FhgfsOpsErr)updateRespMsg->getValue();
|
||||
if(retVal != FhgfsOpsErr_SUCCESS)
|
||||
{ // error
|
||||
LOG_DEBUG_CONTEXT(log, Log_NOTICE,
|
||||
"Failed to update ParentEntryID: " + renamedDirEntryInfo->getEntryID() + "; "
|
||||
"nodeID: " + toNodeID.str() + "; "
|
||||
"Error: " + boost::lexical_cast<std::string>(retVal));
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle unlink for a non-inlined inode
|
||||
* Decrement hardLink count, if linkCount becomes zero then remove inode and chunk files
|
||||
*/
|
||||
FhgfsOpsErr RenameV2MsgEx::unlinkRemoteFileInode(EntryInfo* entryInfo)
|
||||
{
|
||||
const char* logContext = "Unlink remote file inode";
|
||||
App* app = Program::getApp();
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
|
||||
|
||||
NumNodeID ownerNodeID = entryInfo->getOwnerNodeID();
|
||||
|
||||
UnlinkLocalFileInodeMsg unlinkInodeMsg(entryInfo);
|
||||
RequestResponseArgs rrArgs(NULL, &unlinkInodeMsg, NETMSGTYPE_UnlinkLocalFileInodeResp);
|
||||
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes());
|
||||
|
||||
if (entryInfo->getIsBuddyMirrored())
|
||||
rrNode.setMirrorInfo(Program::getApp()->getMetaBuddyGroupMapper(), false);
|
||||
|
||||
do
|
||||
{
|
||||
retVal = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
|
||||
if (unlikely(retVal != FhgfsOpsErr_SUCCESS))
|
||||
{
|
||||
LogContext(logContext).log(Log_WARNING,
|
||||
"Communication with metadata server failed. "
|
||||
"nodeID: " + ownerNodeID.str() + "; " +
|
||||
"entryID: " + entryInfo->getEntryID().c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
// response received
|
||||
const auto unlinkFileInodeRespMsg = (UnlinkLocalFileInodeRespMsg*) rrArgs.outRespMsg.get();
|
||||
retVal = unlinkFileInodeRespMsg->getResult();
|
||||
if (retVal != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
// error: either inode file doesn't exists or some other error happened
|
||||
LogContext(logContext).logErr("unlink of inode failed! "
|
||||
"nodeID: " + ownerNodeID.str() + "; " +
|
||||
"entryID: " + entryInfo->getEntryID().c_str());
|
||||
break;
|
||||
}
|
||||
} while (false);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
std::pair<FhgfsOpsErr, unsigned> RenameV2MsgEx::getLinkCountForMovedEntry(EntryInfo* entryInfo)
|
||||
{
|
||||
const char* logContext = "RenameV2Msg (Get Link Count)";
|
||||
|
||||
App* app = Program::getApp();
|
||||
FhgfsOpsErr statRes = FhgfsOpsErr_SUCCESS;
|
||||
unsigned linkCount = 0;
|
||||
|
||||
NumNodeID ownerNodeID = entryInfo->getOwnerNodeID();
|
||||
bool isLocalOwner = ((!isMirrored() && ownerNodeID == app->getLocalNode().getNumID()) ||
|
||||
(isMirrored() && ownerNodeID.val() == app->getMetaBuddyGroupMapper()->getLocalGroupID()));
|
||||
|
||||
if (isLocalOwner)
|
||||
{
|
||||
StatData statData;
|
||||
statRes = MsgHelperStat::stat(entryInfo, true, getMsgHeaderUserID(), statData);
|
||||
|
||||
if (statRes == FhgfsOpsErr_SUCCESS)
|
||||
linkCount = statData.getNumHardlinks();
|
||||
else
|
||||
LogContext(logContext).logErr("Stat Failed!. entryID: " + entryInfo->getEntryID());
|
||||
}
|
||||
else
|
||||
{
|
||||
// send StatMsg to remote meta node/buddygroup
|
||||
StatMsg statMsg(entryInfo);
|
||||
RequestResponseArgs rrArgs(NULL, &statMsg, NETMSGTYPE_StatResp);
|
||||
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes());
|
||||
rrNode.setTargetStates(app->getMetaStateStore());
|
||||
|
||||
if (entryInfo->getIsBuddyMirrored())
|
||||
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
|
||||
|
||||
do
|
||||
{
|
||||
FhgfsOpsErr resp = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
if (unlikely(resp != FhgfsOpsErr_SUCCESS))
|
||||
{
|
||||
LogContext(logContext).logErr("Communication with metadata server failed. "
|
||||
"nodeID: " + ownerNodeID.str());
|
||||
statRes = resp;
|
||||
break;
|
||||
}
|
||||
|
||||
// response received
|
||||
const auto statRespMsg = (StatRespMsg*) rrArgs.outRespMsg.get();
|
||||
statRes = (FhgfsOpsErr) statRespMsg->getResult();
|
||||
if (statRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LogContext(logContext).logErr("Stat Failed!. nodeID: " + ownerNodeID.str()
|
||||
+ "; entryID: " + entryInfo->getEntryID());
|
||||
break;
|
||||
}
|
||||
|
||||
// success
|
||||
linkCount = statRespMsg->getStatData()->getNumHardlinks();
|
||||
} while (false);
|
||||
}
|
||||
|
||||
return {statRes, linkCount};
|
||||
}
|
||||
100
meta/source/net/message/storage/moving/RenameV2MsgEx.h
Normal file
100
meta/source/net/message/storage/moving/RenameV2MsgEx.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/moving/RenameMsg.h>
|
||||
#include <common/net/message/storage/moving/RenameRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
#include <storage/DirEntry.h>
|
||||
#include <storage/MetaStore.h>
|
||||
|
||||
struct RenameV2Locks
|
||||
{
|
||||
HashDirLock toFileHashLock;
|
||||
|
||||
ParentNameLock fromNameLock;
|
||||
ParentNameLock toNameLock;
|
||||
FileIDLock fromDirLock;
|
||||
FileIDLock toDirLock;
|
||||
// source file must be locked because concurrent modifications of file attributes may
|
||||
// race with the moving operation between two servers.
|
||||
FileIDLock fromFileLockF;
|
||||
FileIDLock fromFileLockD;
|
||||
// if target exists, the target file must be unlocked to exclude concurrent operations on
|
||||
// target (eg close, setxattr, ...)
|
||||
FileIDLock unlinkedFileLock;
|
||||
|
||||
RenameV2Locks() = default;
|
||||
|
||||
RenameV2Locks(const RenameV2Locks&) = delete;
|
||||
RenameV2Locks& operator=(const RenameV2Locks&) = delete;
|
||||
|
||||
RenameV2Locks(RenameV2Locks&& other)
|
||||
{
|
||||
swap(other);
|
||||
}
|
||||
|
||||
RenameV2Locks& operator=(RenameV2Locks&& other)
|
||||
{
|
||||
RenameV2Locks(std::move(other)).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(RenameV2Locks& other)
|
||||
{
|
||||
std::swap(toFileHashLock, other.toFileHashLock);
|
||||
std::swap(fromNameLock, other.fromNameLock);
|
||||
std::swap(toNameLock, other.toNameLock);
|
||||
std::swap(fromDirLock, other.fromDirLock);
|
||||
std::swap(toDirLock, other.toDirLock);
|
||||
std::swap(fromFileLockF, other.fromFileLockF);
|
||||
std::swap(fromFileLockD, other.fromFileLockD);
|
||||
std::swap(unlinkedFileLock, other.unlinkedFileLock);
|
||||
}
|
||||
};
|
||||
|
||||
class RenameV2MsgEx : public MirroredMessage<RenameMsg, RenameV2Locks>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<RenameRespMsg, NETMSGTYPE_Rename> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
RenameV2Locks lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getFromDirInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
FhgfsOpsErr movingPerform(DirInode& fromParent, const std::string& oldName,
|
||||
DirEntryType entryType, EntryInfo* toDirInfo, const std::string& newName,
|
||||
std::string& unlinkedEntryID);
|
||||
|
||||
FhgfsOpsErr renameInSameDir(DirInode& fromParent, const std::string& oldName,
|
||||
const std::string& toName, std::string& unlinkedEntryID);
|
||||
FhgfsOpsErr renameDir(DirInode& fromParent, const std::string& oldName,
|
||||
EntryInfo* toDirInfo, const std::string& newName);
|
||||
FhgfsOpsErr renameFile(DirInode& fromParent, const std::string& oldName, EntryInfo* toDirInfo,
|
||||
const std::string& newName, std::string& unlinkedEntryID);
|
||||
|
||||
FhgfsOpsErr remoteFileInsertAndUnlink(EntryInfo* fromFileInfo, EntryInfo* toDirInfo,
|
||||
const std::string newName, char* serialBuf, size_t serialBufLen,
|
||||
StringVector xattrs, std::string& unlinkedEntryID);
|
||||
FhgfsOpsErr remoteDirInsert(EntryInfo* toDirInfo, const std::string& newName,
|
||||
char* serialBuf, size_t serialBufLen);
|
||||
FhgfsOpsErr updateRenamedDirInode(EntryInfo* renamedDirEntryInfo, EntryInfo* toDirInfo);
|
||||
FhgfsOpsErr unlinkRemoteFileInode(EntryInfo* entryInfo);
|
||||
std::pair<FhgfsOpsErr, unsigned> getLinkCountForMovedEntry(EntryInfo* entryInfo);
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<RenameRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "RenameV2MsgEx/forward"; }
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
#include <common/app/log/LogContext.h>
|
||||
#include <common/net/message/storage/quota/SetExceededQuotaRespMsg.h>
|
||||
#include <common/storage/StoragePool.h>
|
||||
#include <program/Program.h>
|
||||
|
||||
#include "SetExceededQuotaMsgEx.h"
|
||||
|
||||
bool SetExceededQuotaMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
LogContext log("SetExceededQuotaMsgEx incoming");
|
||||
|
||||
bool retVal = true;
|
||||
FhgfsOpsErr errorCode = FhgfsOpsErr_SUCCESS;
|
||||
|
||||
if(Program::getApp()->getConfig()->getQuotaEnableEnforcement() )
|
||||
{
|
||||
// get the storage pool for which quota is exceeded
|
||||
StoragePoolPtr storagePool =
|
||||
Program::getApp()->getStoragePoolStore()->getPool(getStoragePoolId());
|
||||
|
||||
if (!storagePool)
|
||||
{
|
||||
LOG(QUOTA, WARNING, "Couldn't set exceeded quota, "
|
||||
"because requested storage pool doesn't exist on metadata server.",
|
||||
("storagePoolId", getStoragePoolId()));
|
||||
|
||||
errorCode = FhgfsOpsErr_UNKNOWNPOOL;
|
||||
|
||||
goto send_response;
|
||||
}
|
||||
|
||||
// set exceeded quota info for all of its targets
|
||||
UInt16Set targetIds = storagePool->getTargets();
|
||||
|
||||
for (auto targetId : targetIds)
|
||||
{
|
||||
// update exceeded quota
|
||||
ExceededQuotaStorePtr exQuotaStore =
|
||||
Program::getApp()->getExceededQuotaStores()->get(targetId);
|
||||
if (!exQuotaStore)
|
||||
{
|
||||
LOG(QUOTA, ERR, "Could not access exceeded quota store.", targetId);
|
||||
errorCode = FhgfsOpsErr_UNKNOWNTARGET;
|
||||
goto send_response;
|
||||
}
|
||||
exQuotaStore->updateExceededQuota(getExceededQuotaIDs(), getQuotaDataType(),
|
||||
getExceededType());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
log.log(Log_ERR, "Unable to set exceeded quota IDs. Configuration problem detected. "
|
||||
"The management daemon on " + ctx.peerName() + " has quota enforcement enabled, "
|
||||
"but not this storage daemon. Fix this configuration problem or quota enforcement will "
|
||||
"not work correctly. If quota enforcement settings have changed recently in the "
|
||||
"mgmtd configuration, please restart all BeeGFS services.");
|
||||
|
||||
errorCode = FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
send_response:
|
||||
ctx.sendResponse(SetExceededQuotaRespMsg(errorCode) );
|
||||
|
||||
return retVal;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <common/net/message/storage/quota/SetExceededQuotaMsg.h>
|
||||
#include <common/Common.h>
|
||||
|
||||
|
||||
class SetExceededQuotaMsgEx : public SetExceededQuotaMsg
|
||||
{
|
||||
public:
|
||||
virtual bool processIncoming(ResponseContext& ctx);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user