New upstream version 8.1.0

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

View 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;
}

View 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);
};

View 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;
}

View 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);
};

View 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);
}

View 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"; }
};

View 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;
}

View 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"; }
};

View 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));
}

View 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"; }
};

View 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));
}

View 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"; }
};

View 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);
}

View File

@@ -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"; }
};

View 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);
}

View 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"; }
};

View 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;
}

View 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"; }
};

View 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);
}

View 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"; }
};

View File

@@ -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);
}

View File

@@ -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"; }
};

View File

@@ -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);
}

View 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"; }
};

View 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);
}

View 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"; }
};

View 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;
}

View 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);
};

View File

@@ -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);
}

View File

@@ -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"; }
};

View File

@@ -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;
}

View File

@@ -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"; }
};

View File

@@ -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);
}

View File

@@ -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"; }
};

View 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);
}

View 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"; }
};

View 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);
}

View 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;
};

View 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);
}

View 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;
};

View File

@@ -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);
}

View File

@@ -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;
};

View 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);
}

View 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"; }
};

View File

@@ -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);
}

View File

@@ -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"; }
};

View 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;
}

View 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);
};

View 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);
}

View 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"; }
};

View 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);
}

View 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"; }
};

View 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);
}

View 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"; }
};

View File

@@ -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);
}

View File

@@ -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";
}
};

View File

@@ -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;
}

View File

@@ -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);
};

View File

@@ -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;
}

View 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);
};

View 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;
}

View 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);
};

View 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);
}

View 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;
};

View File

@@ -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;
}

View File

@@ -0,0 +1,10 @@
#pragma once
#include <common/net/message/storage/mirroring/GetMetaResyncStatsMsg.h>
class GetMetaResyncStatsMsgEx : public GetMetaResyncStatsMsg
{
public:
virtual bool processIncoming(ResponseContext& ctx);
};

View File

@@ -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;
}

View File

@@ -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();
};

View File

@@ -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;
}

View File

@@ -0,0 +1,10 @@
#pragma once
#include <common/net/message/storage/mirroring/ResyncSessionStoreMsg.h>
class ResyncSessionStoreMsgEx : public ResyncSessionStoreMsg
{
public:
virtual bool processIncoming(ResponseContext& ctx);
};

View File

@@ -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;
}

View File

@@ -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);
};

View File

@@ -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();
}

View File

@@ -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();
};

View File

@@ -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);
}

View File

@@ -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"; }
};

View 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);
}

View 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"; }
};

View 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};
}

View 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"; }
};

View File

@@ -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;
}

View File

@@ -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);
};