New upstream version 8.1.0
This commit is contained in:
303
meta/source/net/message/storage/creating/HardlinkMsgEx.cpp
Normal file
303
meta/source/net/message/storage/creating/HardlinkMsgEx.cpp
Normal file
@@ -0,0 +1,303 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/creating/HardlinkRespMsg.h>
|
||||
#include <common/net/message/storage/creating/MoveFileInodeMsg.h>
|
||||
#include <common/net/message/storage/creating/MoveFileInodeRespMsg.h>
|
||||
#include <common/net/message/storage/attribs/SetAttrMsg.h>
|
||||
#include <common/net/message/storage/attribs/SetAttrRespMsg.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <components/FileEventLogger.h>
|
||||
#include "HardlinkMsgEx.h"
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock, ParentNameLock, FileIDLock> HardlinkMsgEx::lock(
|
||||
EntryLockStore& store)
|
||||
{
|
||||
// NOTE: normally we'd need to also lock on the MDS holding the destination file,
|
||||
// but we don't support hardlinks to different servers, yet
|
||||
ParentNameLock fromLock;
|
||||
ParentNameLock toLock;
|
||||
|
||||
FileIDLock fileLock;
|
||||
|
||||
FileIDLock dirLock(&store, getToDirInfo()->getEntryID(), true);
|
||||
|
||||
// take care about lock ordering! see MirroredMessage::lock()
|
||||
if (getFromDirInfo()->getEntryID() < getToDirInfo()->getEntryID()
|
||||
|| (getFromDirInfo()->getEntryID() == getToDirInfo()->getEntryID()
|
||||
&& getFromName() < getToName()))
|
||||
{
|
||||
fromLock = {&store, getFromDirInfo()->getEntryID(), getFromName()};
|
||||
toLock = {&store, getToDirInfo()->getEntryID(), getToName()};
|
||||
}
|
||||
else if (getFromDirInfo()->getEntryID() == getToDirInfo()->getEntryID()
|
||||
&& getFromName() == getToName())
|
||||
{
|
||||
fromLock = {&store, getFromDirInfo()->getEntryID(), getFromName()};
|
||||
}
|
||||
else
|
||||
{
|
||||
toLock = {&store, getToDirInfo()->getEntryID(), getToName()};
|
||||
fromLock = {&store, getFromDirInfo()->getEntryID(), getFromName()};
|
||||
}
|
||||
|
||||
// We need to lock the inode because hardlinking modifies the link count.
|
||||
// If the file inode is present on the current meta node or buddy-group,
|
||||
// then acquire the inodeLock.
|
||||
App* app = Program::getApp();
|
||||
NumNodeID ownerNodeID = getFromInfo()->getOwnerNodeID();
|
||||
bool isLocalInode = ((!isMirrored() && ownerNodeID == app->getLocalNode().getNumID()) ||
|
||||
(isMirrored() && ownerNodeID.val() == app->getMetaBuddyGroupMapper()->getLocalGroupID()));
|
||||
|
||||
if (isLocalInode)
|
||||
fileLock = {&store, getFromInfo()->getEntryID(), true};
|
||||
|
||||
return std::make_tuple(
|
||||
std::move(dirLock),
|
||||
std::move(fromLock),
|
||||
std::move(toLock),
|
||||
std::move(fileLock));
|
||||
}
|
||||
|
||||
bool HardlinkMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
LOG_DBG(GENERAL, DEBUG, "",
|
||||
("fromDirID", getFromDirInfo()->getEntryID()),
|
||||
("fromID", getFromInfo()->getEntryID()),
|
||||
("fromName", getFromName()),
|
||||
("toDirID", getToDirInfo()->getEntryID()),
|
||||
("toName", getToName()),
|
||||
("buddyMirrored", getToDirInfo()->getIsBuddyMirrored()));
|
||||
|
||||
BaseType::processIncoming(ctx);
|
||||
|
||||
updateNodeOp(ctx, MetaOpCounter_HARDLINK);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> HardlinkMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
const char* logContext = "create hard link";
|
||||
|
||||
App* app = Program::getApp();
|
||||
MetaStore* metaStore = app->getMetaStore();
|
||||
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
|
||||
unsigned updatedLinkCount = 0;
|
||||
|
||||
// reference directory where hardlink needs to be created
|
||||
DirInode* toDir = metaStore->referenceDir(getToDirInfo()->getEntryID(),
|
||||
getToDirInfo()->getIsBuddyMirrored(), true);
|
||||
|
||||
// check if file dentry already exists with same name as hardlink
|
||||
// return error if link already exists
|
||||
if (likely(toDir))
|
||||
{
|
||||
DirEntry newLink(getToName());
|
||||
if (toDir->getFileDentry(getToName(), newLink))
|
||||
{
|
||||
metaStore->releaseDir(toDir->getID());
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_EXISTS);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogContext(logContext).logErr(std::string("target directory for hard-link doesn't exist,"
|
||||
"dirname: " + getToDirInfo()->getFileName()));
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
|
||||
}
|
||||
|
||||
// Prevent hardlinks across directories with different buddy mirror configurations
|
||||
if (getFromDirInfo()->getIsBuddyMirrored() != getToDirInfo()->getIsBuddyMirrored())
|
||||
{
|
||||
LogContext(logContext).logErr("Hardlinks not supported across directories with different "
|
||||
"buddy mirror config setting. SourceDirID: " + getFromDirInfo()->getEntryID() +
|
||||
"; TargetDirID: " + getToDirInfo()->getEntryID());
|
||||
metaStore->releaseDir(toDir->getID());
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_NOTSUPP);
|
||||
}
|
||||
|
||||
// check whether local node/group owns source file's inode or not
|
||||
NumNodeID ownerNodeID = getFromInfo()->getOwnerNodeID();
|
||||
bool isLocalOwner = ((!isMirrored() && ownerNodeID == app->getLocalNode().getNumID()) ||
|
||||
(isMirrored() && ownerNodeID.val() == app->getMetaBuddyGroupMapper()->getLocalGroupID()));
|
||||
|
||||
if (isLocalOwner)
|
||||
{
|
||||
std::tie(retVal, updatedLinkCount) = metaStore->makeNewHardlink(getFromInfo());
|
||||
}
|
||||
else if (!isSecondary)
|
||||
{
|
||||
MoveFileInodeMsg deinlineMsg(getFromInfo(), MODE_DEINLINE, true);
|
||||
|
||||
RequestResponseArgs rrArgs(NULL, &deinlineMsg, NETMSGTYPE_MoveFileInodeResp);
|
||||
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes());
|
||||
rrNode.setTargetStates(app->getMetaStateStore());
|
||||
|
||||
if (getFromInfo()->getIsBuddyMirrored())
|
||||
{
|
||||
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
// send request to other MDs and receive response
|
||||
FhgfsOpsErr resp = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
|
||||
if (unlikely(resp != FhgfsOpsErr_SUCCESS))
|
||||
{
|
||||
// error
|
||||
LogContext(logContext).logErr("Communication with metadata server failed. "
|
||||
"nodeID: " + ownerNodeID.str());
|
||||
|
||||
retVal = resp;
|
||||
break;
|
||||
}
|
||||
|
||||
// response received
|
||||
const auto moveFileInodeRespMsg = (MoveFileInodeRespMsg*) rrArgs.outRespMsg.get();
|
||||
FhgfsOpsErr res = moveFileInodeRespMsg->getResult();
|
||||
if (res != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
// error: either source file not exists or deinline operation failed
|
||||
LogContext(logContext).logErr("verify and move file inode failed! nodeID: "
|
||||
+ ownerNodeID.str() + "; entryID: " + getFromInfo()->getEntryID());
|
||||
|
||||
retVal = res;
|
||||
break;
|
||||
}
|
||||
|
||||
// success
|
||||
retVal = res;
|
||||
updatedLinkCount = moveFileInodeRespMsg->getHardlinkCount();
|
||||
} while (false);
|
||||
}
|
||||
|
||||
// If verify/deinline inode and link count update is successful - only then create new
|
||||
// dentry in "toDir" with entryID and ownerNodeID of "fromFile" which might reside
|
||||
// on another meta node/buddy-group
|
||||
if (retVal == FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
DirEntry newHardlink(DirEntryType_REGULARFILE, getToName(),
|
||||
getFromInfo()->getEntryID(), getFromInfo()->getOwnerNodeID());
|
||||
|
||||
newHardlink.removeDentryFeatureFlag(DENTRY_FEATURE_INODE_INLINE);
|
||||
|
||||
// buddy mirroring is inherited from parent directory
|
||||
if (toDir->getIsBuddyMirrored())
|
||||
newHardlink.setBuddyMirrorFeatureFlag();
|
||||
|
||||
FhgfsOpsErr makeRes = toDir->makeDirEntry(newHardlink);
|
||||
|
||||
if (makeRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
// compensate link count if dentry create fails
|
||||
//
|
||||
// for a remote inode, Use SetAttrMsg to decrease link count
|
||||
// since its a mirrored message so it will update link count on both primary and
|
||||
// secondary hence don't send this message from secondary buddy
|
||||
if (!isSecondary && !isLocalOwner)
|
||||
incDecRemoteLinkCount(ownerNodeID, false);
|
||||
else
|
||||
metaStore->incDecLinkCount(getFromInfo(), -1);
|
||||
|
||||
retVal = makeRes;
|
||||
}
|
||||
}
|
||||
|
||||
if (retVal == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
{
|
||||
fixInodeTimestamp(*toDir, dirTimestamps);
|
||||
|
||||
if (isLocalOwner)
|
||||
{
|
||||
auto [file, referenceRes] = metaStore->referenceFile(getFromInfo());
|
||||
if (file)
|
||||
{
|
||||
fixInodeTimestamp(*file, fileTimestamps, getFromInfo());
|
||||
metaStore->releaseFile(getFromInfo()->getParentEntryID(), file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
metaStore->releaseDir(toDir->getID());
|
||||
|
||||
if (!isSecondary && retVal == FhgfsOpsErr_SUCCESS && app->getFileEventLogger() && getFileEvent())
|
||||
{
|
||||
EventContext eventCtx = makeEventContext(getFromInfo(), getFromInfo()->getParentEntryID(),
|
||||
getMsgHeaderUserID(), "", updatedLinkCount, isSecondary);
|
||||
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(retVal);
|
||||
}
|
||||
|
||||
FhgfsOpsErr HardlinkMsgEx::incDecRemoteLinkCount(NumNodeID const& ownerNodeID, bool increment)
|
||||
{
|
||||
const char* logContext = "incDecRemoteLinkCount";
|
||||
App* app = Program::getApp();
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_INTERNAL;
|
||||
|
||||
SettableFileAttribs dummyAttrib = {};
|
||||
SetAttrMsg msg(getFromInfo(), 0, &dummyAttrib);
|
||||
|
||||
if (increment)
|
||||
{
|
||||
msg.addMsgHeaderFeatureFlag(SETATTRMSG_FLAG_INCR_NLINKCNT);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.addMsgHeaderFeatureFlag(SETATTRMSG_FLAG_DECR_NLINKCNT);
|
||||
}
|
||||
|
||||
RequestResponseArgs rrArgs(NULL, &msg, NETMSGTYPE_SetAttrResp);
|
||||
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes());
|
||||
|
||||
rrNode.setTargetStates(app->getMetaStateStore());
|
||||
|
||||
if (getFromInfo()->getIsBuddyMirrored())
|
||||
{
|
||||
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
// send request to other MDs and receive response
|
||||
FhgfsOpsErr resp = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
|
||||
if (unlikely(resp != FhgfsOpsErr_SUCCESS))
|
||||
{
|
||||
// error
|
||||
LogContext(logContext).log(Log_WARNING,
|
||||
"Communication with metadata server failed. "
|
||||
"nodeID: " + ownerNodeID.str());
|
||||
retVal = resp;
|
||||
break;
|
||||
}
|
||||
|
||||
// response received
|
||||
const auto incLinkCountResp = (SetAttrRespMsg*) rrArgs.outRespMsg.get();
|
||||
FhgfsOpsErr res = static_cast<FhgfsOpsErr>(incLinkCountResp->getValue());
|
||||
if (res != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
// error: either source file not exists or nlink count increment failed
|
||||
std::string operationType = (increment ? "increase": "decrease");
|
||||
LogContext(logContext).logErr("nLink count " + operationType
|
||||
+ " failed! nodeID: " + ownerNodeID.str() + "; "
|
||||
+ "entryID: " + getFromInfo()->getEntryID());
|
||||
|
||||
retVal = res;
|
||||
break;
|
||||
}
|
||||
|
||||
// success
|
||||
retVal = res;
|
||||
} while (false);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void HardlinkMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_HardlinkResp);
|
||||
}
|
||||
38
meta/source/net/message/storage/creating/HardlinkMsgEx.h
Normal file
38
meta/source/net/message/storage/creating/HardlinkMsgEx.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/creating/HardlinkMsg.h>
|
||||
#include <common/net/message/storage/creating/HardlinkRespMsg.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <storage/DirEntry.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
class HardlinkMsgEx : public MirroredMessage<HardlinkMsg,
|
||||
std::tuple<FileIDLock, ParentNameLock, ParentNameLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<HardlinkRespMsg, NETMSGTYPE_Hardlink> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock, ParentNameLock, FileIDLock>
|
||||
lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getFromInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<HardlinkRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
FhgfsOpsErr incDecRemoteLinkCount(NumNodeID const& ownerNodeID, bool increment);
|
||||
const char* mirrorLogContext() const override { return "HardlinkMsgEx/forward"; }
|
||||
};
|
||||
|
||||
|
||||
411
meta/source/net/message/storage/creating/MkDirMsgEx.cpp
Normal file
411
meta/source/net/message/storage/creating/MkDirMsgEx.cpp
Normal file
@@ -0,0 +1,411 @@
|
||||
#include <common/components/streamlistenerv2/IncomingPreprocessedMsgWork.h>
|
||||
#include <common/net/message/control/GenericResponseMsg.h>
|
||||
#include <common/net/message/storage/creating/MkLocalDirMsg.h>
|
||||
#include <common/net/message/storage/creating/MkLocalDirRespMsg.h>
|
||||
#include <common/net/message/storage/creating/MkDirRespMsg.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <components/FileEventLogger.h>
|
||||
#include <components/ModificationEventFlusher.h>
|
||||
#include <program/Program.h>
|
||||
#include <storage/PosixACL.h>
|
||||
#include "RmDirMsgEx.h"
|
||||
#include "MkDirMsgEx.h"
|
||||
|
||||
|
||||
bool MkDirMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
|
||||
entryID = StorageTk::generateFileID(app->getLocalNode().getNumID());
|
||||
|
||||
BaseType::processIncoming(ctx);
|
||||
|
||||
// update operation counters
|
||||
updateNodeOp(ctx, MetaOpCounter_MKDIR);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> MkDirMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
auto result = isSecondary
|
||||
? mkDirSecondary()
|
||||
: mkDirPrimary(ctx);
|
||||
|
||||
if (result && result->getResult() != FhgfsOpsErr_SUCCESS)
|
||||
LOG_DBG(GENERAL, DEBUG, "Failed to create directory",
|
||||
("parentID", getParentInfo()->getEntryID()),
|
||||
("newDirName", getNewDirName()),
|
||||
("error", result->getResult()));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock, ParentNameLock> MkDirMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
HashDirLock hashLock;
|
||||
|
||||
// during resync we must lock the hash dir of the new inode even if the inode will be created on
|
||||
// a different node because we select the target node for the inode only after we have locked our
|
||||
// structures.
|
||||
if (resyncJob && resyncJob->isRunning())
|
||||
hashLock = {&store, MetaStorageTk::getMetaInodeHash(entryID)};
|
||||
|
||||
FileIDLock dirLock(&store, getParentInfo()->getEntryID(), true);
|
||||
ParentNameLock dentryLock(&store, getParentInfo()->getEntryID(), getNewDirName());
|
||||
|
||||
return std::make_tuple(std::move(hashLock), std::move(dirLock), std::move(dentryLock));
|
||||
}
|
||||
|
||||
std::unique_ptr<MkDirMsgEx::ResponseState> MkDirMsgEx::mkDirPrimary(ResponseContext& ctx)
|
||||
{
|
||||
const char* logContext = "MkDirMsg (mkDirPrimary)";
|
||||
|
||||
App* app = Program::getApp();
|
||||
ModificationEventFlusher* modEventFlusher = app->getModificationEventFlusher();
|
||||
const bool modEventLoggingEnabled = modEventFlusher->isLoggingEnabled();
|
||||
MetaStore* metaStore = app->getMetaStore();
|
||||
Config* config = app->getConfig();
|
||||
MirrorBuddyGroupMapper* metaBuddyGroupMapper = app->getMetaBuddyGroupMapper();
|
||||
NodeCapacityPools* metaCapacityPools;
|
||||
NumNodeID expectedOwnerID;
|
||||
|
||||
const EntryInfo* const parentInfo = getParentInfo();
|
||||
const std::string& newName = getNewDirName();
|
||||
|
||||
FhgfsOpsErr retVal;
|
||||
|
||||
// not a good idea to use scoped locks here, because we don't have a well-defined scope with
|
||||
// only buddy mirrored paths here; directly use entrylockstore
|
||||
EntryLockStore* entryLockStore = Program::getApp()->getSessions()->getEntryLockStore();
|
||||
|
||||
// reference parent
|
||||
DirInode* parentDir = metaStore->referenceDir(parentInfo->getEntryID(),
|
||||
parentInfo->getIsBuddyMirrored(), true);
|
||||
if(!parentDir)
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS, EntryInfo());
|
||||
|
||||
// check whether localNode owns this (parent) directory
|
||||
NumNodeID localNodeID = app->getLocalNodeNumID();
|
||||
bool isBuddyMirrored = parentDir->getIsBuddyMirrored()
|
||||
&& !isMsgHeaderFeatureFlagSet(MKDIRMSG_FLAG_NOMIRROR);
|
||||
|
||||
// check whether localNode owns this (parent) directory; if parentDir is buddy mirrored compare
|
||||
// ownership to buddy group id, otherwise to node id itself
|
||||
if (parentDir->getIsBuddyMirrored())
|
||||
expectedOwnerID = NumNodeID(metaBuddyGroupMapper->getLocalGroupID() );
|
||||
else
|
||||
expectedOwnerID = localNodeID;
|
||||
|
||||
if (isBuddyMirrored)
|
||||
metaCapacityPools = app->getMetaBuddyCapacityPools();
|
||||
else
|
||||
metaCapacityPools = app->getMetaCapacityPools();
|
||||
|
||||
if(parentDir->getOwnerNodeID() != expectedOwnerID)
|
||||
{ // this node doesn't own the parent dir
|
||||
LogContext(logContext).logErr(std::string("Dir-owner mismatch: \"") +
|
||||
parentDir->getOwnerNodeID().str() + "\" vs. \"" +
|
||||
expectedOwnerID.str() + "\"");
|
||||
metaStore->releaseDir(parentInfo->getEntryID() );
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_NOTOWNER, EntryInfo());
|
||||
}
|
||||
|
||||
// choose new directory owner...
|
||||
unsigned numDesiredTargets = 1;
|
||||
unsigned minNumRequiredTargets = numDesiredTargets;
|
||||
UInt16Vector newOwnerNodes;
|
||||
|
||||
metaCapacityPools->chooseStorageTargets(numDesiredTargets, minNumRequiredTargets,
|
||||
&getPreferredNodes(), &newOwnerNodes);
|
||||
|
||||
if(unlikely(newOwnerNodes.size() < minNumRequiredTargets) )
|
||||
{ // (might be caused by a bad list of preferred targets)
|
||||
LogContext(logContext).logErr("No metadata servers available for new directory: " + newName);
|
||||
|
||||
metaStore->releaseDir(parentInfo->getEntryID() );
|
||||
// we know that *some* metadata server must exist, since we are obviously active when we get
|
||||
// here. most likely a client has received a pool update before we have, or we have been
|
||||
// switched from secondary to primary and haven't been set to Good yet.
|
||||
// if preferred nodes have been set (currently only done by ctl), those may also be registered
|
||||
// as unavailable at the current time.
|
||||
// have the client retry until things work out.
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_COMMUNICATION, EntryInfo());
|
||||
}
|
||||
|
||||
const uint16_t ownerNodeID = newOwnerNodes[0];
|
||||
const std::string parentEntryID = parentInfo->getEntryID();
|
||||
int entryInfoFlags = isBuddyMirrored ? ENTRYINFO_FEATURE_BUDDYMIRRORED : 0;
|
||||
|
||||
int mode = getMode();
|
||||
const int umask = getUmask();
|
||||
CharVector parentDefaultACLXAttr;
|
||||
CharVector accessACLXAttr;
|
||||
|
||||
if (config->getStoreClientACLs())
|
||||
{
|
||||
// Determine the ACLs of the new directory.
|
||||
PosixACL parentDefaultACL;
|
||||
bool needsACL;
|
||||
FhgfsOpsErr parentDefaultACLRes;
|
||||
|
||||
std::tie(parentDefaultACLRes, parentDefaultACLXAttr, std::ignore) = parentDir->getXAttr(
|
||||
nullptr, PosixACL::defaultACLXAttrName, XATTR_SIZE_MAX);
|
||||
|
||||
if (parentDefaultACLRes == FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
// parent has a default ACL
|
||||
if (!parentDefaultACL.deserializeXAttr(parentDefaultACLXAttr))
|
||||
{
|
||||
LogContext(logContext).log(Log_ERR,
|
||||
"Error deserializing directory default ACL for directory ID " + parentDir->getID());
|
||||
retVal = FhgfsOpsErr_INTERNAL;
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
if (!parentDefaultACL.empty())
|
||||
{
|
||||
// Note: This modifies the mode bits as well as the ACL itself.
|
||||
FhgfsOpsErr modeRes = parentDefaultACL.modifyModeBits(mode, needsACL);
|
||||
setMode(mode, 0);
|
||||
|
||||
if (modeRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LogContext(logContext).log(Log_ERR, "Error generating access ACL for new directory "
|
||||
+ newName);
|
||||
retVal = FhgfsOpsErr_INTERNAL;
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
if (needsACL)
|
||||
parentDefaultACL.serializeXAttr(accessACLXAttr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// On empty ACL, clear the Xattr, so it doesn't get set on the newly created dir
|
||||
parentDefaultACLXAttr.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (parentDefaultACLRes == FhgfsOpsErr_NODATA
|
||||
|| (parentDefaultACLRes == FhgfsOpsErr_SUCCESS && parentDefaultACL.empty()))
|
||||
{
|
||||
// containing dir has no ACL, so we can continue without one.
|
||||
mode &= ~umask;
|
||||
setMode(mode, umask);
|
||||
}
|
||||
|
||||
if (parentDefaultACLRes != FhgfsOpsErr_SUCCESS && parentDefaultACLRes != FhgfsOpsErr_NODATA)
|
||||
{
|
||||
LogContext(logContext).log(Log_ERR,
|
||||
"Error loading default ACL for directory ID " + parentDir->getID() );
|
||||
retVal = parentDefaultACLRes;
|
||||
goto clean_up;
|
||||
}
|
||||
}
|
||||
|
||||
newEntryInfo.set(NumNodeID(ownerNodeID), parentEntryID, entryID, newName,
|
||||
DirEntryType_DIRECTORY, entryInfoFlags);
|
||||
|
||||
// create remote dir metadata
|
||||
// (we create this before the dentry to reduce the risk of dangling dentries)
|
||||
retVal = mkRemoteDirInode(*parentDir, newName, &newEntryInfo, parentDefaultACLXAttr,
|
||||
accessACLXAttr);
|
||||
|
||||
if ( likely(retVal == FhgfsOpsErr_SUCCESS) )
|
||||
{ // remote dir created => create dentry in parent dir
|
||||
// note: we can't lock before this point, because mkRemoteDirInode will also send a message
|
||||
// that needs to aquire a lock on the ID
|
||||
FileIDLock lock;
|
||||
|
||||
if (parentInfo->getIsBuddyMirrored())
|
||||
lock = {entryLockStore, entryID, true};
|
||||
|
||||
retVal = mkDirDentry(*parentDir, newName, &newEntryInfo, isBuddyMirrored);
|
||||
|
||||
if ( retVal != FhgfsOpsErr_SUCCESS )
|
||||
{ // error (or maybe name just existed already) => compensate metaDir creation
|
||||
// note: unlock needs to happen before a possible remoteDirCompensation, because rmDir
|
||||
// will also need to lock entryID
|
||||
lock = {};
|
||||
mkRemoteDirCompensate(&newEntryInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (app->getFileEventLogger() && getFileEvent())
|
||||
{
|
||||
EventContext eventCtx = makeEventContext(&newEntryInfo, newEntryInfo.getParentEntryID(),
|
||||
// This is not the secondary node if mkDirPrimary() was called.
|
||||
getMsgHeaderUserID(), "", INITIAL_DIR_LINK_COUNT, false);
|
||||
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clean_up:
|
||||
metaStore->releaseDir(parentInfo->getEntryID() );
|
||||
|
||||
if (modEventLoggingEnabled)
|
||||
modEventFlusher->add(ModificationEvent_DIRCREATED, newEntryInfo.getEntryID());
|
||||
|
||||
return boost::make_unique<ResponseState>(retVal, std::move(newEntryInfo));
|
||||
}
|
||||
|
||||
std::unique_ptr<MkDirMsgEx::ResponseState> MkDirMsgEx::mkDirSecondary()
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
// only create the dentry here; forwarding of inode creation directly happens in MkLocalFileMsg
|
||||
// and error handling and compensation is done by primary
|
||||
FhgfsOpsErr retVal;
|
||||
|
||||
// reference parent
|
||||
DirInode* parentDir = metaStore->referenceDir(getParentInfo()->getEntryID(),
|
||||
getParentInfo()->getIsBuddyMirrored(), true);
|
||||
if(!parentDir)
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS, EntryInfo());
|
||||
|
||||
retVal = mkDirDentry(*parentDir, getCreatedEntryInfo()->getFileName(), getCreatedEntryInfo(),
|
||||
getCreatedEntryInfo()->getIsBuddyMirrored());
|
||||
|
||||
metaStore->releaseDir(getParentInfo()->getEntryID());
|
||||
|
||||
return boost::make_unique<ResponseState>(retVal, *getCreatedEntryInfo());
|
||||
}
|
||||
|
||||
FhgfsOpsErr MkDirMsgEx::mkDirDentry(DirInode& parentDir, const std::string& name,
|
||||
const EntryInfo* entryInfo, const bool isBuddyMirrored)
|
||||
{
|
||||
const std::string entryID = entryInfo->getEntryID();
|
||||
const NumNodeID ownerNodeID = entryInfo->getOwnerNodeID();
|
||||
|
||||
DirEntry newDirDentry(DirEntryType_DIRECTORY, name, entryID, ownerNodeID);
|
||||
|
||||
if(isBuddyMirrored)
|
||||
newDirDentry.setBuddyMirrorFeatureFlag();
|
||||
|
||||
const FhgfsOpsErr mkRes = parentDir.makeDirEntry(newDirDentry);
|
||||
|
||||
if (mkRes == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
fixInodeTimestamp(parentDir, parentTimestamps);
|
||||
|
||||
return mkRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create dir inode on a remote server.
|
||||
*
|
||||
* @param name only used for logging
|
||||
* @param mirrorNodeID 0 for disabled mirroring
|
||||
*/
|
||||
FhgfsOpsErr MkDirMsgEx::mkRemoteDirInode(DirInode& parentDir, const std::string& name,
|
||||
EntryInfo* entryInfo, const CharVector& defaultACLXAttr, const CharVector& accessACLXAttr)
|
||||
{
|
||||
const char* logContext = "MkDirMsg (mk dir inode)";
|
||||
|
||||
App* app = Program::getApp();
|
||||
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
|
||||
|
||||
StripePattern* pattern = parentDir.getStripePatternClone();
|
||||
NumNodeID ownerNodeID = entryInfo->getOwnerNodeID();
|
||||
|
||||
RemoteStorageTarget rstInfo;
|
||||
if (parentDir.getIsRstAvailable())
|
||||
rstInfo.set(parentDir.getRemoteStorageTargetInfo());
|
||||
|
||||
LOG_DEBUG(logContext, Log_DEBUG,
|
||||
"Creating dir inode at metadata node: " + ownerNodeID.str() + "; dirname: " + name);
|
||||
|
||||
// prepare request
|
||||
|
||||
NumNodeID parentNodeID = app->getLocalNode().getNumID();
|
||||
MkLocalDirMsg mkMsg(entryInfo, getUserID(), getGroupID(), getMode(), pattern, &rstInfo,
|
||||
parentNodeID, defaultACLXAttr, accessACLXAttr);
|
||||
|
||||
RequestResponseArgs rrArgs(NULL, &mkMsg, NETMSGTYPE_MkLocalDirResp);
|
||||
|
||||
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes() );
|
||||
rrNode.setTargetStates(app->getMetaStateStore() );
|
||||
|
||||
if(entryInfo->getIsBuddyMirrored())
|
||||
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
|
||||
|
||||
do // (this loop just exists to enable the "break"-jump, so it's not really a loop)
|
||||
{
|
||||
// send request to other mds and receive response
|
||||
|
||||
FhgfsOpsErr requestRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
|
||||
if(unlikely(requestRes != FhgfsOpsErr_SUCCESS) )
|
||||
{ // communication error
|
||||
LogContext(logContext).log(Log_WARNING,
|
||||
"Communication with metadata server failed. "
|
||||
"nodeID: " + ownerNodeID.str() + "; " +
|
||||
"dirname: " + name);
|
||||
retVal = requestRes;
|
||||
break;
|
||||
}
|
||||
|
||||
// correct response type received
|
||||
const auto mkRespMsg = (const MkLocalDirRespMsg*)rrArgs.outRespMsg.get();
|
||||
|
||||
FhgfsOpsErr mkRemoteInodeRes = mkRespMsg->getResult();
|
||||
if(mkRemoteInodeRes != FhgfsOpsErr_SUCCESS)
|
||||
{ // error: remote dir inode not created
|
||||
LogContext(logContext).log(Log_WARNING,
|
||||
"Metadata server failed to create dir inode. "
|
||||
"nodeID: " + ownerNodeID.str() + "; " +
|
||||
"dirname: " + name);
|
||||
|
||||
retVal = mkRemoteInodeRes;
|
||||
break;
|
||||
}
|
||||
|
||||
// success: remote dir inode created
|
||||
LOG_DEBUG(logContext, Log_DEBUG,
|
||||
"Metadata server created dir inode. "
|
||||
"nodeID: " + ownerNodeID.str() + "; "
|
||||
"dirname: " + name);
|
||||
|
||||
} while(false);
|
||||
|
||||
|
||||
delete(pattern);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove dir metadata on a remote server to compensate for creation.
|
||||
*/
|
||||
FhgfsOpsErr MkDirMsgEx::mkRemoteDirCompensate(EntryInfo* entryInfo)
|
||||
{
|
||||
LogContext log("MkDirMsg (undo dir inode [" + entryInfo->getFileName() + "])");
|
||||
|
||||
FhgfsOpsErr rmRes = RmDirMsgEx::rmRemoteDirInode(entryInfo);
|
||||
|
||||
if(unlikely(rmRes != FhgfsOpsErr_SUCCESS) )
|
||||
{ // error
|
||||
log.log(Log_WARNING, std::string("Compensation not completely successful. ") +
|
||||
"File system might contain (uncritical) inconsistencies.");
|
||||
|
||||
return rmRes;
|
||||
}
|
||||
|
||||
log.log(Log_SPAM, "Creation of dir inode compensated");
|
||||
|
||||
return rmRes;
|
||||
}
|
||||
|
||||
void MkDirMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
// secondary needs to know the created entryInfo, because it needs to use the same information
|
||||
setCreatedEntryInfo(&newEntryInfo);
|
||||
|
||||
// not needed on secondary
|
||||
clearPreferredNodes();
|
||||
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_MkDirResp);
|
||||
}
|
||||
53
meta/source/net/message/storage/creating/MkDirMsgEx.h
Normal file
53
meta/source/net/message/storage/creating/MkDirMsgEx.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/creating/MkDirMsg.h>
|
||||
#include <common/net/message/storage/creating/MkDirRespMsg.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
|
||||
class MkDirMsgEx : public MirroredMessage<MkDirMsg, std::tuple<HashDirLock, FileIDLock, ParentNameLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorAndEntryResponseState<MkDirRespMsg, NETMSGTYPE_MkDir> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock, ParentNameLock> lock(EntryLockStore& store) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
bool isMirrored() override { return getParentInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
// Initial hard link count for a newly created directory (self and "." entry).
|
||||
// Note: ".." entry increments the parent's link count, not this directory's.
|
||||
static constexpr unsigned INITIAL_DIR_LINK_COUNT = 2;
|
||||
|
||||
std::string entryID;
|
||||
|
||||
std::unique_ptr<ResponseState> mkDirPrimary(ResponseContext& ctx);
|
||||
std::unique_ptr<ResponseState> mkDirSecondary();
|
||||
|
||||
FhgfsOpsErr mkDirDentry(DirInode& parentDir, const std::string& name,
|
||||
const EntryInfo* entryInfo, const bool isBuddyMirrored);
|
||||
|
||||
FhgfsOpsErr mkRemoteDirInode(DirInode& parentDir, const std::string& name,
|
||||
EntryInfo* entryInfo, const CharVector& defaultACLXAttr, const CharVector& accessACLXAttr);
|
||||
FhgfsOpsErr mkRemoteDirCompensate(EntryInfo* entryInfo);
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<MkDirRespMsg&>(resp).getResult();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "MkDirMsgEx/forward"; }
|
||||
|
||||
EntryInfo newEntryInfo;
|
||||
};
|
||||
|
||||
136
meta/source/net/message/storage/creating/MkFileMsgEx.cpp
Normal file
136
meta/source/net/message/storage/creating/MkFileMsgEx.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
#include <common/net/message/storage/creating/MkFileRespMsg.h>
|
||||
#include <common/net/message/control/GenericResponseMsg.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <common/storage/StatData.h>
|
||||
#include <components/FileEventLogger.h>
|
||||
#include <net/msghelpers/MsgHelperMkFile.h>
|
||||
#include <program/Program.h>
|
||||
#include "MkFileMsgEx.h"
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock> MkFileMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
FileIDLock dirLock(&store, getParentInfo()->getEntryID(), true);
|
||||
ParentNameLock dentryLock(&store, getParentInfo()->getEntryID(), getNewName());
|
||||
FileIDLock fileLock(&store, newEntryID, true);
|
||||
|
||||
return std::make_tuple(
|
||||
std::move(dirLock),
|
||||
std::move(dentryLock),
|
||||
std::move(fileLock));
|
||||
}
|
||||
|
||||
|
||||
bool MkFileMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
#ifdef BEEGFS_DEBUG
|
||||
const char* logContext = "MkFileMsg incoming";
|
||||
#endif // BEEGFS_DEBUG
|
||||
|
||||
LOG_DEBUG(logContext, Log_DEBUG, "parentEntryID: " +
|
||||
getParentInfo()->getEntryID() + " newFileName: " + getNewName() );
|
||||
|
||||
LOG_DEBUG(logContext, Log_DEBUG,
|
||||
"BuddyMirrored: " + std::string(getParentInfo()->getIsBuddyMirrored() ? "'Yes'" : "'No'") +
|
||||
" Secondary: " + std::string(hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond) ?
|
||||
"Yes" : "No") );
|
||||
|
||||
if (!hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
|
||||
createTime = TimeAbs().getTimeval()->tv_sec;
|
||||
|
||||
mkDetails = {getNewName(), getUserID(), getGroupID(), getMode(), getUmask(), createTime};
|
||||
|
||||
if (getParentInfo()->getIsBuddyMirrored())
|
||||
{
|
||||
if (!hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
|
||||
newEntryID = StorageTk::generateFileID(Program::getApp()->getLocalNode().getNumID());
|
||||
else
|
||||
newEntryID = getNewEntryID();
|
||||
|
||||
mkDetails.setNewEntryID(newEntryID.c_str());
|
||||
}
|
||||
|
||||
BaseType::processIncoming(ctx);
|
||||
|
||||
updateNodeOp(ctx, MetaOpCounter_MKFILE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> MkFileMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
if (isSecondary)
|
||||
return executeSecondary();
|
||||
else
|
||||
return executePrimary();
|
||||
}
|
||||
|
||||
std::unique_ptr<MkFileMsgEx::ResponseState> MkFileMsgEx::executePrimary()
|
||||
{
|
||||
DirInode* dir = Program::getApp()->getMetaStore()->referenceDir(
|
||||
getParentInfo()->getEntryID(), getParentInfo()->getIsBuddyMirrored(), true);
|
||||
if (!dir)
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS, entryInfo);
|
||||
|
||||
FhgfsOpsErr mkRes;
|
||||
if (isMsgHeaderFeatureFlagSet(MKFILEMSG_FLAG_STORAGEPOOLID))
|
||||
{
|
||||
mkRes = MsgHelperMkFile::mkFile(*dir, &mkDetails, &getPreferredNodes(),
|
||||
getNumTargets(), getChunkSize(), NULL, getRemoteStorageTarget(),
|
||||
&entryInfo, &inodeData, storagePoolId);
|
||||
}
|
||||
else
|
||||
{
|
||||
mkRes = MsgHelperMkFile::mkFile(*dir, &mkDetails, &getPreferredNodes(),
|
||||
getNumTargets(), getChunkSize(), NULL, getRemoteStorageTarget(), &entryInfo, &inodeData);
|
||||
}
|
||||
|
||||
if (mkRes == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
fixInodeTimestamp(*dir, dirTimestamps);
|
||||
|
||||
Program::getApp()->getMetaStore()->releaseDir(dir->getID());
|
||||
|
||||
if (mkRes == FhgfsOpsErr_SUCCESS && Program::getApp()->getFileEventLogger() && getFileEvent())
|
||||
{
|
||||
EventContext eventCtx = makeEventContext(&entryInfo, entryInfo.getParentEntryID(),
|
||||
// This is not the secondary node if executePrimary() was called.
|
||||
getMsgHeaderUserID(), "", this->inodeData.getInodeStatData()->getNumHardlinks(), false);
|
||||
|
||||
logEvent(Program::getApp()->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(mkRes, entryInfo);
|
||||
}
|
||||
|
||||
std::unique_ptr<MkFileMsgEx::ResponseState> MkFileMsgEx::executeSecondary()
|
||||
{
|
||||
StripePattern* stripePattern = getPattern().clone();
|
||||
|
||||
FileInodeStoreData inodeData;
|
||||
|
||||
DirInode* dir = Program::getApp()->getMetaStore()->referenceDir(
|
||||
getParentInfo()->getEntryID(), getParentInfo()->getIsBuddyMirrored(), true);
|
||||
if (!dir)
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS, entryInfo);
|
||||
|
||||
FhgfsOpsErr mkRes = MsgHelperMkFile::mkFile(*dir, &mkDetails, &getPreferredNodes(),
|
||||
getNumTargets(), getChunkSize(), stripePattern, getRemoteStorageTarget(), &entryInfo, &inodeData);
|
||||
|
||||
if (mkRes == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
fixInodeTimestamp(*dir, dirTimestamps);
|
||||
|
||||
Program::getApp()->getMetaStore()->releaseDir(dir->getID());
|
||||
|
||||
return boost::make_unique<ResponseState>(mkRes, entryInfo);
|
||||
}
|
||||
|
||||
void MkFileMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
// secondary needs to know the created entryID and stripe pattern, because it needs to use the
|
||||
// same information
|
||||
setNewEntryID(newEntryID.c_str());
|
||||
setPattern(inodeData.getStripePattern());
|
||||
setRemoteStorageTarget(getRemoteStorageTarget());
|
||||
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_MkFileResp);
|
||||
}
|
||||
48
meta/source/net/message/storage/creating/MkFileMsgEx.h
Normal file
48
meta/source/net/message/storage/creating/MkFileMsgEx.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/creating/MkFileMsg.h>
|
||||
#include <common/net/message/storage/creating/MkFileRespMsg.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
|
||||
class MkFileMsgEx : public MirroredMessage<MkFileMsg,
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorAndEntryResponseState<MkFileRespMsg, NETMSGTYPE_MkFile> ResponseState;
|
||||
|
||||
MkFileMsgEx()
|
||||
: mkDetails({}, 0, 0, 0, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock> lock(EntryLockStore& store) override;
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
bool isMirrored() override { return getParentInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<ResponseState> executePrimary();
|
||||
std::unique_ptr<ResponseState> executeSecondary();
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<MkFileRespMsg&>(resp).getResult();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "MkFileMsgEx/forward"; }
|
||||
|
||||
FileInodeStoreData inodeData;
|
||||
MkFileDetails mkDetails;
|
||||
std::string newEntryID;
|
||||
|
||||
EntryInfo entryInfo;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
#include <common/net/message/storage/creating/MkFileMsg.h>
|
||||
#include <common/net/message/storage/creating/MkFileRespMsg.h>
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileMsg.h>
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileRespMsg.h>
|
||||
#include <common/storage/striping/StripePattern.h>
|
||||
#include <common/toolkit/MathTk.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <components/FileEventLogger.h>
|
||||
#include <net/msghelpers/MsgHelperMkFile.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include "MkFileWithPatternMsgEx.h"
|
||||
|
||||
// Called from fhgfs-ctl (online_cfg) to create a file on a specific node.
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock> MkFileWithPatternMsgEx::lock(
|
||||
EntryLockStore& store)
|
||||
{
|
||||
// no need to lock the created file as well, since
|
||||
// a) no other operation can create the same file id
|
||||
// b) until we finish, no part of the system except us knows the new file id
|
||||
// c) if bulk resync gets the file while it is incomplete, individual resync will get it again
|
||||
FileIDLock dirLock(&store, getParentInfo()->getEntryID(), true);
|
||||
ParentNameLock dentryLock(&store, getParentInfo()->getEntryID(), getNewFileName());
|
||||
FileIDLock fileLock(&store, entryID, true);
|
||||
|
||||
return std::make_tuple(std::move(dirLock), std::move(dentryLock), std::move(fileLock));
|
||||
}
|
||||
|
||||
bool MkFileWithPatternMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
const EntryInfo* parentInfo = getParentInfo();
|
||||
std::string newFileName = getNewFileName();
|
||||
EntryInfo newEntryInfo;
|
||||
|
||||
LOG_DEBUG("MkFileWithPatternMsg", Log_DEBUG,
|
||||
" parentDirID: " + parentInfo->getEntryID() + " newFileName: " + newFileName
|
||||
+ " isBuddyMirrored: " + (parentInfo->getIsBuddyMirrored() ? "'true'" : "'false'"));
|
||||
|
||||
// create ID first
|
||||
if (parentInfo->getIsBuddyMirrored())
|
||||
entryID = StorageTk::generateFileID(Program::getApp()->getLocalNode().getNumID());
|
||||
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> MkFileWithPatternMsgEx::executeLocally(
|
||||
ResponseContext& ctx, bool isSecondary)
|
||||
{
|
||||
EntryInfo newEntryInfo;
|
||||
|
||||
MkFileDetails mkDetails(getNewFileName(), getUserID(), getGroupID(), getMode(), getUmask(),
|
||||
TimeAbs().getTimeval()->tv_sec);
|
||||
if (!entryID.empty())
|
||||
mkDetails.setNewEntryID(entryID.c_str());
|
||||
|
||||
FhgfsOpsErr mkRes = mkFile(getParentInfo(), mkDetails, &newEntryInfo, inodeDiskData);
|
||||
|
||||
updateNodeOp(ctx, MetaOpCounter_MKFILE);
|
||||
|
||||
return boost::make_unique<ResponseState>(mkRes, std::move(newEntryInfo));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param dir current directory
|
||||
* @param currentDepth 1-based path depth
|
||||
*/
|
||||
FhgfsOpsErr MkFileWithPatternMsgEx::mkFile(const EntryInfo* parentInfo, MkFileDetails& mkDetails,
|
||||
EntryInfo* outEntryInfo, FileInodeStoreData& inodeDiskData)
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
FhgfsOpsErr retVal;
|
||||
|
||||
// reference parent
|
||||
DirInode* dir = metaStore->referenceDir(parentInfo->getEntryID(),
|
||||
parentInfo->getIsBuddyMirrored(), true);
|
||||
if ( !dir )
|
||||
return FhgfsOpsErr_PATHNOTEXISTS;
|
||||
|
||||
// create meta file
|
||||
retVal = mkMetaFile(*dir, mkDetails, outEntryInfo, inodeDiskData);
|
||||
|
||||
if (shouldFixTimestamps())
|
||||
fixInodeTimestamp(*dir, dirTimestamps);
|
||||
|
||||
// clean-up
|
||||
metaStore->releaseDir(parentInfo->getEntryID());
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an inode and directory-entry
|
||||
*/
|
||||
FhgfsOpsErr MkFileWithPatternMsgEx::mkMetaFile(DirInode& dir, MkFileDetails& mkDetails,
|
||||
EntryInfo* outEntryInfo, FileInodeStoreData& inodeDiskData)
|
||||
{
|
||||
// note: to guarantee amtomicity of file creation (from the view of a client), we have
|
||||
// to create the inode first and insert the directory entry afterwards
|
||||
|
||||
std::unique_ptr<StripePattern> stripePattern(getPattern().clone());
|
||||
const UInt16Vector* stripeTargets = stripePattern->getStripeTargetIDs();
|
||||
StoragePoolId storagePoolId = stripePattern->getStoragePoolId();
|
||||
|
||||
if (stripeTargets->empty())
|
||||
{
|
||||
StoragePoolPtr storagePool =
|
||||
Program::getApp()->getStoragePoolStore()->getPool(storagePoolId);
|
||||
|
||||
if (!storagePool)
|
||||
{
|
||||
LOG(GENERAL, ERR, "Given Storage Pool ID "
|
||||
+ StringTk::uintToStr(storagePoolId.val()) + " doesn't exist.");
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
UInt16Vector chosenStripeTargets;
|
||||
|
||||
if(stripePattern->getPatternType() == StripePatternType_BuddyMirror)
|
||||
{
|
||||
storagePool->getBuddyCapacityPools()->chooseStorageTargets(
|
||||
stripePattern->getDefaultNumTargets(), stripePattern->getMinNumTargets(),
|
||||
nullptr, &chosenStripeTargets);
|
||||
}
|
||||
else
|
||||
{
|
||||
storagePool->getTargetCapacityPools()->chooseStorageTargets(
|
||||
stripePattern->getDefaultNumTargets(), stripePattern->getMinNumTargets(),
|
||||
nullptr, &chosenStripeTargets);
|
||||
}
|
||||
stripePattern->getStripeTargetIDsModifyable()->swap(chosenStripeTargets);
|
||||
}
|
||||
|
||||
// check if num targets and target list match and if targets actually exist
|
||||
if(stripeTargets->empty() || (stripeTargets->size() < stripePattern->getMinNumTargets() ) )
|
||||
{
|
||||
LOG(GENERAL, ERR, "No (or not enough) storage targets defined.",
|
||||
("numTargets", stripeTargets->size()),
|
||||
("expectedMinNumTargets", stripePattern->getMinNumTargets()));
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
for (auto iter = stripeTargets->begin(); iter != stripeTargets->end(); iter++)
|
||||
{
|
||||
if(stripePattern->getPatternType() == StripePatternType_BuddyMirror)
|
||||
{
|
||||
if (!Program::getApp()->getStorageBuddyGroupMapper()->getPrimaryTargetID(*iter))
|
||||
{
|
||||
LOG(GENERAL, ERR, "Unknown buddy group targets defined.",
|
||||
("targetId", *iter));
|
||||
return FhgfsOpsErr_UNKNOWNTARGET;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Program::getApp()->getTargetMapper()->targetExists(*iter))
|
||||
{
|
||||
LOG(GENERAL, ERR, "Unknown storage targets defined.",
|
||||
("targetId", *iter));
|
||||
return FhgfsOpsErr_UNKNOWNTARGET;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if chunk size satisfies constraints
|
||||
if (!MathTk::isPowerOfTwo(stripePattern->getChunkSize()))
|
||||
{
|
||||
LOG(GENERAL, DEBUG, "Invalid chunk size: Must be a power of two.",
|
||||
stripePattern->getChunkSize());
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
if (stripePattern->getChunkSize() < STRIPEPATTERN_MIN_CHUNKSIZE)
|
||||
{
|
||||
LOG(GENERAL, DEBUG, "Invalid chunk size: Below minimum size.",
|
||||
stripePattern->getChunkSize(),
|
||||
("minChunkSize", STRIPEPATTERN_MIN_CHUNKSIZE));
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
return Program::getApp()->getMetaStore()->mkNewMetaFile(
|
||||
dir, &mkDetails, std::move(stripePattern), getRemoteStorageTarget(), outEntryInfo,
|
||||
&inodeDiskData);
|
||||
// (note: internally deletes stripePattern)
|
||||
}
|
||||
|
||||
void MkFileWithPatternMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
UInt16List preferredTargets; // can be an empty dummy
|
||||
std::string newFileName = getNewFileName();
|
||||
|
||||
MkFileMsg mkFileMsg(getParentInfo(), newFileName, getUserID(), getGroupID(), getMode(),
|
||||
getUmask(), &preferredTargets);
|
||||
|
||||
mkFileMsg.addFlag(NetMessageHeader::Flag_BuddyMirrorSecond);
|
||||
// secondary needs to know the created entryID ans stripe pattern, because it needs to use the
|
||||
// same information
|
||||
mkFileMsg.setNewEntryID(entryID.c_str());
|
||||
mkFileMsg.setPattern(inodeDiskData.getStripePattern());
|
||||
mkFileMsg.setDirTimestamps(dirTimestamps);
|
||||
mkFileMsg.setCreateTime(inodeDiskData.getInodeStatData()->getCreationTimeSecs());
|
||||
mkFileMsg.setRemoteStorageTarget(getRemoteStorageTarget());
|
||||
|
||||
sendToSecondary(ctx, mkFileMsg, NETMSGTYPE_MkFileResp);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/creating/MkFileRespMsg.h>
|
||||
#include <common/net/message/storage/creating/MkFileWithPatternMsg.h>
|
||||
#include <common/net/message/storage/creating/MkFileWithPatternRespMsg.h>
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <storage/MetaStore.h>
|
||||
|
||||
/**
|
||||
* Similar to class MsgHelperMkFile, but called with a create pattern, for example from fhgfs-ctl
|
||||
* or from ioctl calls.
|
||||
*/
|
||||
class MkFileWithPatternMsgEx : public MirroredMessage<MkFileWithPatternMsg,
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorAndEntryResponseState<MkFileWithPatternRespMsg, NETMSGTYPE_MkFileWithPattern>
|
||||
ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock> lock(EntryLockStore& store) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
bool isMirrored() override { return getParentInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
MirroredTimestamps dirTimestamps;
|
||||
|
||||
FhgfsOpsErr mkFile(const EntryInfo* parentInfo, MkFileDetails& mkDetails,
|
||||
EntryInfo* outEntryInfo, FileInodeStoreData& inodeDiskData);
|
||||
FhgfsOpsErr mkMetaFile(DirInode& dir, MkFileDetails& mkDetails,
|
||||
EntryInfo* outEntryInfo, FileInodeStoreData& inodeDiskData);
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<MkFileRespMsg&>(resp).getResult();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "MkFileWithPatternMsgEx/forward"; }
|
||||
|
||||
FileInodeStoreData inodeDiskData;
|
||||
std::string entryID;
|
||||
};
|
||||
|
||||
76
meta/source/net/message/storage/creating/MkLocalDirMsgEx.cpp
Normal file
76
meta/source/net/message/storage/creating/MkLocalDirMsgEx.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include <common/net/message/control/GenericResponseMsg.h>
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/creating/MkLocalDirRespMsg.h>
|
||||
|
||||
#include "MkLocalDirMsgEx.h"
|
||||
|
||||
HashDirLock MkLocalDirMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
// we usually need not lock anything here, because the inode ID will be completely unknown to
|
||||
// anyone until we finish processing here *and* on the metadata server that sent this message.
|
||||
// during resync though we need to lock the hash dir to avoid interefence between bulk resync and
|
||||
// mod resync.
|
||||
|
||||
// do not lock the hash dir if we are creating the inode on the same meta node as the dentry,
|
||||
// MkDir will have already locked the hash dir.
|
||||
if (!rctx->isLocallyGenerated() && resyncJob && resyncJob->isRunning())
|
||||
return {&store, MetaStorageTk::getMetaInodeHash(getEntryInfo()->getEntryID())};
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool MkLocalDirMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
EntryInfo* entryInfo = getEntryInfo();
|
||||
|
||||
LOG_DBG(GENERAL, DEBUG, "", entryInfo->getEntryID(), entryInfo->getFileName());
|
||||
(void) entryInfo;
|
||||
|
||||
rctx = &ctx;
|
||||
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> MkLocalDirMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
MetaStore* metaStore = app->getMetaStore();
|
||||
StripePattern& pattern = getPattern();
|
||||
|
||||
RemoteStorageTarget* rstInfo = getRemoteStorageTarget();
|
||||
|
||||
EntryInfo *entryInfo = getEntryInfo();
|
||||
NumNodeID parentNodeID = getParentNodeID();
|
||||
|
||||
NumNodeID ownerNodeID = entryInfo->getIsBuddyMirrored()
|
||||
? NumNodeID(app->getMetaBuddyGroupMapper()->getLocalGroupID() )
|
||||
: app->getLocalNode().getNumID();
|
||||
|
||||
DirInode newDir(entryInfo->getEntryID(), getMode(), getUserID(),
|
||||
getGroupID(), ownerNodeID, pattern, entryInfo->getIsBuddyMirrored());
|
||||
|
||||
newDir.setParentInfoInitial(entryInfo->getParentEntryID(), parentNodeID);
|
||||
|
||||
FhgfsOpsErr mkRes = metaStore->makeDirInode(newDir, getDefaultACLXAttr(), getAccessACLXAttr() );
|
||||
|
||||
if (!rstInfo->hasInvalidVersion() && (mkRes == FhgfsOpsErr_SUCCESS))
|
||||
{
|
||||
FhgfsOpsErr setRstRes = newDir.setRemoteStorageTarget(*rstInfo);
|
||||
if (setRstRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LogContext("MkLocalDir").log(Log_WARNING, "Failed to set remote storage targets for "
|
||||
"dirID: " + newDir.getID() + ". RST might be invalid.");
|
||||
}
|
||||
}
|
||||
|
||||
if (mkRes == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
fixInodeTimestamp(newDir, dirTimestamps);
|
||||
|
||||
return boost::make_unique<ResponseState>(mkRes);
|
||||
}
|
||||
|
||||
void MkLocalDirMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_MkLocalDirResp);
|
||||
}
|
||||
36
meta/source/net/message/storage/creating/MkLocalDirMsgEx.h
Normal file
36
meta/source/net/message/storage/creating/MkLocalDirMsgEx.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/creating/MkLocalDirMsg.h>
|
||||
#include <common/net/message/storage/creating/MkLocalDirRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <storage/MetaStore.h>
|
||||
|
||||
class MkLocalDirMsgEx : public MirroredMessage<MkLocalDirMsg, HashDirLock>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<MkLocalDirRespMsg, NETMSGTYPE_MkLocalDir> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
HashDirLock lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
ResponseContext* rctx;
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<MkLocalDirRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "MkLocalDirMsgEx/forward"; }
|
||||
};
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#include <common/net/message/storage/creating/MoveFileInodeRespMsg.h>
|
||||
#include "MoveFileInodeMsgEx.h"
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock> MoveFileInodeMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
EntryInfo* fileInfo = getFromFileEntryInfo();
|
||||
|
||||
FileIDLock dirLock(&store, fileInfo->getParentEntryID(), true);
|
||||
ParentNameLock dentryLock(&store, fileInfo->getParentEntryID(), fileInfo->getFileName());
|
||||
FileIDLock inodeLock(&store, fileInfo->getEntryID(), true);
|
||||
|
||||
return std::make_tuple(
|
||||
std::move(dirLock),
|
||||
std::move(dentryLock),
|
||||
std::move(inodeLock));
|
||||
}
|
||||
|
||||
bool MoveFileInodeMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
rctx = &ctx;
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> MoveFileInodeMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
MetaStore* metaStore = app->getMetaStore();
|
||||
MoveFileInodeMsgResponseState resp;
|
||||
|
||||
if (getMode() == FileInodeMode::MODE_INVALID)
|
||||
{
|
||||
// invalid operation requested
|
||||
return boost::make_unique<ResponseState>(std::move(resp));
|
||||
}
|
||||
|
||||
EntryInfo* fromFileInfo = getFromFileEntryInfo();
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_INTERNAL;
|
||||
unsigned linkCount = 0;
|
||||
|
||||
if (getCreateHardlink())
|
||||
{
|
||||
std::tie(retVal, linkCount) = metaStore->makeNewHardlink(fromFileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
DirInode* parentDir = metaStore->referenceDir(
|
||||
fromFileInfo->getParentEntryID(), fromFileInfo->getIsBuddyMirrored(), true);
|
||||
|
||||
if (likely(parentDir))
|
||||
{
|
||||
retVal = metaStore->verifyAndMoveFileInode(*parentDir, fromFileInfo, getMode());
|
||||
metaStore->releaseDir(parentDir->getID());
|
||||
}
|
||||
else
|
||||
retVal = FhgfsOpsErr_PATHNOTEXISTS;
|
||||
}
|
||||
|
||||
resp.setResult(retVal);
|
||||
resp.setHardlinkCount(linkCount);
|
||||
return boost::make_unique<ResponseState>(std::move(resp));
|
||||
}
|
||||
|
||||
void MoveFileInodeMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_MoveFileInodeResp);
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/creating/MoveFileInodeMsg.h>
|
||||
#include <common/net/message/storage/creating/MoveFileInodeRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <storage/MetaStore.h>
|
||||
|
||||
class MoveFileInodeMsgResponseState : public MirroredMessageResponseState
|
||||
{
|
||||
public:
|
||||
MoveFileInodeMsgResponseState() : result(FhgfsOpsErr_INTERNAL), linkCount(0) {}
|
||||
|
||||
explicit MoveFileInodeMsgResponseState(Deserializer& des)
|
||||
{
|
||||
serialize(this, des);
|
||||
}
|
||||
|
||||
MoveFileInodeMsgResponseState(MoveFileInodeMsgResponseState&& other) :
|
||||
result(other.result), linkCount(other.linkCount) {}
|
||||
|
||||
void sendResponse(NetMessage::ResponseContext& ctx) override
|
||||
{
|
||||
MoveFileInodeRespMsg resp(result, linkCount);
|
||||
ctx.sendResponse(resp);
|
||||
}
|
||||
|
||||
void setResult(FhgfsOpsErr res) { this->result = res; }
|
||||
void setHardlinkCount(unsigned linkCount) { this->linkCount = linkCount; }
|
||||
bool changesObservableState() const override { return this->result == FhgfsOpsErr_SUCCESS; }
|
||||
|
||||
protected:
|
||||
uint32_t serializerTag() const override { return NETMSGTYPE_MoveFileInodeResp; }
|
||||
|
||||
template<typename This, typename Ctx>
|
||||
static void serialize(This obj, Ctx& ctx)
|
||||
{
|
||||
ctx
|
||||
% obj->result
|
||||
% obj->linkCount;
|
||||
}
|
||||
|
||||
void serializeContents(Serializer& ser) const override
|
||||
{
|
||||
serialize(this, ser);
|
||||
}
|
||||
|
||||
private:
|
||||
FhgfsOpsErr result;
|
||||
unsigned linkCount;
|
||||
};
|
||||
|
||||
class MoveFileInodeMsgEx : public MirroredMessage<MoveFileInodeMsg,
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef MoveFileInodeMsgResponseState ResponseState;
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock, FileIDLock> lock(EntryLockStore& store) override;
|
||||
bool isMirrored() override { return getFromFileEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
ResponseContext* rctx;
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<MoveFileInodeRespMsg&>(resp).getResult();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "MoveFileInodeMsgEx/forward"; }
|
||||
};
|
||||
67
meta/source/net/message/storage/creating/RmDirEntryMsgEx.cpp
Normal file
67
meta/source/net/message/storage/creating/RmDirEntryMsgEx.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/creating/RmDirEntryRespMsg.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include "RmDirEntryMsgEx.h"
|
||||
|
||||
bool RmDirEntryMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
EntryInfo* parentInfo = getParentInfo();
|
||||
std::string entryName = getEntryName();
|
||||
|
||||
FhgfsOpsErr rmRes = rmDirEntry(parentInfo, entryName);
|
||||
|
||||
ctx.sendResponse(RmDirEntryRespMsg(rmRes) );
|
||||
|
||||
App* app = Program::getApp();
|
||||
app->getNodeOpStats()->updateNodeOp(ctx.getSocket()->getPeerIP(), MetaOpCounter_RMLINK,
|
||||
getMsgHeaderUserID() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FhgfsOpsErr RmDirEntryMsgEx::rmDirEntry(EntryInfo* parentInfo, std::string& entryName)
|
||||
{
|
||||
const char* logContext = "RmDirEntryMsg (rm entry)";
|
||||
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
FhgfsOpsErr retVal;
|
||||
|
||||
|
||||
// reference parent
|
||||
DirInode* parentDir = metaStore->referenceDir(parentInfo->getEntryID(),
|
||||
parentInfo->getIsBuddyMirrored(), true);
|
||||
if(!parentDir)
|
||||
return FhgfsOpsErr_PATHNOTEXISTS;
|
||||
|
||||
DirEntry removeDentry(entryName);
|
||||
bool getRes = parentDir->getDentry(entryName, removeDentry);
|
||||
if(!getRes)
|
||||
retVal = FhgfsOpsErr_PATHNOTEXISTS;
|
||||
else
|
||||
{
|
||||
if(DirEntryType_ISDIR(removeDentry.getEntryType() ) )
|
||||
{ // remove dir link
|
||||
retVal = parentDir->removeDir(entryName, NULL);
|
||||
}
|
||||
else
|
||||
if(DirEntryType_ISFILE(removeDentry.getEntryType() ) )
|
||||
{
|
||||
retVal = parentDir->unlinkDirEntry(entryName, &removeDentry,
|
||||
DirEntry_UNLINK_ID_AND_FILENAME);
|
||||
}
|
||||
else
|
||||
{ // unknown entry type
|
||||
LogContext(logContext).logErr(std::string("Unknown entry type: ") +
|
||||
StringTk::intToStr(removeDentry.getEntryType() ) );
|
||||
|
||||
retVal = FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
// clean-up
|
||||
metaStore->releaseDir(parentInfo->getEntryID() );
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
17
meta/source/net/message/storage/creating/RmDirEntryMsgEx.h
Normal file
17
meta/source/net/message/storage/creating/RmDirEntryMsgEx.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/creating/RmDirEntryMsg.h>
|
||||
#include <storage/MetaStore.h>
|
||||
|
||||
// A repair operation, called from fhgfs-ctl
|
||||
|
||||
class RmDirEntryMsgEx : public RmDirEntryMsg
|
||||
{
|
||||
public:
|
||||
virtual bool processIncoming(ResponseContext& ctx);
|
||||
|
||||
private:
|
||||
FhgfsOpsErr rmDirEntry(EntryInfo* parentInfo, std::string& entryName);
|
||||
};
|
||||
|
||||
229
meta/source/net/message/storage/creating/RmDirMsgEx.cpp
Normal file
229
meta/source/net/message/storage/creating/RmDirMsgEx.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
#include <common/components/streamlistenerv2/IncomingPreprocessedMsgWork.h>
|
||||
#include <common/net/message/control/GenericResponseMsg.h>
|
||||
#include <common/net/message/storage/creating/RmDirRespMsg.h>
|
||||
#include <common/net/message/storage/creating/RmLocalDirMsg.h>
|
||||
#include <common/net/message/storage/creating/RmLocalDirRespMsg.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <components/FileEventLogger.h>
|
||||
#include <components/ModificationEventFlusher.h>
|
||||
#include <program/Program.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include "RmDirMsgEx.h"
|
||||
|
||||
|
||||
bool RmDirMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
BaseType::processIncoming(ctx);
|
||||
|
||||
// update operation counters
|
||||
updateNodeOp(ctx, MetaOpCounter_RMDIR);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock, FileIDLock, ParentNameLock> RmDirMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
EntryInfo delEntryInfo;
|
||||
|
||||
DirInode* parentDir = metaStore->referenceDir(
|
||||
getParentInfo()->getEntryID(), getParentInfo()->getIsBuddyMirrored(), true);
|
||||
if (!parentDir)
|
||||
return {};
|
||||
else
|
||||
{
|
||||
parentDir->getDirEntryInfo(getDelDirName(), delEntryInfo);
|
||||
metaStore->releaseDir(getParentInfo()->getEntryID());
|
||||
}
|
||||
|
||||
FileIDLock parentDirLock;
|
||||
FileIDLock delDirLock;
|
||||
HashDirLock hashLock;
|
||||
|
||||
if (resyncJob && resyncJob->isRunning())
|
||||
hashLock = {&store, MetaStorageTk::getMetaInodeHash(delEntryInfo.getEntryID())};
|
||||
|
||||
// lock directories in deadlock-avoidance order, see MirroredMessage::lock()
|
||||
if (delEntryInfo.getEntryID().empty())
|
||||
{
|
||||
parentDirLock = {&store, getParentInfo()->getEntryID(), true};
|
||||
}
|
||||
else if (delEntryInfo.getEntryID() < getParentInfo()->getEntryID())
|
||||
{
|
||||
delDirLock = {&store, delEntryInfo.getEntryID(), true};
|
||||
parentDirLock = {&store, getParentInfo()->getEntryID(), true};
|
||||
}
|
||||
else if (delEntryInfo.getEntryID() == getParentInfo()->getEntryID())
|
||||
{
|
||||
delDirLock = {&store, delEntryInfo.getEntryID(), true};
|
||||
}
|
||||
else
|
||||
{
|
||||
parentDirLock = {&store, getParentInfo()->getEntryID(), true};
|
||||
delDirLock = {&store, delEntryInfo.getEntryID(), true};
|
||||
}
|
||||
|
||||
ParentNameLock parentNameLock(&store, getParentInfo()->getEntryID(), getDelDirName());
|
||||
|
||||
return std::make_tuple(
|
||||
std::move(hashLock),
|
||||
std::move(parentDirLock),
|
||||
std::move(delDirLock),
|
||||
std::move(parentNameLock));
|
||||
}
|
||||
|
||||
std::unique_ptr<RmDirMsgEx::ResponseState> RmDirMsgEx::rmDir(ResponseContext& ctx,
|
||||
const bool isSecondary)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
ModificationEventFlusher* modEventFlusher = Program::getApp()->getModificationEventFlusher();
|
||||
bool modEventLoggingEnabled = modEventFlusher->isLoggingEnabled();
|
||||
|
||||
FhgfsOpsErr retVal;
|
||||
|
||||
EntryInfo* parentInfo = this->getParentInfo();
|
||||
const std::string& delDirName = this->getDelDirName();
|
||||
|
||||
EntryInfo outDelEntryInfo;
|
||||
|
||||
// reference parent
|
||||
DirInode* parentDir = metaStore->referenceDir(parentInfo->getEntryID(),
|
||||
parentInfo->getIsBuddyMirrored(), true);
|
||||
if(!parentDir)
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
|
||||
|
||||
DirEntry removeDirEntry(delDirName);
|
||||
bool getEntryRes = parentDir->getDirDentry(delDirName, removeDirEntry);
|
||||
if(!getEntryRes)
|
||||
retVal = FhgfsOpsErr_PATHNOTEXISTS;
|
||||
else
|
||||
{
|
||||
int additionalFlags = 0;
|
||||
|
||||
const std::string& parentEntryID = parentInfo->getEntryID();
|
||||
removeDirEntry.getEntryInfo(parentEntryID, additionalFlags, &outDelEntryInfo);
|
||||
|
||||
App* app = Program::getApp();
|
||||
NumNodeID ownerNodeID = outDelEntryInfo.getOwnerNodeID();
|
||||
|
||||
// no-comms path: the dir inode is owned by us
|
||||
if ((!isMirrored() && ownerNodeID == app->getLocalNode().getNumID()) ||
|
||||
(isMirrored() &&
|
||||
ownerNodeID.val() == app->getMetaBuddyGroupMapper()->getLocalGroupID()))
|
||||
retVal = app->getMetaStore()->removeDirInode(outDelEntryInfo.getEntryID(),
|
||||
outDelEntryInfo.getIsBuddyMirrored());
|
||||
else if (!isSecondary)
|
||||
retVal = rmRemoteDirInode(&outDelEntryInfo);
|
||||
else
|
||||
retVal = FhgfsOpsErr_SUCCESS;
|
||||
|
||||
if(retVal == FhgfsOpsErr_SUCCESS)
|
||||
{ // local removal succeeded => remove meta dir
|
||||
retVal = parentDir->removeDir(delDirName, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
if (retVal == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
fixInodeTimestamp(*parentDir, dirTimestamps);
|
||||
|
||||
// clean-up
|
||||
metaStore->releaseDir(parentInfo->getEntryID() );
|
||||
|
||||
if (!isSecondary && retVal == FhgfsOpsErr_SUCCESS && app->getFileEventLogger() && getFileEvent())
|
||||
{
|
||||
EventContext eventCtx = makeEventContext(&outDelEntryInfo, outDelEntryInfo.getParentEntryID(),
|
||||
getMsgHeaderUserID(), "", 0, isSecondary);
|
||||
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
|
||||
if (retVal == FhgfsOpsErr_SUCCESS && modEventLoggingEnabled)
|
||||
{
|
||||
const std::string& entryID = outDelEntryInfo.getEntryID();
|
||||
modEventFlusher->add(ModificationEvent_DIRREMOVED, entryID);
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(retVal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the inode of this directory
|
||||
*/
|
||||
FhgfsOpsErr RmDirMsgEx::rmRemoteDirInode(EntryInfo* delEntryInfo)
|
||||
{
|
||||
const char* logContext = "RmDirMsg (rm dir inode)";
|
||||
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
|
||||
|
||||
App* app = Program::getApp();
|
||||
NumNodeID ownerNodeID = delEntryInfo->getOwnerNodeID();
|
||||
|
||||
LOG_DEBUG(logContext, Log_DEBUG,
|
||||
"Removing dir inode from metadata node: " + ownerNodeID.str() + "; "
|
||||
"dirname: " + delEntryInfo->getFileName() + "; isBuddyMirrored: " +
|
||||
StringTk::intToStr(delEntryInfo->getIsBuddyMirrored()) );
|
||||
|
||||
// prepare request
|
||||
|
||||
RmLocalDirMsg rmMsg(delEntryInfo);
|
||||
|
||||
RequestResponseArgs rrArgs(NULL, &rmMsg, NETMSGTYPE_RmLocalDirResp);
|
||||
|
||||
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes() );
|
||||
rrNode.setTargetStates(app->getMetaStateStore() );
|
||||
|
||||
if (delEntryInfo->getIsBuddyMirrored())
|
||||
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
|
||||
|
||||
do // (this loop just exists to enable the "break"-jump, so it's not really a loop)
|
||||
{
|
||||
// send request to other mds and receive response
|
||||
|
||||
FhgfsOpsErr requestRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
|
||||
if(requestRes != FhgfsOpsErr_SUCCESS)
|
||||
{ // communication error
|
||||
LogContext(logContext).log(Log_WARNING,
|
||||
"Communication with metadata server failed. "
|
||||
"ownerNodeID: " + ownerNodeID.str() + "; "
|
||||
"dirID: " + delEntryInfo->getEntryID() + "; " +
|
||||
"dirname: " + delEntryInfo->getFileName() );
|
||||
retVal = requestRes;
|
||||
break;
|
||||
}
|
||||
|
||||
// correct response type received
|
||||
const auto rmRespMsg = (const RmLocalDirRespMsg*)rrArgs.outRespMsg.get();
|
||||
|
||||
retVal = rmRespMsg->getResult();
|
||||
if(unlikely( (retVal != FhgfsOpsErr_SUCCESS) && (retVal != FhgfsOpsErr_NOTEMPTY) ) )
|
||||
{ // error: dir inode not removed
|
||||
std::string errString = boost::lexical_cast<std::string>(retVal);
|
||||
|
||||
LogContext(logContext).log(Log_DEBUG,
|
||||
"Metadata server was unable to remove dir inode. "
|
||||
"ownerNodeID: " + ownerNodeID.str() + "; "
|
||||
"dirID: " + delEntryInfo->getEntryID() + "; " +
|
||||
"dirname: " + delEntryInfo->getFileName() + "; " +
|
||||
"error: " + errString);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// success: dir inode removed
|
||||
LOG_DEBUG(logContext, Log_DEBUG,
|
||||
"Metadata server removed dir inode: "
|
||||
"ownerNodeID: " + ownerNodeID.str() + "; "
|
||||
"dirID: " + delEntryInfo->getEntryID() + "; " +
|
||||
"dirname: " + delEntryInfo->getFileName() );
|
||||
|
||||
} while(false);
|
||||
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void RmDirMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_RmDirResp);
|
||||
}
|
||||
42
meta/source/net/message/storage/creating/RmDirMsgEx.h
Normal file
42
meta/source/net/message/storage/creating/RmDirMsgEx.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/creating/RmDirMsg.h>
|
||||
#include <common/net/message/storage/creating/RmDirRespMsg.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
|
||||
class RmDirMsgEx : public MirroredMessage<RmDirMsg,
|
||||
std::tuple<HashDirLock, FileIDLock, FileIDLock, ParentNameLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<RmDirRespMsg, NETMSGTYPE_RmDir> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock, FileIDLock, ParentNameLock>
|
||||
lock(EntryLockStore& store) override;
|
||||
|
||||
static FhgfsOpsErr rmRemoteDirInode(EntryInfo* delEntryInfo);
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override
|
||||
{
|
||||
return rmDir(ctx, isSecondary);
|
||||
}
|
||||
|
||||
bool isMirrored() override { return getParentInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<ResponseState> rmDir(ResponseContext& ctx, const bool isSecondary);
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<RmDirRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "RmDirMsgEx/forward"; }
|
||||
};
|
||||
|
||||
56
meta/source/net/message/storage/creating/RmLocalDirMsgEx.cpp
Normal file
56
meta/source/net/message/storage/creating/RmLocalDirMsgEx.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/creating/RmLocalDirRespMsg.h>
|
||||
#include <common/toolkit/MetaStorageTk.h>
|
||||
|
||||
#include "RmLocalDirMsgEx.h"
|
||||
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock> RmLocalDirMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
HashDirLock hashLock;
|
||||
|
||||
// if we are currently under modsync, we must lock the hash dir from which we are removing the
|
||||
// inode. otherwise bulk sync may interfere with mod sync and cause the resync to fail.
|
||||
// do not lock the hash dir if we are removing the inode from the same meta node as the dentry,
|
||||
// RmDir will have already locked the hash dir.
|
||||
if (!rctx->isLocallyGenerated() && resyncJob && resyncJob->isRunning())
|
||||
hashLock = {&store, MetaStorageTk::getMetaInodeHash(getDelEntryInfo()->getEntryID())};
|
||||
|
||||
return std::make_tuple(
|
||||
std::move(hashLock),
|
||||
FileIDLock(&store, getDelEntryInfo()->getEntryID(), true));
|
||||
}
|
||||
|
||||
bool RmLocalDirMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
rctx = &ctx;
|
||||
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> RmLocalDirMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
return rmDir();
|
||||
}
|
||||
|
||||
std::unique_ptr<RmLocalDirMsgEx::ResponseState> RmLocalDirMsgEx::rmDir()
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
EntryInfo* delEntryInfo = this->getDelEntryInfo();
|
||||
|
||||
LOG_DEBUG("RmLocalDirMsgEx (rmDir)", Log_DEBUG,
|
||||
"Removing local dir inode: " + delEntryInfo->getFileName() + "; isBuddyMirrored: " +
|
||||
StringTk::intToStr(delEntryInfo->getIsBuddyMirrored()) );
|
||||
|
||||
FhgfsOpsErr res = metaStore->removeDirInode(delEntryInfo->getEntryID(),
|
||||
delEntryInfo->getIsBuddyMirrored());
|
||||
|
||||
return boost::make_unique<ResponseState>(res);
|
||||
}
|
||||
|
||||
void RmLocalDirMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_RmLocalDirResp);
|
||||
}
|
||||
38
meta/source/net/message/storage/creating/RmLocalDirMsgEx.h
Normal file
38
meta/source/net/message/storage/creating/RmLocalDirMsgEx.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/creating/RmLocalDirMsg.h>
|
||||
#include <common/net/message/storage/creating/RmLocalDirRespMsg.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
class RmLocalDirMsgEx : public MirroredMessage<RmLocalDirMsg, std::tuple<HashDirLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<RmLocalDirRespMsg, NETMSGTYPE_RmLocalDir> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock> lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getDelEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
ResponseContext* rctx;
|
||||
|
||||
std::unique_ptr<ResponseState> rmDir();
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<RmLocalDirRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "RmLocalDirMsgEx/forward"; }
|
||||
};
|
||||
|
||||
294
meta/source/net/message/storage/creating/UnlinkFileMsgEx.cpp
Normal file
294
meta/source/net/message/storage/creating/UnlinkFileMsgEx.cpp
Normal file
@@ -0,0 +1,294 @@
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileInodeMsg.h>
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileInodeRespMsg.h>
|
||||
#include <components/FileEventLogger.h>
|
||||
#include <net/msghelpers/MsgHelperUnlink.h>
|
||||
#include <program/Program.h>
|
||||
#include "UnlinkFileMsgEx.h"
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock, ParentNameLock, FileIDLock> UnlinkFileMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
HashDirLock hashLock;
|
||||
FileIDLock inodeLock;
|
||||
|
||||
// we also have to lock the inode attached to the dentry - if we delete the inode, we must
|
||||
// exclude concurrent actions on the same inode. if we cannot look up a file inode for the
|
||||
// dentry, nothing bad happens.
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
auto dir = metaStore->referenceDir(getParentInfo()->getEntryID(),
|
||||
getParentInfo()->getIsBuddyMirrored(), false);
|
||||
|
||||
DirEntry dentry(getDelFileName());
|
||||
bool dentryExists = dir->getFileDentry(getDelFileName(), dentry);
|
||||
|
||||
if (dentryExists)
|
||||
{
|
||||
dentry.getEntryInfo(getParentInfo()->getEntryID(), 0, &fileInfo);
|
||||
|
||||
// lock hash dir where we are going to remove (or update) file inode
|
||||
// need to take it only if it is a non-inlined inode and resynch is running
|
||||
if (resyncJob && resyncJob->isRunning() && !dentry.getIsInodeInlined())
|
||||
hashLock = {&store, MetaStorageTk::getMetaInodeHash(dentry.getID())};
|
||||
}
|
||||
|
||||
FileIDLock dirLock(&store, getParentInfo()->getEntryID(), true);
|
||||
ParentNameLock dentryLock(&store, getParentInfo()->getEntryID(), getDelFileName());
|
||||
|
||||
if (dentryExists)
|
||||
inodeLock = {&store, dentry.getID(), true};
|
||||
|
||||
metaStore->releaseDir(dir->getID());
|
||||
return std::make_tuple(std::move(hashLock), std::move(dirLock), std::move(dentryLock), std::move(inodeLock));
|
||||
}
|
||||
|
||||
bool UnlinkFileMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
#ifdef BEEGFS_DEBUG
|
||||
const char* logContext = "UnlinkFileMsg incoming";
|
||||
|
||||
const std::string& removeName = getDelFileName();
|
||||
EntryInfo* parentInfo = getParentInfo();
|
||||
LOG_DEBUG(logContext, Log_DEBUG, "ParentID: " + parentInfo->getEntryID() + "; "
|
||||
"deleteName: " + removeName);
|
||||
#endif // BEEGFS_DEBUG
|
||||
|
||||
// update operation counters (here on top because we have an early sock release in this msg)
|
||||
updateNodeOp(ctx, MetaOpCounter_UNLINK);
|
||||
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> UnlinkFileMsgEx::executeLocally(
|
||||
ResponseContext& ctx, bool isSecondary)
|
||||
{
|
||||
const char* logContext = "Unlink File Msg";
|
||||
App* app = Program::getApp();
|
||||
MetaStore* metaStore = app->getMetaStore();
|
||||
|
||||
// reference parent dir
|
||||
DirInode* dir = metaStore->referenceDir(getParentInfo()->getEntryID(),
|
||||
getParentInfo()->getIsBuddyMirrored(), true);
|
||||
|
||||
if (!dir)
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
|
||||
|
||||
DirEntry dentryToRemove(getDelFileName());
|
||||
if (!dir->getFileDentry(getDelFileName(), dentryToRemove))
|
||||
{
|
||||
metaStore->releaseDir(dir->getID());
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
|
||||
}
|
||||
|
||||
// On meta-mirrored setup, ensure the dentry we just loaded i.e. 'dentryToRemove' has the same entryID as 'fileInfo'.
|
||||
// This check is crucial to detect a very rare race condition where dentry gets unlinked
|
||||
// because the parent directory was not locked during UnlinkFileMsgEx::lock(),
|
||||
// and then reappears because the same file is being created again. If the entryIDs
|
||||
// don't match, it means the dentry has changed, and the exclusive lock taken on the
|
||||
// entryID before won't be effective in preventing future races of ongoing unlink
|
||||
// operation with other filesystem operations (e.g. close()) which might happen on this file.
|
||||
// Therefore, we release the directory from the meta store and return an error saying path
|
||||
// not exists.
|
||||
if (isMirrored() && (dentryToRemove.getEntryID() != this->fileInfo.getEntryID()))
|
||||
{
|
||||
metaStore->releaseDir(dir->getID());
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
|
||||
}
|
||||
|
||||
// re-fetch entryInfo
|
||||
dentryToRemove.getEntryInfo(getParentInfo()->getEntryID(), 0, &fileInfo);
|
||||
|
||||
// check whether local node/group owns file's inode (dentry's owner may/maynot be same)
|
||||
NumNodeID ownerNodeID = fileInfo.getOwnerNodeID();
|
||||
if ( (!isMirrored() && ownerNodeID == app->getLocalNode().getNumID()) ||
|
||||
(isMirrored() &&
|
||||
ownerNodeID.val() == app->getMetaBuddyGroupMapper()->getLocalGroupID()) )
|
||||
{
|
||||
// File inode is on the same metadata node/buddy group as the dentry.
|
||||
if (isSecondary)
|
||||
return executeSecondary(ctx, *dir);
|
||||
else
|
||||
return executePrimary(ctx, *dir);
|
||||
}
|
||||
else
|
||||
{ // Handle case where file inode is on a different metadata node/buddy group than dentry.
|
||||
// Step 1: Remove file's dentry (local operation)
|
||||
// Note: Dentry was already loaded and validated earlier
|
||||
FhgfsOpsErr unlinkRes = FhgfsOpsErr_INTERNAL;
|
||||
unlinkRes = dir->unlinkDirEntry(getDelFileName(), &dentryToRemove, DirEntry_UNLINK_FILENAME);
|
||||
metaStore->releaseDir(dir->getID());
|
||||
if (unlinkRes != FhgfsOpsErr_SUCCESS)
|
||||
return boost::make_unique<ResponseState>(unlinkRes);
|
||||
|
||||
// Step 2: Remove file's inode (remote operation)
|
||||
if (!isSecondary)
|
||||
{
|
||||
unsigned preUnlinkHardlinkCount = 0;
|
||||
|
||||
UnlinkLocalFileInodeMsg unlinkInodeMsg(&fileInfo);
|
||||
RequestResponseArgs rrArgs(NULL, &unlinkInodeMsg, NETMSGTYPE_UnlinkLocalFileInodeResp);
|
||||
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes());
|
||||
rrNode.setTargetStates(app->getMetaStateStore());
|
||||
if (fileInfo.getIsBuddyMirrored())
|
||||
{
|
||||
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
FhgfsOpsErr resp = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
if (unlikely(resp != FhgfsOpsErr_SUCCESS))
|
||||
{
|
||||
LogContext(logContext).log(Log_WARNING,
|
||||
"Communication with metadata server failed. "
|
||||
"nodeID: " + ownerNodeID.str() + "; " +
|
||||
"entryID: " + fileInfo.getEntryID().c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
// response received
|
||||
const auto respMsg = (UnlinkLocalFileInodeRespMsg*) rrArgs.outRespMsg.get();
|
||||
FhgfsOpsErr res = respMsg->getResult();
|
||||
if (res != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
// error: either inode file doesn't exists or some other error happened
|
||||
LogContext(logContext).log(Log_WARNING, "unlink file inode failed! "
|
||||
"nodeID: " + ownerNodeID.str() + "; " +
|
||||
"entryID: " + fileInfo.getEntryID().c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
// since dentry has been removed successfully so all good for user - no need
|
||||
// to return an error if unlinking remote inode fails due to some reasons.
|
||||
// we still need to remove dentry from secondary buddy so should not overwrite
|
||||
// local dentry removal success with some remote error
|
||||
unlinkRes = FhgfsOpsErr_SUCCESS;
|
||||
preUnlinkHardlinkCount = respMsg->getPreUnlinkHardlinkCount();
|
||||
} while (false);
|
||||
|
||||
if (unlinkRes == FhgfsOpsErr_SUCCESS && app->getFileEventLogger() && getFileEvent())
|
||||
{
|
||||
// Determine remaining hardlinks after unlink operation
|
||||
unsigned remainingLinks = (preUnlinkHardlinkCount > 0) ? preUnlinkHardlinkCount - 1 : 0;
|
||||
|
||||
EventContext eventCtx = makeEventContext(&fileInfo, getParentInfo()->getEntryID(),
|
||||
getMsgHeaderUserID(), "", remainingLinks, isSecondary);
|
||||
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(unlinkRes);
|
||||
}
|
||||
|
||||
// Should never reach this point but added to please the compiler
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_SUCCESS);
|
||||
}
|
||||
|
||||
std::unique_ptr<UnlinkFileMsgEx::ResponseState> UnlinkFileMsgEx::executePrimary(
|
||||
ResponseContext& ctx, DirInode& dir)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
unsigned preUnlinkHardlinkCount = 0; // number of hardlink(s) before unlink happens
|
||||
|
||||
// Two alternatives:
|
||||
// 1) early response before chunk files unlink.
|
||||
// 2) normal response after chunk files unlink (incl. chunk files error).
|
||||
if (app->getConfig()->getTuneEarlyUnlinkResponse() && !isMirrored())
|
||||
{
|
||||
// alternative 1: response before chunk files unlink
|
||||
std::unique_ptr<FileInode> unlinkedInode;
|
||||
|
||||
FhgfsOpsErr unlinkMetaRes = MsgHelperUnlink::unlinkMetaFile(dir,
|
||||
getDelFileName(), &unlinkedInode, preUnlinkHardlinkCount);
|
||||
|
||||
app->getMetaStore()->releaseDir(dir.getID());
|
||||
earlyComplete(ctx, ResponseState(unlinkMetaRes));
|
||||
|
||||
/* note: if the file is still opened or if there were hardlinks then unlinkedInode will be
|
||||
NULL even on FhgfsOpsErr_SUCCESS */
|
||||
if ((unlinkMetaRes == FhgfsOpsErr_SUCCESS) && unlinkedInode)
|
||||
MsgHelperUnlink::unlinkChunkFiles(unlinkedInode.release(), getMsgHeaderUserID() );
|
||||
|
||||
if (unlinkMetaRes == FhgfsOpsErr_SUCCESS && app->getFileEventLogger() && getFileEvent())
|
||||
{
|
||||
// Determine remaining hardlinks after unlink operation
|
||||
unsigned remainingLinks = (preUnlinkHardlinkCount > 0) ? preUnlinkHardlinkCount - 1 : 0;
|
||||
|
||||
EventContext eventCtx = makeEventContext(&fileInfo, getParentInfo()->getEntryID(),
|
||||
// This is not the secondary node if executePrimary() was called.
|
||||
getMsgHeaderUserID(), "", remainingLinks, false);
|
||||
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// alternative 2: response after chunk files unlink
|
||||
std::unique_ptr<FileInode> unlinkedInode;
|
||||
FhgfsOpsErr unlinkRes = MsgHelperUnlink::unlinkMetaFile(dir,
|
||||
getDelFileName(), &unlinkedInode, preUnlinkHardlinkCount);
|
||||
|
||||
if ((unlinkRes == FhgfsOpsErr_SUCCESS) && shouldFixTimestamps())
|
||||
{
|
||||
fixInodeTimestamp(dir, dirTimestamps);
|
||||
if (!unlinkedInode)
|
||||
{
|
||||
auto [file, referenceRes] = app->getMetaStore()->referenceFile(&fileInfo);
|
||||
if (file)
|
||||
{
|
||||
fixInodeTimestamp(*file, fileTimestamps, &fileInfo);
|
||||
app->getMetaStore()->releaseFile(dir.getID(), file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* note: if the file is still opened or if there are/were hardlinks then unlinkedInode will be
|
||||
NULL even on FhgfsOpsErr_SUCCESS */
|
||||
if ((unlinkRes == FhgfsOpsErr_SUCCESS) && unlinkedInode)
|
||||
MsgHelperUnlink::unlinkChunkFiles(unlinkedInode.release(), getMsgHeaderUserID());
|
||||
|
||||
app->getMetaStore()->releaseDir(dir.getID());
|
||||
if ((unlinkRes == FhgfsOpsErr_SUCCESS) && app->getFileEventLogger() && getFileEvent())
|
||||
{
|
||||
// Determine remaining hardlinks after unlink operation
|
||||
unsigned remainingLinks = (preUnlinkHardlinkCount > 0) ? preUnlinkHardlinkCount - 1 : 0;
|
||||
|
||||
EventContext eventCtx = makeEventContext(&fileInfo, getParentInfo()->getEntryID(),
|
||||
// This is not the secondary node if executePrimary() was called.
|
||||
getMsgHeaderUserID(), "", remainingLinks, false);
|
||||
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(unlinkRes);
|
||||
}
|
||||
|
||||
std::unique_ptr<UnlinkFileMsgEx::ResponseState> UnlinkFileMsgEx::executeSecondary(
|
||||
ResponseContext& ctx, DirInode& dir)
|
||||
{
|
||||
MetaStore* const metaStore = Program::getApp()->getMetaStore();
|
||||
std::unique_ptr<FileInode> unlinkedInode;
|
||||
unsigned preUnlinkHardlinkCount; // Not used here!
|
||||
|
||||
FhgfsOpsErr unlinkMetaRes = MsgHelperUnlink::unlinkMetaFile(dir,
|
||||
getDelFileName(), &unlinkedInode, preUnlinkHardlinkCount);
|
||||
|
||||
if ((unlinkMetaRes == FhgfsOpsErr_SUCCESS) && shouldFixTimestamps())
|
||||
{
|
||||
fixInodeTimestamp(dir, dirTimestamps);
|
||||
if (!unlinkedInode)
|
||||
{
|
||||
auto [file, referenceRes] = metaStore->referenceFile(&fileInfo);
|
||||
if (file)
|
||||
{
|
||||
fixInodeTimestamp(*file, fileTimestamps, &fileInfo);
|
||||
metaStore->releaseFile(dir.getID(), file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
metaStore->releaseDir(dir.getID());
|
||||
return boost::make_unique<ResponseState>(unlinkMetaRes);
|
||||
}
|
||||
|
||||
void UnlinkFileMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_UnlinkFileResp);
|
||||
}
|
||||
45
meta/source/net/message/storage/creating/UnlinkFileMsgEx.h
Normal file
45
meta/source/net/message/storage/creating/UnlinkFileMsgEx.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/creating/UnlinkFileMsg.h>
|
||||
#include <common/net/message/storage/creating/UnlinkFileRespMsg.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
class UnlinkFileMsgEx : public MirroredMessage<UnlinkFileMsg,
|
||||
std::tuple<HashDirLock, FileIDLock, ParentNameLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<UnlinkFileRespMsg, NETMSGTYPE_UnlinkFile> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock, ParentNameLock, FileIDLock> lock(EntryLockStore& store) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
bool isMirrored() override { return getParentInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
/**
|
||||
* Execute unlink operation on primary and secondary metadata nodes.
|
||||
* Primary handles metadata removal, chunk cleanup, and event logging.
|
||||
* Secondary handles only metadata operations.
|
||||
*
|
||||
* @param ctx Response context
|
||||
* @param dir Parent directory reference. Functions guarantee directory reference
|
||||
* release before return, including error paths.
|
||||
* @return Response state containing operation result.
|
||||
*/
|
||||
std::unique_ptr<ResponseState> executePrimary(ResponseContext& ctx, DirInode& dir);
|
||||
std::unique_ptr<ResponseState> executeSecondary(ResponseContext& ctx, DirInode& dir);
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<UnlinkFileRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "UnlinkFileMsgEx/forward"; }
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/toolkit/MetaStorageTk.h>
|
||||
#include <net/msghelpers/MsgHelperUnlink.h>
|
||||
#include "UnlinkLocalFileInodeMsgEx.h"
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock> UnlinkLocalFileInodeMsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
// we must not lock hash dir and inode if it is owned by current node. if it is a locally
|
||||
// generated message which is sent by unlinkmsg and renamev2msg sends then aforementioned
|
||||
// locks are already held by them
|
||||
if (rctx->isLocallyGenerated())
|
||||
return {};
|
||||
|
||||
HashDirLock hashLock;
|
||||
|
||||
if (resyncJob && resyncJob->isRunning())
|
||||
HashDirLock hashLock = {&store, MetaStorageTk::getMetaInodeHash(getDelEntryInfo()->getEntryID())};
|
||||
|
||||
return std::make_tuple(
|
||||
std::move(hashLock),
|
||||
FileIDLock(&store, getDelEntryInfo()->getEntryID(), true));
|
||||
}
|
||||
|
||||
bool UnlinkLocalFileInodeMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
rctx = &ctx;
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> UnlinkLocalFileInodeMsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
// Create a copy of the passed-in entryInfo and use it!
|
||||
// Reason:
|
||||
// Some fields of EntryInfo, such as parentEntryID and isInlined, can be modified
|
||||
// by MetaStore::unlinkInodeLater() during unlink processing. Using a copy ensures
|
||||
// that the original (and unmodified) entryInfo is forwarded to the secondary buddy
|
||||
// to avoid different states on meta-buddies.
|
||||
EntryInfo entryInfo;
|
||||
entryInfo.set(getDelEntryInfo());
|
||||
|
||||
UnlinkLocalFileInodeResponseState resp;
|
||||
unsigned outLinkCount = 0;
|
||||
|
||||
std::unique_ptr<FileInode> unlinkedInode;
|
||||
FhgfsOpsErr unlinkInodeRes = MsgHelperUnlink::unlinkFileInode(&entryInfo, &unlinkedInode, outLinkCount);
|
||||
resp.setResult(unlinkInodeRes);
|
||||
resp.setPreUnlinkHardlinkCount(outLinkCount);
|
||||
|
||||
if ((unlinkInodeRes == FhgfsOpsErr_SUCCESS) && unlinkedInode && !isSecondary)
|
||||
{
|
||||
MsgHelperUnlink::unlinkChunkFiles(unlinkedInode.release(), getMsgHeaderUserID());
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(std::move(resp));
|
||||
}
|
||||
|
||||
void UnlinkLocalFileInodeMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_UnlinkLocalFileInodeResp);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileInodeMsg.h>
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileInodeRespMsg.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
class UnlinkLocalFileInodeResponseState : public MirroredMessageResponseState
|
||||
{
|
||||
public:
|
||||
UnlinkLocalFileInodeResponseState() : result(FhgfsOpsErr_INTERNAL), preUnlinkHardlinkCount(0) {}
|
||||
|
||||
UnlinkLocalFileInodeResponseState(FhgfsOpsErr result, unsigned linkCount) :
|
||||
result(result), preUnlinkHardlinkCount(linkCount) {}
|
||||
|
||||
explicit UnlinkLocalFileInodeResponseState(Deserializer& des)
|
||||
{
|
||||
serialize(this, des);
|
||||
}
|
||||
|
||||
UnlinkLocalFileInodeResponseState(UnlinkLocalFileInodeResponseState&& other) :
|
||||
result(other.result), preUnlinkHardlinkCount(other.preUnlinkHardlinkCount) {}
|
||||
|
||||
void sendResponse(NetMessage::ResponseContext& ctx) override
|
||||
{
|
||||
UnlinkLocalFileInodeRespMsg resp(result, preUnlinkHardlinkCount);
|
||||
ctx.sendResponse(resp);
|
||||
}
|
||||
|
||||
void setResult(FhgfsOpsErr res) { this->result = res; }
|
||||
void setPreUnlinkHardlinkCount(unsigned linkCount) { this->preUnlinkHardlinkCount = linkCount; }
|
||||
bool changesObservableState() const override { return true; }
|
||||
|
||||
protected:
|
||||
uint32_t serializerTag() const override { return NETMSGTYPE_UnlinkLocalFileInodeResp; }
|
||||
|
||||
template<typename This, typename Ctx>
|
||||
static void serialize(This obj, Ctx& ctx)
|
||||
{
|
||||
ctx
|
||||
% obj->result
|
||||
% obj->preUnlinkHardlinkCount;
|
||||
}
|
||||
|
||||
void serializeContents(Serializer& ser) const override
|
||||
{
|
||||
serialize(this, ser);
|
||||
}
|
||||
|
||||
private:
|
||||
FhgfsOpsErr result;
|
||||
// The preUnlinkHardlinkCount represents the number of hardlinks that existed before
|
||||
// the actual unlink happens. This is mainly needed for event logging purposes.
|
||||
unsigned preUnlinkHardlinkCount;
|
||||
};
|
||||
|
||||
class UnlinkLocalFileInodeMsgEx : public MirroredMessage<UnlinkLocalFileInodeMsg,
|
||||
std::tuple<HashDirLock, FileIDLock>>
|
||||
{
|
||||
public:
|
||||
typedef UnlinkLocalFileInodeResponseState ResponseState;
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
std::tuple<HashDirLock, FileIDLock> lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getDelEntryInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
ResponseContext* rctx;
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<UnlinkLocalFileInodeRespMsg&>(resp).getResult();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override
|
||||
{
|
||||
return "UnlinkLocalFileInodeMsgEx/forward";
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user