2025-08-10 01:34:16 +02:00

304 lines
10 KiB
C++

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