New upstream version 8.1.0

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

View File

@@ -0,0 +1,303 @@
#include <program/Program.h>
#include <common/net/message/storage/creating/HardlinkRespMsg.h>
#include <common/net/message/storage/creating/MoveFileInodeMsg.h>
#include <common/net/message/storage/creating/MoveFileInodeRespMsg.h>
#include <common/net/message/storage/attribs/SetAttrMsg.h>
#include <common/net/message/storage/attribs/SetAttrRespMsg.h>
#include <common/toolkit/MessagingTk.h>
#include <components/FileEventLogger.h>
#include "HardlinkMsgEx.h"
std::tuple<FileIDLock, ParentNameLock, ParentNameLock, FileIDLock> HardlinkMsgEx::lock(
EntryLockStore& store)
{
// NOTE: normally we'd need to also lock on the MDS holding the destination file,
// but we don't support hardlinks to different servers, yet
ParentNameLock fromLock;
ParentNameLock toLock;
FileIDLock fileLock;
FileIDLock dirLock(&store, getToDirInfo()->getEntryID(), true);
// take care about lock ordering! see MirroredMessage::lock()
if (getFromDirInfo()->getEntryID() < getToDirInfo()->getEntryID()
|| (getFromDirInfo()->getEntryID() == getToDirInfo()->getEntryID()
&& getFromName() < getToName()))
{
fromLock = {&store, getFromDirInfo()->getEntryID(), getFromName()};
toLock = {&store, getToDirInfo()->getEntryID(), getToName()};
}
else if (getFromDirInfo()->getEntryID() == getToDirInfo()->getEntryID()
&& getFromName() == getToName())
{
fromLock = {&store, getFromDirInfo()->getEntryID(), getFromName()};
}
else
{
toLock = {&store, getToDirInfo()->getEntryID(), getToName()};
fromLock = {&store, getFromDirInfo()->getEntryID(), getFromName()};
}
// We need to lock the inode because hardlinking modifies the link count.
// If the file inode is present on the current meta node or buddy-group,
// then acquire the inodeLock.
App* app = Program::getApp();
NumNodeID ownerNodeID = getFromInfo()->getOwnerNodeID();
bool isLocalInode = ((!isMirrored() && ownerNodeID == app->getLocalNode().getNumID()) ||
(isMirrored() && ownerNodeID.val() == app->getMetaBuddyGroupMapper()->getLocalGroupID()));
if (isLocalInode)
fileLock = {&store, getFromInfo()->getEntryID(), true};
return std::make_tuple(
std::move(dirLock),
std::move(fromLock),
std::move(toLock),
std::move(fileLock));
}
bool HardlinkMsgEx::processIncoming(ResponseContext& ctx)
{
LOG_DBG(GENERAL, DEBUG, "",
("fromDirID", getFromDirInfo()->getEntryID()),
("fromID", getFromInfo()->getEntryID()),
("fromName", getFromName()),
("toDirID", getToDirInfo()->getEntryID()),
("toName", getToName()),
("buddyMirrored", getToDirInfo()->getIsBuddyMirrored()));
BaseType::processIncoming(ctx);
updateNodeOp(ctx, MetaOpCounter_HARDLINK);
return true;
}
std::unique_ptr<MirroredMessageResponseState> HardlinkMsgEx::executeLocally(ResponseContext& ctx,
bool isSecondary)
{
const char* logContext = "create hard link";
App* app = Program::getApp();
MetaStore* metaStore = app->getMetaStore();
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
unsigned updatedLinkCount = 0;
// reference directory where hardlink needs to be created
DirInode* toDir = metaStore->referenceDir(getToDirInfo()->getEntryID(),
getToDirInfo()->getIsBuddyMirrored(), true);
// check if file dentry already exists with same name as hardlink
// return error if link already exists
if (likely(toDir))
{
DirEntry newLink(getToName());
if (toDir->getFileDentry(getToName(), newLink))
{
metaStore->releaseDir(toDir->getID());
return boost::make_unique<ResponseState>(FhgfsOpsErr_EXISTS);
}
}
else
{
LogContext(logContext).logErr(std::string("target directory for hard-link doesn't exist,"
"dirname: " + getToDirInfo()->getFileName()));
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
}
// Prevent hardlinks across directories with different buddy mirror configurations
if (getFromDirInfo()->getIsBuddyMirrored() != getToDirInfo()->getIsBuddyMirrored())
{
LogContext(logContext).logErr("Hardlinks not supported across directories with different "
"buddy mirror config setting. SourceDirID: " + getFromDirInfo()->getEntryID() +
"; TargetDirID: " + getToDirInfo()->getEntryID());
metaStore->releaseDir(toDir->getID());
return boost::make_unique<ResponseState>(FhgfsOpsErr_NOTSUPP);
}
// check whether local node/group owns source file's inode or not
NumNodeID ownerNodeID = getFromInfo()->getOwnerNodeID();
bool isLocalOwner = ((!isMirrored() && ownerNodeID == app->getLocalNode().getNumID()) ||
(isMirrored() && ownerNodeID.val() == app->getMetaBuddyGroupMapper()->getLocalGroupID()));
if (isLocalOwner)
{
std::tie(retVal, updatedLinkCount) = metaStore->makeNewHardlink(getFromInfo());
}
else if (!isSecondary)
{
MoveFileInodeMsg deinlineMsg(getFromInfo(), MODE_DEINLINE, true);
RequestResponseArgs rrArgs(NULL, &deinlineMsg, NETMSGTYPE_MoveFileInodeResp);
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes());
rrNode.setTargetStates(app->getMetaStateStore());
if (getFromInfo()->getIsBuddyMirrored())
{
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
}
do
{
// send request to other MDs and receive response
FhgfsOpsErr resp = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if (unlikely(resp != FhgfsOpsErr_SUCCESS))
{
// error
LogContext(logContext).logErr("Communication with metadata server failed. "
"nodeID: " + ownerNodeID.str());
retVal = resp;
break;
}
// response received
const auto moveFileInodeRespMsg = (MoveFileInodeRespMsg*) rrArgs.outRespMsg.get();
FhgfsOpsErr res = moveFileInodeRespMsg->getResult();
if (res != FhgfsOpsErr_SUCCESS)
{
// error: either source file not exists or deinline operation failed
LogContext(logContext).logErr("verify and move file inode failed! nodeID: "
+ ownerNodeID.str() + "; entryID: " + getFromInfo()->getEntryID());
retVal = res;
break;
}
// success
retVal = res;
updatedLinkCount = moveFileInodeRespMsg->getHardlinkCount();
} while (false);
}
// If verify/deinline inode and link count update is successful - only then create new
// dentry in "toDir" with entryID and ownerNodeID of "fromFile" which might reside
// on another meta node/buddy-group
if (retVal == FhgfsOpsErr_SUCCESS)
{
DirEntry newHardlink(DirEntryType_REGULARFILE, getToName(),
getFromInfo()->getEntryID(), getFromInfo()->getOwnerNodeID());
newHardlink.removeDentryFeatureFlag(DENTRY_FEATURE_INODE_INLINE);
// buddy mirroring is inherited from parent directory
if (toDir->getIsBuddyMirrored())
newHardlink.setBuddyMirrorFeatureFlag();
FhgfsOpsErr makeRes = toDir->makeDirEntry(newHardlink);
if (makeRes != FhgfsOpsErr_SUCCESS)
{
// compensate link count if dentry create fails
//
// for a remote inode, Use SetAttrMsg to decrease link count
// since its a mirrored message so it will update link count on both primary and
// secondary hence don't send this message from secondary buddy
if (!isSecondary && !isLocalOwner)
incDecRemoteLinkCount(ownerNodeID, false);
else
metaStore->incDecLinkCount(getFromInfo(), -1);
retVal = makeRes;
}
}
if (retVal == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
{
fixInodeTimestamp(*toDir, dirTimestamps);
if (isLocalOwner)
{
auto [file, referenceRes] = metaStore->referenceFile(getFromInfo());
if (file)
{
fixInodeTimestamp(*file, fileTimestamps, getFromInfo());
metaStore->releaseFile(getFromInfo()->getParentEntryID(), file);
}
}
}
metaStore->releaseDir(toDir->getID());
if (!isSecondary && retVal == FhgfsOpsErr_SUCCESS && app->getFileEventLogger() && getFileEvent())
{
EventContext eventCtx = makeEventContext(getFromInfo(), getFromInfo()->getParentEntryID(),
getMsgHeaderUserID(), "", updatedLinkCount, isSecondary);
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
}
return boost::make_unique<ResponseState>(retVal);
}
FhgfsOpsErr HardlinkMsgEx::incDecRemoteLinkCount(NumNodeID const& ownerNodeID, bool increment)
{
const char* logContext = "incDecRemoteLinkCount";
App* app = Program::getApp();
FhgfsOpsErr retVal = FhgfsOpsErr_INTERNAL;
SettableFileAttribs dummyAttrib = {};
SetAttrMsg msg(getFromInfo(), 0, &dummyAttrib);
if (increment)
{
msg.addMsgHeaderFeatureFlag(SETATTRMSG_FLAG_INCR_NLINKCNT);
}
else
{
msg.addMsgHeaderFeatureFlag(SETATTRMSG_FLAG_DECR_NLINKCNT);
}
RequestResponseArgs rrArgs(NULL, &msg, NETMSGTYPE_SetAttrResp);
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes());
rrNode.setTargetStates(app->getMetaStateStore());
if (getFromInfo()->getIsBuddyMirrored())
{
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
}
do
{
// send request to other MDs and receive response
FhgfsOpsErr resp = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if (unlikely(resp != FhgfsOpsErr_SUCCESS))
{
// error
LogContext(logContext).log(Log_WARNING,
"Communication with metadata server failed. "
"nodeID: " + ownerNodeID.str());
retVal = resp;
break;
}
// response received
const auto incLinkCountResp = (SetAttrRespMsg*) rrArgs.outRespMsg.get();
FhgfsOpsErr res = static_cast<FhgfsOpsErr>(incLinkCountResp->getValue());
if (res != FhgfsOpsErr_SUCCESS)
{
// error: either source file not exists or nlink count increment failed
std::string operationType = (increment ? "increase": "decrease");
LogContext(logContext).logErr("nLink count " + operationType
+ " failed! nodeID: " + ownerNodeID.str() + "; "
+ "entryID: " + getFromInfo()->getEntryID());
retVal = res;
break;
}
// success
retVal = res;
} while (false);
return retVal;
}
void HardlinkMsgEx::forwardToSecondary(ResponseContext& ctx)
{
sendToSecondary(ctx, *this, NETMSGTYPE_HardlinkResp);
}

View File

@@ -0,0 +1,38 @@
#pragma once
#include <common/storage/StorageErrors.h>
#include <common/net/message/storage/creating/HardlinkMsg.h>
#include <common/net/message/storage/creating/HardlinkRespMsg.h>
#include <session/EntryLock.h>
#include <storage/DirEntry.h>
#include <storage/MetaStore.h>
#include <net/message/MirroredMessage.h>
class HardlinkMsgEx : public MirroredMessage<HardlinkMsg,
std::tuple<FileIDLock, ParentNameLock, ParentNameLock, FileIDLock>>
{
public:
typedef ErrorCodeResponseState<HardlinkRespMsg, NETMSGTYPE_Hardlink> ResponseState;
virtual bool processIncoming(ResponseContext& ctx) override;
std::tuple<FileIDLock, ParentNameLock, ParentNameLock, FileIDLock>
lock(EntryLockStore& store) override;
bool isMirrored() override { return getFromInfo()->getIsBuddyMirrored(); }
private:
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
bool isSecondary) override;
void forwardToSecondary(ResponseContext& ctx) override;
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
{
return (FhgfsOpsErr) static_cast<HardlinkRespMsg&>(resp).getValue();
}
FhgfsOpsErr incDecRemoteLinkCount(NumNodeID const& ownerNodeID, bool increment);
const char* mirrorLogContext() const override { return "HardlinkMsgEx/forward"; }
};

View File

@@ -0,0 +1,411 @@
#include <common/components/streamlistenerv2/IncomingPreprocessedMsgWork.h>
#include <common/net/message/control/GenericResponseMsg.h>
#include <common/net/message/storage/creating/MkLocalDirMsg.h>
#include <common/net/message/storage/creating/MkLocalDirRespMsg.h>
#include <common/net/message/storage/creating/MkDirRespMsg.h>
#include <common/toolkit/MessagingTk.h>
#include <components/FileEventLogger.h>
#include <components/ModificationEventFlusher.h>
#include <program/Program.h>
#include <storage/PosixACL.h>
#include "RmDirMsgEx.h"
#include "MkDirMsgEx.h"
bool MkDirMsgEx::processIncoming(ResponseContext& ctx)
{
App* app = Program::getApp();
entryID = StorageTk::generateFileID(app->getLocalNode().getNumID());
BaseType::processIncoming(ctx);
// update operation counters
updateNodeOp(ctx, MetaOpCounter_MKDIR);
return true;
}
std::unique_ptr<MirroredMessageResponseState> MkDirMsgEx::executeLocally(ResponseContext& ctx,
bool isSecondary)
{
auto result = isSecondary
? mkDirSecondary()
: mkDirPrimary(ctx);
if (result && result->getResult() != FhgfsOpsErr_SUCCESS)
LOG_DBG(GENERAL, DEBUG, "Failed to create directory",
("parentID", getParentInfo()->getEntryID()),
("newDirName", getNewDirName()),
("error", result->getResult()));
return result;
}
std::tuple<HashDirLock, FileIDLock, ParentNameLock> MkDirMsgEx::lock(EntryLockStore& store)
{
HashDirLock hashLock;
// during resync we must lock the hash dir of the new inode even if the inode will be created on
// a different node because we select the target node for the inode only after we have locked our
// structures.
if (resyncJob && resyncJob->isRunning())
hashLock = {&store, MetaStorageTk::getMetaInodeHash(entryID)};
FileIDLock dirLock(&store, getParentInfo()->getEntryID(), true);
ParentNameLock dentryLock(&store, getParentInfo()->getEntryID(), getNewDirName());
return std::make_tuple(std::move(hashLock), std::move(dirLock), std::move(dentryLock));
}
std::unique_ptr<MkDirMsgEx::ResponseState> MkDirMsgEx::mkDirPrimary(ResponseContext& ctx)
{
const char* logContext = "MkDirMsg (mkDirPrimary)";
App* app = Program::getApp();
ModificationEventFlusher* modEventFlusher = app->getModificationEventFlusher();
const bool modEventLoggingEnabled = modEventFlusher->isLoggingEnabled();
MetaStore* metaStore = app->getMetaStore();
Config* config = app->getConfig();
MirrorBuddyGroupMapper* metaBuddyGroupMapper = app->getMetaBuddyGroupMapper();
NodeCapacityPools* metaCapacityPools;
NumNodeID expectedOwnerID;
const EntryInfo* const parentInfo = getParentInfo();
const std::string& newName = getNewDirName();
FhgfsOpsErr retVal;
// not a good idea to use scoped locks here, because we don't have a well-defined scope with
// only buddy mirrored paths here; directly use entrylockstore
EntryLockStore* entryLockStore = Program::getApp()->getSessions()->getEntryLockStore();
// reference parent
DirInode* parentDir = metaStore->referenceDir(parentInfo->getEntryID(),
parentInfo->getIsBuddyMirrored(), true);
if(!parentDir)
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS, EntryInfo());
// check whether localNode owns this (parent) directory
NumNodeID localNodeID = app->getLocalNodeNumID();
bool isBuddyMirrored = parentDir->getIsBuddyMirrored()
&& !isMsgHeaderFeatureFlagSet(MKDIRMSG_FLAG_NOMIRROR);
// check whether localNode owns this (parent) directory; if parentDir is buddy mirrored compare
// ownership to buddy group id, otherwise to node id itself
if (parentDir->getIsBuddyMirrored())
expectedOwnerID = NumNodeID(metaBuddyGroupMapper->getLocalGroupID() );
else
expectedOwnerID = localNodeID;
if (isBuddyMirrored)
metaCapacityPools = app->getMetaBuddyCapacityPools();
else
metaCapacityPools = app->getMetaCapacityPools();
if(parentDir->getOwnerNodeID() != expectedOwnerID)
{ // this node doesn't own the parent dir
LogContext(logContext).logErr(std::string("Dir-owner mismatch: \"") +
parentDir->getOwnerNodeID().str() + "\" vs. \"" +
expectedOwnerID.str() + "\"");
metaStore->releaseDir(parentInfo->getEntryID() );
return boost::make_unique<ResponseState>(FhgfsOpsErr_NOTOWNER, EntryInfo());
}
// choose new directory owner...
unsigned numDesiredTargets = 1;
unsigned minNumRequiredTargets = numDesiredTargets;
UInt16Vector newOwnerNodes;
metaCapacityPools->chooseStorageTargets(numDesiredTargets, minNumRequiredTargets,
&getPreferredNodes(), &newOwnerNodes);
if(unlikely(newOwnerNodes.size() < minNumRequiredTargets) )
{ // (might be caused by a bad list of preferred targets)
LogContext(logContext).logErr("No metadata servers available for new directory: " + newName);
metaStore->releaseDir(parentInfo->getEntryID() );
// we know that *some* metadata server must exist, since we are obviously active when we get
// here. most likely a client has received a pool update before we have, or we have been
// switched from secondary to primary and haven't been set to Good yet.
// if preferred nodes have been set (currently only done by ctl), those may also be registered
// as unavailable at the current time.
// have the client retry until things work out.
return boost::make_unique<ResponseState>(FhgfsOpsErr_COMMUNICATION, EntryInfo());
}
const uint16_t ownerNodeID = newOwnerNodes[0];
const std::string parentEntryID = parentInfo->getEntryID();
int entryInfoFlags = isBuddyMirrored ? ENTRYINFO_FEATURE_BUDDYMIRRORED : 0;
int mode = getMode();
const int umask = getUmask();
CharVector parentDefaultACLXAttr;
CharVector accessACLXAttr;
if (config->getStoreClientACLs())
{
// Determine the ACLs of the new directory.
PosixACL parentDefaultACL;
bool needsACL;
FhgfsOpsErr parentDefaultACLRes;
std::tie(parentDefaultACLRes, parentDefaultACLXAttr, std::ignore) = parentDir->getXAttr(
nullptr, PosixACL::defaultACLXAttrName, XATTR_SIZE_MAX);
if (parentDefaultACLRes == FhgfsOpsErr_SUCCESS)
{
// parent has a default ACL
if (!parentDefaultACL.deserializeXAttr(parentDefaultACLXAttr))
{
LogContext(logContext).log(Log_ERR,
"Error deserializing directory default ACL for directory ID " + parentDir->getID());
retVal = FhgfsOpsErr_INTERNAL;
goto clean_up;
}
if (!parentDefaultACL.empty())
{
// Note: This modifies the mode bits as well as the ACL itself.
FhgfsOpsErr modeRes = parentDefaultACL.modifyModeBits(mode, needsACL);
setMode(mode, 0);
if (modeRes != FhgfsOpsErr_SUCCESS)
{
LogContext(logContext).log(Log_ERR, "Error generating access ACL for new directory "
+ newName);
retVal = FhgfsOpsErr_INTERNAL;
goto clean_up;
}
if (needsACL)
parentDefaultACL.serializeXAttr(accessACLXAttr);
}
else
{
// On empty ACL, clear the Xattr, so it doesn't get set on the newly created dir
parentDefaultACLXAttr.clear();
}
}
if (parentDefaultACLRes == FhgfsOpsErr_NODATA
|| (parentDefaultACLRes == FhgfsOpsErr_SUCCESS && parentDefaultACL.empty()))
{
// containing dir has no ACL, so we can continue without one.
mode &= ~umask;
setMode(mode, umask);
}
if (parentDefaultACLRes != FhgfsOpsErr_SUCCESS && parentDefaultACLRes != FhgfsOpsErr_NODATA)
{
LogContext(logContext).log(Log_ERR,
"Error loading default ACL for directory ID " + parentDir->getID() );
retVal = parentDefaultACLRes;
goto clean_up;
}
}
newEntryInfo.set(NumNodeID(ownerNodeID), parentEntryID, entryID, newName,
DirEntryType_DIRECTORY, entryInfoFlags);
// create remote dir metadata
// (we create this before the dentry to reduce the risk of dangling dentries)
retVal = mkRemoteDirInode(*parentDir, newName, &newEntryInfo, parentDefaultACLXAttr,
accessACLXAttr);
if ( likely(retVal == FhgfsOpsErr_SUCCESS) )
{ // remote dir created => create dentry in parent dir
// note: we can't lock before this point, because mkRemoteDirInode will also send a message
// that needs to aquire a lock on the ID
FileIDLock lock;
if (parentInfo->getIsBuddyMirrored())
lock = {entryLockStore, entryID, true};
retVal = mkDirDentry(*parentDir, newName, &newEntryInfo, isBuddyMirrored);
if ( retVal != FhgfsOpsErr_SUCCESS )
{ // error (or maybe name just existed already) => compensate metaDir creation
// note: unlock needs to happen before a possible remoteDirCompensation, because rmDir
// will also need to lock entryID
lock = {};
mkRemoteDirCompensate(&newEntryInfo);
}
else
{
if (app->getFileEventLogger() && getFileEvent())
{
EventContext eventCtx = makeEventContext(&newEntryInfo, newEntryInfo.getParentEntryID(),
// This is not the secondary node if mkDirPrimary() was called.
getMsgHeaderUserID(), "", INITIAL_DIR_LINK_COUNT, false);
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
}
}
}
clean_up:
metaStore->releaseDir(parentInfo->getEntryID() );
if (modEventLoggingEnabled)
modEventFlusher->add(ModificationEvent_DIRCREATED, newEntryInfo.getEntryID());
return boost::make_unique<ResponseState>(retVal, std::move(newEntryInfo));
}
std::unique_ptr<MkDirMsgEx::ResponseState> MkDirMsgEx::mkDirSecondary()
{
MetaStore* metaStore = Program::getApp()->getMetaStore();
// only create the dentry here; forwarding of inode creation directly happens in MkLocalFileMsg
// and error handling and compensation is done by primary
FhgfsOpsErr retVal;
// reference parent
DirInode* parentDir = metaStore->referenceDir(getParentInfo()->getEntryID(),
getParentInfo()->getIsBuddyMirrored(), true);
if(!parentDir)
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS, EntryInfo());
retVal = mkDirDentry(*parentDir, getCreatedEntryInfo()->getFileName(), getCreatedEntryInfo(),
getCreatedEntryInfo()->getIsBuddyMirrored());
metaStore->releaseDir(getParentInfo()->getEntryID());
return boost::make_unique<ResponseState>(retVal, *getCreatedEntryInfo());
}
FhgfsOpsErr MkDirMsgEx::mkDirDentry(DirInode& parentDir, const std::string& name,
const EntryInfo* entryInfo, const bool isBuddyMirrored)
{
const std::string entryID = entryInfo->getEntryID();
const NumNodeID ownerNodeID = entryInfo->getOwnerNodeID();
DirEntry newDirDentry(DirEntryType_DIRECTORY, name, entryID, ownerNodeID);
if(isBuddyMirrored)
newDirDentry.setBuddyMirrorFeatureFlag();
const FhgfsOpsErr mkRes = parentDir.makeDirEntry(newDirDentry);
if (mkRes == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
fixInodeTimestamp(parentDir, parentTimestamps);
return mkRes;
}
/**
* Create dir inode on a remote server.
*
* @param name only used for logging
* @param mirrorNodeID 0 for disabled mirroring
*/
FhgfsOpsErr MkDirMsgEx::mkRemoteDirInode(DirInode& parentDir, const std::string& name,
EntryInfo* entryInfo, const CharVector& defaultACLXAttr, const CharVector& accessACLXAttr)
{
const char* logContext = "MkDirMsg (mk dir inode)";
App* app = Program::getApp();
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
StripePattern* pattern = parentDir.getStripePatternClone();
NumNodeID ownerNodeID = entryInfo->getOwnerNodeID();
RemoteStorageTarget rstInfo;
if (parentDir.getIsRstAvailable())
rstInfo.set(parentDir.getRemoteStorageTargetInfo());
LOG_DEBUG(logContext, Log_DEBUG,
"Creating dir inode at metadata node: " + ownerNodeID.str() + "; dirname: " + name);
// prepare request
NumNodeID parentNodeID = app->getLocalNode().getNumID();
MkLocalDirMsg mkMsg(entryInfo, getUserID(), getGroupID(), getMode(), pattern, &rstInfo,
parentNodeID, defaultACLXAttr, accessACLXAttr);
RequestResponseArgs rrArgs(NULL, &mkMsg, NETMSGTYPE_MkLocalDirResp);
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes() );
rrNode.setTargetStates(app->getMetaStateStore() );
if(entryInfo->getIsBuddyMirrored())
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
do // (this loop just exists to enable the "break"-jump, so it's not really a loop)
{
// send request to other mds and receive response
FhgfsOpsErr requestRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if(unlikely(requestRes != FhgfsOpsErr_SUCCESS) )
{ // communication error
LogContext(logContext).log(Log_WARNING,
"Communication with metadata server failed. "
"nodeID: " + ownerNodeID.str() + "; " +
"dirname: " + name);
retVal = requestRes;
break;
}
// correct response type received
const auto mkRespMsg = (const MkLocalDirRespMsg*)rrArgs.outRespMsg.get();
FhgfsOpsErr mkRemoteInodeRes = mkRespMsg->getResult();
if(mkRemoteInodeRes != FhgfsOpsErr_SUCCESS)
{ // error: remote dir inode not created
LogContext(logContext).log(Log_WARNING,
"Metadata server failed to create dir inode. "
"nodeID: " + ownerNodeID.str() + "; " +
"dirname: " + name);
retVal = mkRemoteInodeRes;
break;
}
// success: remote dir inode created
LOG_DEBUG(logContext, Log_DEBUG,
"Metadata server created dir inode. "
"nodeID: " + ownerNodeID.str() + "; "
"dirname: " + name);
} while(false);
delete(pattern);
return retVal;
}
/**
* Remove dir metadata on a remote server to compensate for creation.
*/
FhgfsOpsErr MkDirMsgEx::mkRemoteDirCompensate(EntryInfo* entryInfo)
{
LogContext log("MkDirMsg (undo dir inode [" + entryInfo->getFileName() + "])");
FhgfsOpsErr rmRes = RmDirMsgEx::rmRemoteDirInode(entryInfo);
if(unlikely(rmRes != FhgfsOpsErr_SUCCESS) )
{ // error
log.log(Log_WARNING, std::string("Compensation not completely successful. ") +
"File system might contain (uncritical) inconsistencies.");
return rmRes;
}
log.log(Log_SPAM, "Creation of dir inode compensated");
return rmRes;
}
void MkDirMsgEx::forwardToSecondary(ResponseContext& ctx)
{
// secondary needs to know the created entryInfo, because it needs to use the same information
setCreatedEntryInfo(&newEntryInfo);
// not needed on secondary
clearPreferredNodes();
sendToSecondary(ctx, *this, NETMSGTYPE_MkDirResp);
}

View File

@@ -0,0 +1,53 @@
#pragma once
#include <common/storage/StorageErrors.h>
#include <common/net/message/storage/creating/MkDirMsg.h>
#include <common/net/message/storage/creating/MkDirRespMsg.h>
#include <session/EntryLock.h>
#include <storage/MetaStore.h>
#include <net/message/MirroredMessage.h>
class MkDirMsgEx : public MirroredMessage<MkDirMsg, std::tuple<HashDirLock, FileIDLock, ParentNameLock>>
{
public:
typedef ErrorAndEntryResponseState<MkDirRespMsg, NETMSGTYPE_MkDir> ResponseState;
virtual bool processIncoming(ResponseContext& ctx) override;
std::tuple<HashDirLock, FileIDLock, ParentNameLock> lock(EntryLockStore& store) override;
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
bool isSecondary) override;
bool isMirrored() override { return getParentInfo()->getIsBuddyMirrored(); }
private:
// Initial hard link count for a newly created directory (self and "." entry).
// Note: ".." entry increments the parent's link count, not this directory's.
static constexpr unsigned INITIAL_DIR_LINK_COUNT = 2;
std::string entryID;
std::unique_ptr<ResponseState> mkDirPrimary(ResponseContext& ctx);
std::unique_ptr<ResponseState> mkDirSecondary();
FhgfsOpsErr mkDirDentry(DirInode& parentDir, const std::string& name,
const EntryInfo* entryInfo, const bool isBuddyMirrored);
FhgfsOpsErr mkRemoteDirInode(DirInode& parentDir, const std::string& name,
EntryInfo* entryInfo, const CharVector& defaultACLXAttr, const CharVector& accessACLXAttr);
FhgfsOpsErr mkRemoteDirCompensate(EntryInfo* entryInfo);
void forwardToSecondary(ResponseContext& ctx) override;
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
{
return (FhgfsOpsErr) static_cast<MkDirRespMsg&>(resp).getResult();
}
const char* mirrorLogContext() const override { return "MkDirMsgEx/forward"; }
EntryInfo newEntryInfo;
};

View File

@@ -0,0 +1,136 @@
#include <common/net/message/storage/creating/MkFileRespMsg.h>
#include <common/net/message/control/GenericResponseMsg.h>
#include <common/toolkit/MessagingTk.h>
#include <common/storage/StatData.h>
#include <components/FileEventLogger.h>
#include <net/msghelpers/MsgHelperMkFile.h>
#include <program/Program.h>
#include "MkFileMsgEx.h"
std::tuple<FileIDLock, ParentNameLock, FileIDLock> MkFileMsgEx::lock(EntryLockStore& store)
{
FileIDLock dirLock(&store, getParentInfo()->getEntryID(), true);
ParentNameLock dentryLock(&store, getParentInfo()->getEntryID(), getNewName());
FileIDLock fileLock(&store, newEntryID, true);
return std::make_tuple(
std::move(dirLock),
std::move(dentryLock),
std::move(fileLock));
}
bool MkFileMsgEx::processIncoming(ResponseContext& ctx)
{
#ifdef BEEGFS_DEBUG
const char* logContext = "MkFileMsg incoming";
#endif // BEEGFS_DEBUG
LOG_DEBUG(logContext, Log_DEBUG, "parentEntryID: " +
getParentInfo()->getEntryID() + " newFileName: " + getNewName() );
LOG_DEBUG(logContext, Log_DEBUG,
"BuddyMirrored: " + std::string(getParentInfo()->getIsBuddyMirrored() ? "'Yes'" : "'No'") +
" Secondary: " + std::string(hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond) ?
"Yes" : "No") );
if (!hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
createTime = TimeAbs().getTimeval()->tv_sec;
mkDetails = {getNewName(), getUserID(), getGroupID(), getMode(), getUmask(), createTime};
if (getParentInfo()->getIsBuddyMirrored())
{
if (!hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
newEntryID = StorageTk::generateFileID(Program::getApp()->getLocalNode().getNumID());
else
newEntryID = getNewEntryID();
mkDetails.setNewEntryID(newEntryID.c_str());
}
BaseType::processIncoming(ctx);
updateNodeOp(ctx, MetaOpCounter_MKFILE);
return true;
}
std::unique_ptr<MirroredMessageResponseState> MkFileMsgEx::executeLocally(ResponseContext& ctx,
bool isSecondary)
{
if (isSecondary)
return executeSecondary();
else
return executePrimary();
}
std::unique_ptr<MkFileMsgEx::ResponseState> MkFileMsgEx::executePrimary()
{
DirInode* dir = Program::getApp()->getMetaStore()->referenceDir(
getParentInfo()->getEntryID(), getParentInfo()->getIsBuddyMirrored(), true);
if (!dir)
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS, entryInfo);
FhgfsOpsErr mkRes;
if (isMsgHeaderFeatureFlagSet(MKFILEMSG_FLAG_STORAGEPOOLID))
{
mkRes = MsgHelperMkFile::mkFile(*dir, &mkDetails, &getPreferredNodes(),
getNumTargets(), getChunkSize(), NULL, getRemoteStorageTarget(),
&entryInfo, &inodeData, storagePoolId);
}
else
{
mkRes = MsgHelperMkFile::mkFile(*dir, &mkDetails, &getPreferredNodes(),
getNumTargets(), getChunkSize(), NULL, getRemoteStorageTarget(), &entryInfo, &inodeData);
}
if (mkRes == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
fixInodeTimestamp(*dir, dirTimestamps);
Program::getApp()->getMetaStore()->releaseDir(dir->getID());
if (mkRes == FhgfsOpsErr_SUCCESS && Program::getApp()->getFileEventLogger() && getFileEvent())
{
EventContext eventCtx = makeEventContext(&entryInfo, entryInfo.getParentEntryID(),
// This is not the secondary node if executePrimary() was called.
getMsgHeaderUserID(), "", this->inodeData.getInodeStatData()->getNumHardlinks(), false);
logEvent(Program::getApp()->getFileEventLogger(), *getFileEvent(), eventCtx);
}
return boost::make_unique<ResponseState>(mkRes, entryInfo);
}
std::unique_ptr<MkFileMsgEx::ResponseState> MkFileMsgEx::executeSecondary()
{
StripePattern* stripePattern = getPattern().clone();
FileInodeStoreData inodeData;
DirInode* dir = Program::getApp()->getMetaStore()->referenceDir(
getParentInfo()->getEntryID(), getParentInfo()->getIsBuddyMirrored(), true);
if (!dir)
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS, entryInfo);
FhgfsOpsErr mkRes = MsgHelperMkFile::mkFile(*dir, &mkDetails, &getPreferredNodes(),
getNumTargets(), getChunkSize(), stripePattern, getRemoteStorageTarget(), &entryInfo, &inodeData);
if (mkRes == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
fixInodeTimestamp(*dir, dirTimestamps);
Program::getApp()->getMetaStore()->releaseDir(dir->getID());
return boost::make_unique<ResponseState>(mkRes, entryInfo);
}
void MkFileMsgEx::forwardToSecondary(ResponseContext& ctx)
{
// secondary needs to know the created entryID and stripe pattern, because it needs to use the
// same information
setNewEntryID(newEntryID.c_str());
setPattern(inodeData.getStripePattern());
setRemoteStorageTarget(getRemoteStorageTarget());
sendToSecondary(ctx, *this, NETMSGTYPE_MkFileResp);
}

View File

@@ -0,0 +1,48 @@
#pragma once
#include <common/storage/StorageErrors.h>
#include <common/net/message/storage/creating/MkFileMsg.h>
#include <common/net/message/storage/creating/MkFileRespMsg.h>
#include <session/EntryLock.h>
#include <storage/MetaStore.h>
#include <net/message/MirroredMessage.h>
class MkFileMsgEx : public MirroredMessage<MkFileMsg,
std::tuple<FileIDLock, ParentNameLock, FileIDLock>>
{
public:
typedef ErrorAndEntryResponseState<MkFileRespMsg, NETMSGTYPE_MkFile> ResponseState;
MkFileMsgEx()
: mkDetails({}, 0, 0, 0, 0, 0)
{
}
virtual bool processIncoming(ResponseContext& ctx) override;
std::tuple<FileIDLock, ParentNameLock, FileIDLock> lock(EntryLockStore& store) override;
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
bool isSecondary) override;
bool isMirrored() override { return getParentInfo()->getIsBuddyMirrored(); }
private:
std::unique_ptr<ResponseState> executePrimary();
std::unique_ptr<ResponseState> executeSecondary();
void forwardToSecondary(ResponseContext& ctx) override;
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
{
return (FhgfsOpsErr) static_cast<MkFileRespMsg&>(resp).getResult();
}
const char* mirrorLogContext() const override { return "MkFileMsgEx/forward"; }
FileInodeStoreData inodeData;
MkFileDetails mkDetails;
std::string newEntryID;
EntryInfo entryInfo;
};

View File

@@ -0,0 +1,206 @@
#include <common/net/message/storage/creating/MkFileMsg.h>
#include <common/net/message/storage/creating/MkFileRespMsg.h>
#include <program/Program.h>
#include <common/net/message/storage/creating/UnlinkLocalFileMsg.h>
#include <common/net/message/storage/creating/UnlinkLocalFileRespMsg.h>
#include <common/storage/striping/StripePattern.h>
#include <common/toolkit/MathTk.h>
#include <common/toolkit/MessagingTk.h>
#include <components/FileEventLogger.h>
#include <net/msghelpers/MsgHelperMkFile.h>
#include <session/EntryLock.h>
#include "MkFileWithPatternMsgEx.h"
// Called from fhgfs-ctl (online_cfg) to create a file on a specific node.
std::tuple<FileIDLock, ParentNameLock, FileIDLock> MkFileWithPatternMsgEx::lock(
EntryLockStore& store)
{
// no need to lock the created file as well, since
// a) no other operation can create the same file id
// b) until we finish, no part of the system except us knows the new file id
// c) if bulk resync gets the file while it is incomplete, individual resync will get it again
FileIDLock dirLock(&store, getParentInfo()->getEntryID(), true);
ParentNameLock dentryLock(&store, getParentInfo()->getEntryID(), getNewFileName());
FileIDLock fileLock(&store, entryID, true);
return std::make_tuple(std::move(dirLock), std::move(dentryLock), std::move(fileLock));
}
bool MkFileWithPatternMsgEx::processIncoming(ResponseContext& ctx)
{
const EntryInfo* parentInfo = getParentInfo();
std::string newFileName = getNewFileName();
EntryInfo newEntryInfo;
LOG_DEBUG("MkFileWithPatternMsg", Log_DEBUG,
" parentDirID: " + parentInfo->getEntryID() + " newFileName: " + newFileName
+ " isBuddyMirrored: " + (parentInfo->getIsBuddyMirrored() ? "'true'" : "'false'"));
// create ID first
if (parentInfo->getIsBuddyMirrored())
entryID = StorageTk::generateFileID(Program::getApp()->getLocalNode().getNumID());
return BaseType::processIncoming(ctx);
}
std::unique_ptr<MirroredMessageResponseState> MkFileWithPatternMsgEx::executeLocally(
ResponseContext& ctx, bool isSecondary)
{
EntryInfo newEntryInfo;
MkFileDetails mkDetails(getNewFileName(), getUserID(), getGroupID(), getMode(), getUmask(),
TimeAbs().getTimeval()->tv_sec);
if (!entryID.empty())
mkDetails.setNewEntryID(entryID.c_str());
FhgfsOpsErr mkRes = mkFile(getParentInfo(), mkDetails, &newEntryInfo, inodeDiskData);
updateNodeOp(ctx, MetaOpCounter_MKFILE);
return boost::make_unique<ResponseState>(mkRes, std::move(newEntryInfo));
}
/**
* @param dir current directory
* @param currentDepth 1-based path depth
*/
FhgfsOpsErr MkFileWithPatternMsgEx::mkFile(const EntryInfo* parentInfo, MkFileDetails& mkDetails,
EntryInfo* outEntryInfo, FileInodeStoreData& inodeDiskData)
{
MetaStore* metaStore = Program::getApp()->getMetaStore();
FhgfsOpsErr retVal;
// reference parent
DirInode* dir = metaStore->referenceDir(parentInfo->getEntryID(),
parentInfo->getIsBuddyMirrored(), true);
if ( !dir )
return FhgfsOpsErr_PATHNOTEXISTS;
// create meta file
retVal = mkMetaFile(*dir, mkDetails, outEntryInfo, inodeDiskData);
if (shouldFixTimestamps())
fixInodeTimestamp(*dir, dirTimestamps);
// clean-up
metaStore->releaseDir(parentInfo->getEntryID());
return retVal;
}
/**
* Create an inode and directory-entry
*/
FhgfsOpsErr MkFileWithPatternMsgEx::mkMetaFile(DirInode& dir, MkFileDetails& mkDetails,
EntryInfo* outEntryInfo, FileInodeStoreData& inodeDiskData)
{
// note: to guarantee amtomicity of file creation (from the view of a client), we have
// to create the inode first and insert the directory entry afterwards
std::unique_ptr<StripePattern> stripePattern(getPattern().clone());
const UInt16Vector* stripeTargets = stripePattern->getStripeTargetIDs();
StoragePoolId storagePoolId = stripePattern->getStoragePoolId();
if (stripeTargets->empty())
{
StoragePoolPtr storagePool =
Program::getApp()->getStoragePoolStore()->getPool(storagePoolId);
if (!storagePool)
{
LOG(GENERAL, ERR, "Given Storage Pool ID "
+ StringTk::uintToStr(storagePoolId.val()) + " doesn't exist.");
return FhgfsOpsErr_INTERNAL;
}
UInt16Vector chosenStripeTargets;
if(stripePattern->getPatternType() == StripePatternType_BuddyMirror)
{
storagePool->getBuddyCapacityPools()->chooseStorageTargets(
stripePattern->getDefaultNumTargets(), stripePattern->getMinNumTargets(),
nullptr, &chosenStripeTargets);
}
else
{
storagePool->getTargetCapacityPools()->chooseStorageTargets(
stripePattern->getDefaultNumTargets(), stripePattern->getMinNumTargets(),
nullptr, &chosenStripeTargets);
}
stripePattern->getStripeTargetIDsModifyable()->swap(chosenStripeTargets);
}
// check if num targets and target list match and if targets actually exist
if(stripeTargets->empty() || (stripeTargets->size() < stripePattern->getMinNumTargets() ) )
{
LOG(GENERAL, ERR, "No (or not enough) storage targets defined.",
("numTargets", stripeTargets->size()),
("expectedMinNumTargets", stripePattern->getMinNumTargets()));
return FhgfsOpsErr_INTERNAL;
}
for (auto iter = stripeTargets->begin(); iter != stripeTargets->end(); iter++)
{
if(stripePattern->getPatternType() == StripePatternType_BuddyMirror)
{
if (!Program::getApp()->getStorageBuddyGroupMapper()->getPrimaryTargetID(*iter))
{
LOG(GENERAL, ERR, "Unknown buddy group targets defined.",
("targetId", *iter));
return FhgfsOpsErr_UNKNOWNTARGET;
}
}
else
{
if (!Program::getApp()->getTargetMapper()->targetExists(*iter))
{
LOG(GENERAL, ERR, "Unknown storage targets defined.",
("targetId", *iter));
return FhgfsOpsErr_UNKNOWNTARGET;
}
}
}
// check if chunk size satisfies constraints
if (!MathTk::isPowerOfTwo(stripePattern->getChunkSize()))
{
LOG(GENERAL, DEBUG, "Invalid chunk size: Must be a power of two.",
stripePattern->getChunkSize());
return FhgfsOpsErr_INTERNAL;
}
if (stripePattern->getChunkSize() < STRIPEPATTERN_MIN_CHUNKSIZE)
{
LOG(GENERAL, DEBUG, "Invalid chunk size: Below minimum size.",
stripePattern->getChunkSize(),
("minChunkSize", STRIPEPATTERN_MIN_CHUNKSIZE));
return FhgfsOpsErr_INTERNAL;
}
return Program::getApp()->getMetaStore()->mkNewMetaFile(
dir, &mkDetails, std::move(stripePattern), getRemoteStorageTarget(), outEntryInfo,
&inodeDiskData);
// (note: internally deletes stripePattern)
}
void MkFileWithPatternMsgEx::forwardToSecondary(ResponseContext& ctx)
{
UInt16List preferredTargets; // can be an empty dummy
std::string newFileName = getNewFileName();
MkFileMsg mkFileMsg(getParentInfo(), newFileName, getUserID(), getGroupID(), getMode(),
getUmask(), &preferredTargets);
mkFileMsg.addFlag(NetMessageHeader::Flag_BuddyMirrorSecond);
// secondary needs to know the created entryID ans stripe pattern, because it needs to use the
// same information
mkFileMsg.setNewEntryID(entryID.c_str());
mkFileMsg.setPattern(inodeDiskData.getStripePattern());
mkFileMsg.setDirTimestamps(dirTimestamps);
mkFileMsg.setCreateTime(inodeDiskData.getInodeStatData()->getCreationTimeSecs());
mkFileMsg.setRemoteStorageTarget(getRemoteStorageTarget());
sendToSecondary(ctx, mkFileMsg, NETMSGTYPE_MkFileResp);
}

View File

@@ -0,0 +1,51 @@
#pragma once
#include <common/net/message/storage/creating/MkFileRespMsg.h>
#include <common/net/message/storage/creating/MkFileWithPatternMsg.h>
#include <common/net/message/storage/creating/MkFileWithPatternRespMsg.h>
#include <common/storage/StorageErrors.h>
#include <net/message/MirroredMessage.h>
#include <session/EntryLock.h>
#include <storage/MetaStore.h>
/**
* Similar to class MsgHelperMkFile, but called with a create pattern, for example from fhgfs-ctl
* or from ioctl calls.
*/
class MkFileWithPatternMsgEx : public MirroredMessage<MkFileWithPatternMsg,
std::tuple<FileIDLock, ParentNameLock, FileIDLock>>
{
public:
typedef ErrorAndEntryResponseState<MkFileWithPatternRespMsg, NETMSGTYPE_MkFileWithPattern>
ResponseState;
virtual bool processIncoming(ResponseContext& ctx) override;
std::tuple<FileIDLock, ParentNameLock, FileIDLock> lock(EntryLockStore& store) override;
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
bool isSecondary) override;
bool isMirrored() override { return getParentInfo()->getIsBuddyMirrored(); }
private:
MirroredTimestamps dirTimestamps;
FhgfsOpsErr mkFile(const EntryInfo* parentInfo, MkFileDetails& mkDetails,
EntryInfo* outEntryInfo, FileInodeStoreData& inodeDiskData);
FhgfsOpsErr mkMetaFile(DirInode& dir, MkFileDetails& mkDetails,
EntryInfo* outEntryInfo, FileInodeStoreData& inodeDiskData);
void forwardToSecondary(ResponseContext& ctx) override;
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
{
return (FhgfsOpsErr) static_cast<MkFileRespMsg&>(resp).getResult();
}
const char* mirrorLogContext() const override { return "MkFileWithPatternMsgEx/forward"; }
FileInodeStoreData inodeDiskData;
std::string entryID;
};

View File

@@ -0,0 +1,76 @@
#include <common/net/message/control/GenericResponseMsg.h>
#include <program/Program.h>
#include <common/net/message/storage/creating/MkLocalDirRespMsg.h>
#include "MkLocalDirMsgEx.h"
HashDirLock MkLocalDirMsgEx::lock(EntryLockStore& store)
{
// we usually need not lock anything here, because the inode ID will be completely unknown to
// anyone until we finish processing here *and* on the metadata server that sent this message.
// during resync though we need to lock the hash dir to avoid interefence between bulk resync and
// mod resync.
// do not lock the hash dir if we are creating the inode on the same meta node as the dentry,
// MkDir will have already locked the hash dir.
if (!rctx->isLocallyGenerated() && resyncJob && resyncJob->isRunning())
return {&store, MetaStorageTk::getMetaInodeHash(getEntryInfo()->getEntryID())};
return {};
}
bool MkLocalDirMsgEx::processIncoming(ResponseContext& ctx)
{
EntryInfo* entryInfo = getEntryInfo();
LOG_DBG(GENERAL, DEBUG, "", entryInfo->getEntryID(), entryInfo->getFileName());
(void) entryInfo;
rctx = &ctx;
return BaseType::processIncoming(ctx);
}
std::unique_ptr<MirroredMessageResponseState> MkLocalDirMsgEx::executeLocally(ResponseContext& ctx,
bool isSecondary)
{
App* app = Program::getApp();
MetaStore* metaStore = app->getMetaStore();
StripePattern& pattern = getPattern();
RemoteStorageTarget* rstInfo = getRemoteStorageTarget();
EntryInfo *entryInfo = getEntryInfo();
NumNodeID parentNodeID = getParentNodeID();
NumNodeID ownerNodeID = entryInfo->getIsBuddyMirrored()
? NumNodeID(app->getMetaBuddyGroupMapper()->getLocalGroupID() )
: app->getLocalNode().getNumID();
DirInode newDir(entryInfo->getEntryID(), getMode(), getUserID(),
getGroupID(), ownerNodeID, pattern, entryInfo->getIsBuddyMirrored());
newDir.setParentInfoInitial(entryInfo->getParentEntryID(), parentNodeID);
FhgfsOpsErr mkRes = metaStore->makeDirInode(newDir, getDefaultACLXAttr(), getAccessACLXAttr() );
if (!rstInfo->hasInvalidVersion() && (mkRes == FhgfsOpsErr_SUCCESS))
{
FhgfsOpsErr setRstRes = newDir.setRemoteStorageTarget(*rstInfo);
if (setRstRes != FhgfsOpsErr_SUCCESS)
{
LogContext("MkLocalDir").log(Log_WARNING, "Failed to set remote storage targets for "
"dirID: " + newDir.getID() + ". RST might be invalid.");
}
}
if (mkRes == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
fixInodeTimestamp(newDir, dirTimestamps);
return boost::make_unique<ResponseState>(mkRes);
}
void MkLocalDirMsgEx::forwardToSecondary(ResponseContext& ctx)
{
sendToSecondary(ctx, *this, NETMSGTYPE_MkLocalDirResp);
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include <common/storage/StorageErrors.h>
#include <common/net/message/storage/creating/MkLocalDirMsg.h>
#include <common/net/message/storage/creating/MkLocalDirRespMsg.h>
#include <net/message/MirroredMessage.h>
#include <session/EntryLock.h>
#include <storage/MetaStore.h>
class MkLocalDirMsgEx : public MirroredMessage<MkLocalDirMsg, HashDirLock>
{
public:
typedef ErrorCodeResponseState<MkLocalDirRespMsg, NETMSGTYPE_MkLocalDir> ResponseState;
virtual bool processIncoming(ResponseContext& ctx) override;
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
bool isSecondary) override;
HashDirLock lock(EntryLockStore& store) override;
bool isMirrored() override { return getEntryInfo()->getIsBuddyMirrored(); }
private:
ResponseContext* rctx;
void forwardToSecondary(ResponseContext& ctx) override;
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
{
return (FhgfsOpsErr) static_cast<MkLocalDirRespMsg&>(resp).getValue();
}
const char* mirrorLogContext() const override { return "MkLocalDirMsgEx/forward"; }
};

View File

@@ -0,0 +1,67 @@
#include <common/net/message/storage/creating/MoveFileInodeRespMsg.h>
#include "MoveFileInodeMsgEx.h"
std::tuple<FileIDLock, ParentNameLock, FileIDLock> MoveFileInodeMsgEx::lock(EntryLockStore& store)
{
EntryInfo* fileInfo = getFromFileEntryInfo();
FileIDLock dirLock(&store, fileInfo->getParentEntryID(), true);
ParentNameLock dentryLock(&store, fileInfo->getParentEntryID(), fileInfo->getFileName());
FileIDLock inodeLock(&store, fileInfo->getEntryID(), true);
return std::make_tuple(
std::move(dirLock),
std::move(dentryLock),
std::move(inodeLock));
}
bool MoveFileInodeMsgEx::processIncoming(ResponseContext& ctx)
{
rctx = &ctx;
return BaseType::processIncoming(ctx);
}
std::unique_ptr<MirroredMessageResponseState> MoveFileInodeMsgEx::executeLocally(ResponseContext& ctx,
bool isSecondary)
{
App* app = Program::getApp();
MetaStore* metaStore = app->getMetaStore();
MoveFileInodeMsgResponseState resp;
if (getMode() == FileInodeMode::MODE_INVALID)
{
// invalid operation requested
return boost::make_unique<ResponseState>(std::move(resp));
}
EntryInfo* fromFileInfo = getFromFileEntryInfo();
FhgfsOpsErr retVal = FhgfsOpsErr_INTERNAL;
unsigned linkCount = 0;
if (getCreateHardlink())
{
std::tie(retVal, linkCount) = metaStore->makeNewHardlink(fromFileInfo);
}
else
{
DirInode* parentDir = metaStore->referenceDir(
fromFileInfo->getParentEntryID(), fromFileInfo->getIsBuddyMirrored(), true);
if (likely(parentDir))
{
retVal = metaStore->verifyAndMoveFileInode(*parentDir, fromFileInfo, getMode());
metaStore->releaseDir(parentDir->getID());
}
else
retVal = FhgfsOpsErr_PATHNOTEXISTS;
}
resp.setResult(retVal);
resp.setHardlinkCount(linkCount);
return boost::make_unique<ResponseState>(std::move(resp));
}
void MoveFileInodeMsgEx::forwardToSecondary(ResponseContext& ctx)
{
sendToSecondary(ctx, *this, NETMSGTYPE_MoveFileInodeResp);
}

View File

@@ -0,0 +1,78 @@
#pragma once
#include <common/storage/StorageErrors.h>
#include <common/net/message/storage/creating/MoveFileInodeMsg.h>
#include <common/net/message/storage/creating/MoveFileInodeRespMsg.h>
#include <net/message/MirroredMessage.h>
#include <session/EntryLock.h>
#include <storage/MetaStore.h>
class MoveFileInodeMsgResponseState : public MirroredMessageResponseState
{
public:
MoveFileInodeMsgResponseState() : result(FhgfsOpsErr_INTERNAL), linkCount(0) {}
explicit MoveFileInodeMsgResponseState(Deserializer& des)
{
serialize(this, des);
}
MoveFileInodeMsgResponseState(MoveFileInodeMsgResponseState&& other) :
result(other.result), linkCount(other.linkCount) {}
void sendResponse(NetMessage::ResponseContext& ctx) override
{
MoveFileInodeRespMsg resp(result, linkCount);
ctx.sendResponse(resp);
}
void setResult(FhgfsOpsErr res) { this->result = res; }
void setHardlinkCount(unsigned linkCount) { this->linkCount = linkCount; }
bool changesObservableState() const override { return this->result == FhgfsOpsErr_SUCCESS; }
protected:
uint32_t serializerTag() const override { return NETMSGTYPE_MoveFileInodeResp; }
template<typename This, typename Ctx>
static void serialize(This obj, Ctx& ctx)
{
ctx
% obj->result
% obj->linkCount;
}
void serializeContents(Serializer& ser) const override
{
serialize(this, ser);
}
private:
FhgfsOpsErr result;
unsigned linkCount;
};
class MoveFileInodeMsgEx : public MirroredMessage<MoveFileInodeMsg,
std::tuple<FileIDLock, ParentNameLock, FileIDLock>>
{
public:
typedef MoveFileInodeMsgResponseState ResponseState;
virtual bool processIncoming(ResponseContext& ctx) override;
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
bool isSecondary) override;
std::tuple<FileIDLock, ParentNameLock, FileIDLock> lock(EntryLockStore& store) override;
bool isMirrored() override { return getFromFileEntryInfo()->getIsBuddyMirrored(); }
private:
ResponseContext* rctx;
void forwardToSecondary(ResponseContext& ctx) override;
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
{
return (FhgfsOpsErr) static_cast<MoveFileInodeRespMsg&>(resp).getResult();
}
const char* mirrorLogContext() const override { return "MoveFileInodeMsgEx/forward"; }
};

View File

@@ -0,0 +1,67 @@
#include <program/Program.h>
#include <common/net/message/storage/creating/RmDirEntryRespMsg.h>
#include <common/toolkit/MessagingTk.h>
#include "RmDirEntryMsgEx.h"
bool RmDirEntryMsgEx::processIncoming(ResponseContext& ctx)
{
EntryInfo* parentInfo = getParentInfo();
std::string entryName = getEntryName();
FhgfsOpsErr rmRes = rmDirEntry(parentInfo, entryName);
ctx.sendResponse(RmDirEntryRespMsg(rmRes) );
App* app = Program::getApp();
app->getNodeOpStats()->updateNodeOp(ctx.getSocket()->getPeerIP(), MetaOpCounter_RMLINK,
getMsgHeaderUserID() );
return true;
}
FhgfsOpsErr RmDirEntryMsgEx::rmDirEntry(EntryInfo* parentInfo, std::string& entryName)
{
const char* logContext = "RmDirEntryMsg (rm entry)";
MetaStore* metaStore = Program::getApp()->getMetaStore();
FhgfsOpsErr retVal;
// reference parent
DirInode* parentDir = metaStore->referenceDir(parentInfo->getEntryID(),
parentInfo->getIsBuddyMirrored(), true);
if(!parentDir)
return FhgfsOpsErr_PATHNOTEXISTS;
DirEntry removeDentry(entryName);
bool getRes = parentDir->getDentry(entryName, removeDentry);
if(!getRes)
retVal = FhgfsOpsErr_PATHNOTEXISTS;
else
{
if(DirEntryType_ISDIR(removeDentry.getEntryType() ) )
{ // remove dir link
retVal = parentDir->removeDir(entryName, NULL);
}
else
if(DirEntryType_ISFILE(removeDentry.getEntryType() ) )
{
retVal = parentDir->unlinkDirEntry(entryName, &removeDentry,
DirEntry_UNLINK_ID_AND_FILENAME);
}
else
{ // unknown entry type
LogContext(logContext).logErr(std::string("Unknown entry type: ") +
StringTk::intToStr(removeDentry.getEntryType() ) );
retVal = FhgfsOpsErr_INTERNAL;
}
}
// clean-up
metaStore->releaseDir(parentInfo->getEntryID() );
return retVal;
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include <common/storage/StorageErrors.h>
#include <common/net/message/storage/creating/RmDirEntryMsg.h>
#include <storage/MetaStore.h>
// A repair operation, called from fhgfs-ctl
class RmDirEntryMsgEx : public RmDirEntryMsg
{
public:
virtual bool processIncoming(ResponseContext& ctx);
private:
FhgfsOpsErr rmDirEntry(EntryInfo* parentInfo, std::string& entryName);
};

View File

@@ -0,0 +1,229 @@
#include <common/components/streamlistenerv2/IncomingPreprocessedMsgWork.h>
#include <common/net/message/control/GenericResponseMsg.h>
#include <common/net/message/storage/creating/RmDirRespMsg.h>
#include <common/net/message/storage/creating/RmLocalDirMsg.h>
#include <common/net/message/storage/creating/RmLocalDirRespMsg.h>
#include <common/toolkit/MessagingTk.h>
#include <components/FileEventLogger.h>
#include <components/ModificationEventFlusher.h>
#include <program/Program.h>
#include <session/EntryLock.h>
#include "RmDirMsgEx.h"
bool RmDirMsgEx::processIncoming(ResponseContext& ctx)
{
BaseType::processIncoming(ctx);
// update operation counters
updateNodeOp(ctx, MetaOpCounter_RMDIR);
return true;
}
std::tuple<HashDirLock, FileIDLock, FileIDLock, ParentNameLock> RmDirMsgEx::lock(EntryLockStore& store)
{
MetaStore* metaStore = Program::getApp()->getMetaStore();
EntryInfo delEntryInfo;
DirInode* parentDir = metaStore->referenceDir(
getParentInfo()->getEntryID(), getParentInfo()->getIsBuddyMirrored(), true);
if (!parentDir)
return {};
else
{
parentDir->getDirEntryInfo(getDelDirName(), delEntryInfo);
metaStore->releaseDir(getParentInfo()->getEntryID());
}
FileIDLock parentDirLock;
FileIDLock delDirLock;
HashDirLock hashLock;
if (resyncJob && resyncJob->isRunning())
hashLock = {&store, MetaStorageTk::getMetaInodeHash(delEntryInfo.getEntryID())};
// lock directories in deadlock-avoidance order, see MirroredMessage::lock()
if (delEntryInfo.getEntryID().empty())
{
parentDirLock = {&store, getParentInfo()->getEntryID(), true};
}
else if (delEntryInfo.getEntryID() < getParentInfo()->getEntryID())
{
delDirLock = {&store, delEntryInfo.getEntryID(), true};
parentDirLock = {&store, getParentInfo()->getEntryID(), true};
}
else if (delEntryInfo.getEntryID() == getParentInfo()->getEntryID())
{
delDirLock = {&store, delEntryInfo.getEntryID(), true};
}
else
{
parentDirLock = {&store, getParentInfo()->getEntryID(), true};
delDirLock = {&store, delEntryInfo.getEntryID(), true};
}
ParentNameLock parentNameLock(&store, getParentInfo()->getEntryID(), getDelDirName());
return std::make_tuple(
std::move(hashLock),
std::move(parentDirLock),
std::move(delDirLock),
std::move(parentNameLock));
}
std::unique_ptr<RmDirMsgEx::ResponseState> RmDirMsgEx::rmDir(ResponseContext& ctx,
const bool isSecondary)
{
App* app = Program::getApp();
MetaStore* metaStore = Program::getApp()->getMetaStore();
ModificationEventFlusher* modEventFlusher = Program::getApp()->getModificationEventFlusher();
bool modEventLoggingEnabled = modEventFlusher->isLoggingEnabled();
FhgfsOpsErr retVal;
EntryInfo* parentInfo = this->getParentInfo();
const std::string& delDirName = this->getDelDirName();
EntryInfo outDelEntryInfo;
// reference parent
DirInode* parentDir = metaStore->referenceDir(parentInfo->getEntryID(),
parentInfo->getIsBuddyMirrored(), true);
if(!parentDir)
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
DirEntry removeDirEntry(delDirName);
bool getEntryRes = parentDir->getDirDentry(delDirName, removeDirEntry);
if(!getEntryRes)
retVal = FhgfsOpsErr_PATHNOTEXISTS;
else
{
int additionalFlags = 0;
const std::string& parentEntryID = parentInfo->getEntryID();
removeDirEntry.getEntryInfo(parentEntryID, additionalFlags, &outDelEntryInfo);
App* app = Program::getApp();
NumNodeID ownerNodeID = outDelEntryInfo.getOwnerNodeID();
// no-comms path: the dir inode is owned by us
if ((!isMirrored() && ownerNodeID == app->getLocalNode().getNumID()) ||
(isMirrored() &&
ownerNodeID.val() == app->getMetaBuddyGroupMapper()->getLocalGroupID()))
retVal = app->getMetaStore()->removeDirInode(outDelEntryInfo.getEntryID(),
outDelEntryInfo.getIsBuddyMirrored());
else if (!isSecondary)
retVal = rmRemoteDirInode(&outDelEntryInfo);
else
retVal = FhgfsOpsErr_SUCCESS;
if(retVal == FhgfsOpsErr_SUCCESS)
{ // local removal succeeded => remove meta dir
retVal = parentDir->removeDir(delDirName, NULL);
}
}
if (retVal == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
fixInodeTimestamp(*parentDir, dirTimestamps);
// clean-up
metaStore->releaseDir(parentInfo->getEntryID() );
if (!isSecondary && retVal == FhgfsOpsErr_SUCCESS && app->getFileEventLogger() && getFileEvent())
{
EventContext eventCtx = makeEventContext(&outDelEntryInfo, outDelEntryInfo.getParentEntryID(),
getMsgHeaderUserID(), "", 0, isSecondary);
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
}
if (retVal == FhgfsOpsErr_SUCCESS && modEventLoggingEnabled)
{
const std::string& entryID = outDelEntryInfo.getEntryID();
modEventFlusher->add(ModificationEvent_DIRREMOVED, entryID);
}
return boost::make_unique<ResponseState>(retVal);
}
/**
* Remove the inode of this directory
*/
FhgfsOpsErr RmDirMsgEx::rmRemoteDirInode(EntryInfo* delEntryInfo)
{
const char* logContext = "RmDirMsg (rm dir inode)";
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
App* app = Program::getApp();
NumNodeID ownerNodeID = delEntryInfo->getOwnerNodeID();
LOG_DEBUG(logContext, Log_DEBUG,
"Removing dir inode from metadata node: " + ownerNodeID.str() + "; "
"dirname: " + delEntryInfo->getFileName() + "; isBuddyMirrored: " +
StringTk::intToStr(delEntryInfo->getIsBuddyMirrored()) );
// prepare request
RmLocalDirMsg rmMsg(delEntryInfo);
RequestResponseArgs rrArgs(NULL, &rmMsg, NETMSGTYPE_RmLocalDirResp);
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes() );
rrNode.setTargetStates(app->getMetaStateStore() );
if (delEntryInfo->getIsBuddyMirrored())
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
do // (this loop just exists to enable the "break"-jump, so it's not really a loop)
{
// send request to other mds and receive response
FhgfsOpsErr requestRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if(requestRes != FhgfsOpsErr_SUCCESS)
{ // communication error
LogContext(logContext).log(Log_WARNING,
"Communication with metadata server failed. "
"ownerNodeID: " + ownerNodeID.str() + "; "
"dirID: " + delEntryInfo->getEntryID() + "; " +
"dirname: " + delEntryInfo->getFileName() );
retVal = requestRes;
break;
}
// correct response type received
const auto rmRespMsg = (const RmLocalDirRespMsg*)rrArgs.outRespMsg.get();
retVal = rmRespMsg->getResult();
if(unlikely( (retVal != FhgfsOpsErr_SUCCESS) && (retVal != FhgfsOpsErr_NOTEMPTY) ) )
{ // error: dir inode not removed
std::string errString = boost::lexical_cast<std::string>(retVal);
LogContext(logContext).log(Log_DEBUG,
"Metadata server was unable to remove dir inode. "
"ownerNodeID: " + ownerNodeID.str() + "; "
"dirID: " + delEntryInfo->getEntryID() + "; " +
"dirname: " + delEntryInfo->getFileName() + "; " +
"error: " + errString);
break;
}
// success: dir inode removed
LOG_DEBUG(logContext, Log_DEBUG,
"Metadata server removed dir inode: "
"ownerNodeID: " + ownerNodeID.str() + "; "
"dirID: " + delEntryInfo->getEntryID() + "; " +
"dirname: " + delEntryInfo->getFileName() );
} while(false);
return retVal;
}
void RmDirMsgEx::forwardToSecondary(ResponseContext& ctx)
{
sendToSecondary(ctx, *this, NETMSGTYPE_RmDirResp);
}

View File

@@ -0,0 +1,42 @@
#pragma once
#include <common/storage/StorageErrors.h>
#include <common/net/message/storage/creating/RmDirMsg.h>
#include <common/net/message/storage/creating/RmDirRespMsg.h>
#include <storage/MetaStore.h>
#include <net/message/MirroredMessage.h>
class RmDirMsgEx : public MirroredMessage<RmDirMsg,
std::tuple<HashDirLock, FileIDLock, FileIDLock, ParentNameLock>>
{
public:
typedef ErrorCodeResponseState<RmDirRespMsg, NETMSGTYPE_RmDir> ResponseState;
virtual bool processIncoming(ResponseContext& ctx) override;
std::tuple<HashDirLock, FileIDLock, FileIDLock, ParentNameLock>
lock(EntryLockStore& store) override;
static FhgfsOpsErr rmRemoteDirInode(EntryInfo* delEntryInfo);
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
bool isSecondary) override
{
return rmDir(ctx, isSecondary);
}
bool isMirrored() override { return getParentInfo()->getIsBuddyMirrored(); }
private:
std::unique_ptr<ResponseState> rmDir(ResponseContext& ctx, const bool isSecondary);
void forwardToSecondary(ResponseContext& ctx) override;
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
{
return (FhgfsOpsErr) static_cast<RmDirRespMsg&>(resp).getValue();
}
const char* mirrorLogContext() const override { return "RmDirMsgEx/forward"; }
};

View File

@@ -0,0 +1,56 @@
#include <program/Program.h>
#include <common/net/message/storage/creating/RmLocalDirRespMsg.h>
#include <common/toolkit/MetaStorageTk.h>
#include "RmLocalDirMsgEx.h"
std::tuple<HashDirLock, FileIDLock> RmLocalDirMsgEx::lock(EntryLockStore& store)
{
HashDirLock hashLock;
// if we are currently under modsync, we must lock the hash dir from which we are removing the
// inode. otherwise bulk sync may interfere with mod sync and cause the resync to fail.
// do not lock the hash dir if we are removing the inode from the same meta node as the dentry,
// RmDir will have already locked the hash dir.
if (!rctx->isLocallyGenerated() && resyncJob && resyncJob->isRunning())
hashLock = {&store, MetaStorageTk::getMetaInodeHash(getDelEntryInfo()->getEntryID())};
return std::make_tuple(
std::move(hashLock),
FileIDLock(&store, getDelEntryInfo()->getEntryID(), true));
}
bool RmLocalDirMsgEx::processIncoming(ResponseContext& ctx)
{
rctx = &ctx;
return BaseType::processIncoming(ctx);
}
std::unique_ptr<MirroredMessageResponseState> RmLocalDirMsgEx::executeLocally(ResponseContext& ctx,
bool isSecondary)
{
return rmDir();
}
std::unique_ptr<RmLocalDirMsgEx::ResponseState> RmLocalDirMsgEx::rmDir()
{
MetaStore* metaStore = Program::getApp()->getMetaStore();
EntryInfo* delEntryInfo = this->getDelEntryInfo();
LOG_DEBUG("RmLocalDirMsgEx (rmDir)", Log_DEBUG,
"Removing local dir inode: " + delEntryInfo->getFileName() + "; isBuddyMirrored: " +
StringTk::intToStr(delEntryInfo->getIsBuddyMirrored()) );
FhgfsOpsErr res = metaStore->removeDirInode(delEntryInfo->getEntryID(),
delEntryInfo->getIsBuddyMirrored());
return boost::make_unique<ResponseState>(res);
}
void RmLocalDirMsgEx::forwardToSecondary(ResponseContext& ctx)
{
sendToSecondary(ctx, *this, NETMSGTYPE_RmLocalDirResp);
}

View File

@@ -0,0 +1,38 @@
#pragma once
#include <common/storage/StorageErrors.h>
#include <common/net/message/storage/creating/RmLocalDirMsg.h>
#include <common/net/message/storage/creating/RmLocalDirRespMsg.h>
#include <session/EntryLock.h>
#include <storage/MetaStore.h>
#include <net/message/MirroredMessage.h>
class RmLocalDirMsgEx : public MirroredMessage<RmLocalDirMsg, std::tuple<HashDirLock, FileIDLock>>
{
public:
typedef ErrorCodeResponseState<RmLocalDirRespMsg, NETMSGTYPE_RmLocalDir> ResponseState;
virtual bool processIncoming(ResponseContext& ctx) override;
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
bool isSecondary) override;
std::tuple<HashDirLock, FileIDLock> lock(EntryLockStore& store) override;
bool isMirrored() override { return getDelEntryInfo()->getIsBuddyMirrored(); }
private:
ResponseContext* rctx;
std::unique_ptr<ResponseState> rmDir();
void forwardToSecondary(ResponseContext& ctx) override;
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
{
return (FhgfsOpsErr) static_cast<RmLocalDirRespMsg&>(resp).getValue();
}
const char* mirrorLogContext() const override { return "RmLocalDirMsgEx/forward"; }
};

View File

@@ -0,0 +1,294 @@
#include <common/net/message/storage/creating/UnlinkLocalFileInodeMsg.h>
#include <common/net/message/storage/creating/UnlinkLocalFileInodeRespMsg.h>
#include <components/FileEventLogger.h>
#include <net/msghelpers/MsgHelperUnlink.h>
#include <program/Program.h>
#include "UnlinkFileMsgEx.h"
std::tuple<HashDirLock, FileIDLock, ParentNameLock, FileIDLock> UnlinkFileMsgEx::lock(EntryLockStore& store)
{
HashDirLock hashLock;
FileIDLock inodeLock;
// we also have to lock the inode attached to the dentry - if we delete the inode, we must
// exclude concurrent actions on the same inode. if we cannot look up a file inode for the
// dentry, nothing bad happens.
MetaStore* metaStore = Program::getApp()->getMetaStore();
auto dir = metaStore->referenceDir(getParentInfo()->getEntryID(),
getParentInfo()->getIsBuddyMirrored(), false);
DirEntry dentry(getDelFileName());
bool dentryExists = dir->getFileDentry(getDelFileName(), dentry);
if (dentryExists)
{
dentry.getEntryInfo(getParentInfo()->getEntryID(), 0, &fileInfo);
// lock hash dir where we are going to remove (or update) file inode
// need to take it only if it is a non-inlined inode and resynch is running
if (resyncJob && resyncJob->isRunning() && !dentry.getIsInodeInlined())
hashLock = {&store, MetaStorageTk::getMetaInodeHash(dentry.getID())};
}
FileIDLock dirLock(&store, getParentInfo()->getEntryID(), true);
ParentNameLock dentryLock(&store, getParentInfo()->getEntryID(), getDelFileName());
if (dentryExists)
inodeLock = {&store, dentry.getID(), true};
metaStore->releaseDir(dir->getID());
return std::make_tuple(std::move(hashLock), std::move(dirLock), std::move(dentryLock), std::move(inodeLock));
}
bool UnlinkFileMsgEx::processIncoming(ResponseContext& ctx)
{
#ifdef BEEGFS_DEBUG
const char* logContext = "UnlinkFileMsg incoming";
const std::string& removeName = getDelFileName();
EntryInfo* parentInfo = getParentInfo();
LOG_DEBUG(logContext, Log_DEBUG, "ParentID: " + parentInfo->getEntryID() + "; "
"deleteName: " + removeName);
#endif // BEEGFS_DEBUG
// update operation counters (here on top because we have an early sock release in this msg)
updateNodeOp(ctx, MetaOpCounter_UNLINK);
return BaseType::processIncoming(ctx);
}
std::unique_ptr<MirroredMessageResponseState> UnlinkFileMsgEx::executeLocally(
ResponseContext& ctx, bool isSecondary)
{
const char* logContext = "Unlink File Msg";
App* app = Program::getApp();
MetaStore* metaStore = app->getMetaStore();
// reference parent dir
DirInode* dir = metaStore->referenceDir(getParentInfo()->getEntryID(),
getParentInfo()->getIsBuddyMirrored(), true);
if (!dir)
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
DirEntry dentryToRemove(getDelFileName());
if (!dir->getFileDentry(getDelFileName(), dentryToRemove))
{
metaStore->releaseDir(dir->getID());
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
}
// On meta-mirrored setup, ensure the dentry we just loaded i.e. 'dentryToRemove' has the same entryID as 'fileInfo'.
// This check is crucial to detect a very rare race condition where dentry gets unlinked
// because the parent directory was not locked during UnlinkFileMsgEx::lock(),
// and then reappears because the same file is being created again. If the entryIDs
// don't match, it means the dentry has changed, and the exclusive lock taken on the
// entryID before won't be effective in preventing future races of ongoing unlink
// operation with other filesystem operations (e.g. close()) which might happen on this file.
// Therefore, we release the directory from the meta store and return an error saying path
// not exists.
if (isMirrored() && (dentryToRemove.getEntryID() != this->fileInfo.getEntryID()))
{
metaStore->releaseDir(dir->getID());
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
}
// re-fetch entryInfo
dentryToRemove.getEntryInfo(getParentInfo()->getEntryID(), 0, &fileInfo);
// check whether local node/group owns file's inode (dentry's owner may/maynot be same)
NumNodeID ownerNodeID = fileInfo.getOwnerNodeID();
if ( (!isMirrored() && ownerNodeID == app->getLocalNode().getNumID()) ||
(isMirrored() &&
ownerNodeID.val() == app->getMetaBuddyGroupMapper()->getLocalGroupID()) )
{
// File inode is on the same metadata node/buddy group as the dentry.
if (isSecondary)
return executeSecondary(ctx, *dir);
else
return executePrimary(ctx, *dir);
}
else
{ // Handle case where file inode is on a different metadata node/buddy group than dentry.
// Step 1: Remove file's dentry (local operation)
// Note: Dentry was already loaded and validated earlier
FhgfsOpsErr unlinkRes = FhgfsOpsErr_INTERNAL;
unlinkRes = dir->unlinkDirEntry(getDelFileName(), &dentryToRemove, DirEntry_UNLINK_FILENAME);
metaStore->releaseDir(dir->getID());
if (unlinkRes != FhgfsOpsErr_SUCCESS)
return boost::make_unique<ResponseState>(unlinkRes);
// Step 2: Remove file's inode (remote operation)
if (!isSecondary)
{
unsigned preUnlinkHardlinkCount = 0;
UnlinkLocalFileInodeMsg unlinkInodeMsg(&fileInfo);
RequestResponseArgs rrArgs(NULL, &unlinkInodeMsg, NETMSGTYPE_UnlinkLocalFileInodeResp);
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes());
rrNode.setTargetStates(app->getMetaStateStore());
if (fileInfo.getIsBuddyMirrored())
{
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
}
do
{
FhgfsOpsErr resp = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
if (unlikely(resp != FhgfsOpsErr_SUCCESS))
{
LogContext(logContext).log(Log_WARNING,
"Communication with metadata server failed. "
"nodeID: " + ownerNodeID.str() + "; " +
"entryID: " + fileInfo.getEntryID().c_str());
break;
}
// response received
const auto respMsg = (UnlinkLocalFileInodeRespMsg*) rrArgs.outRespMsg.get();
FhgfsOpsErr res = respMsg->getResult();
if (res != FhgfsOpsErr_SUCCESS)
{
// error: either inode file doesn't exists or some other error happened
LogContext(logContext).log(Log_WARNING, "unlink file inode failed! "
"nodeID: " + ownerNodeID.str() + "; " +
"entryID: " + fileInfo.getEntryID().c_str());
break;
}
// since dentry has been removed successfully so all good for user - no need
// to return an error if unlinking remote inode fails due to some reasons.
// we still need to remove dentry from secondary buddy so should not overwrite
// local dentry removal success with some remote error
unlinkRes = FhgfsOpsErr_SUCCESS;
preUnlinkHardlinkCount = respMsg->getPreUnlinkHardlinkCount();
} while (false);
if (unlinkRes == FhgfsOpsErr_SUCCESS && app->getFileEventLogger() && getFileEvent())
{
// Determine remaining hardlinks after unlink operation
unsigned remainingLinks = (preUnlinkHardlinkCount > 0) ? preUnlinkHardlinkCount - 1 : 0;
EventContext eventCtx = makeEventContext(&fileInfo, getParentInfo()->getEntryID(),
getMsgHeaderUserID(), "", remainingLinks, isSecondary);
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
}
}
return boost::make_unique<ResponseState>(unlinkRes);
}
// Should never reach this point but added to please the compiler
return boost::make_unique<ResponseState>(FhgfsOpsErr_SUCCESS);
}
std::unique_ptr<UnlinkFileMsgEx::ResponseState> UnlinkFileMsgEx::executePrimary(
ResponseContext& ctx, DirInode& dir)
{
App* app = Program::getApp();
unsigned preUnlinkHardlinkCount = 0; // number of hardlink(s) before unlink happens
// Two alternatives:
// 1) early response before chunk files unlink.
// 2) normal response after chunk files unlink (incl. chunk files error).
if (app->getConfig()->getTuneEarlyUnlinkResponse() && !isMirrored())
{
// alternative 1: response before chunk files unlink
std::unique_ptr<FileInode> unlinkedInode;
FhgfsOpsErr unlinkMetaRes = MsgHelperUnlink::unlinkMetaFile(dir,
getDelFileName(), &unlinkedInode, preUnlinkHardlinkCount);
app->getMetaStore()->releaseDir(dir.getID());
earlyComplete(ctx, ResponseState(unlinkMetaRes));
/* note: if the file is still opened or if there were hardlinks then unlinkedInode will be
NULL even on FhgfsOpsErr_SUCCESS */
if ((unlinkMetaRes == FhgfsOpsErr_SUCCESS) && unlinkedInode)
MsgHelperUnlink::unlinkChunkFiles(unlinkedInode.release(), getMsgHeaderUserID() );
if (unlinkMetaRes == FhgfsOpsErr_SUCCESS && app->getFileEventLogger() && getFileEvent())
{
// Determine remaining hardlinks after unlink operation
unsigned remainingLinks = (preUnlinkHardlinkCount > 0) ? preUnlinkHardlinkCount - 1 : 0;
EventContext eventCtx = makeEventContext(&fileInfo, getParentInfo()->getEntryID(),
// This is not the secondary node if executePrimary() was called.
getMsgHeaderUserID(), "", remainingLinks, false);
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
}
return {};
}
// alternative 2: response after chunk files unlink
std::unique_ptr<FileInode> unlinkedInode;
FhgfsOpsErr unlinkRes = MsgHelperUnlink::unlinkMetaFile(dir,
getDelFileName(), &unlinkedInode, preUnlinkHardlinkCount);
if ((unlinkRes == FhgfsOpsErr_SUCCESS) && shouldFixTimestamps())
{
fixInodeTimestamp(dir, dirTimestamps);
if (!unlinkedInode)
{
auto [file, referenceRes] = app->getMetaStore()->referenceFile(&fileInfo);
if (file)
{
fixInodeTimestamp(*file, fileTimestamps, &fileInfo);
app->getMetaStore()->releaseFile(dir.getID(), file);
}
}
}
/* note: if the file is still opened or if there are/were hardlinks then unlinkedInode will be
NULL even on FhgfsOpsErr_SUCCESS */
if ((unlinkRes == FhgfsOpsErr_SUCCESS) && unlinkedInode)
MsgHelperUnlink::unlinkChunkFiles(unlinkedInode.release(), getMsgHeaderUserID());
app->getMetaStore()->releaseDir(dir.getID());
if ((unlinkRes == FhgfsOpsErr_SUCCESS) && app->getFileEventLogger() && getFileEvent())
{
// Determine remaining hardlinks after unlink operation
unsigned remainingLinks = (preUnlinkHardlinkCount > 0) ? preUnlinkHardlinkCount - 1 : 0;
EventContext eventCtx = makeEventContext(&fileInfo, getParentInfo()->getEntryID(),
// This is not the secondary node if executePrimary() was called.
getMsgHeaderUserID(), "", remainingLinks, false);
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
}
return boost::make_unique<ResponseState>(unlinkRes);
}
std::unique_ptr<UnlinkFileMsgEx::ResponseState> UnlinkFileMsgEx::executeSecondary(
ResponseContext& ctx, DirInode& dir)
{
MetaStore* const metaStore = Program::getApp()->getMetaStore();
std::unique_ptr<FileInode> unlinkedInode;
unsigned preUnlinkHardlinkCount; // Not used here!
FhgfsOpsErr unlinkMetaRes = MsgHelperUnlink::unlinkMetaFile(dir,
getDelFileName(), &unlinkedInode, preUnlinkHardlinkCount);
if ((unlinkMetaRes == FhgfsOpsErr_SUCCESS) && shouldFixTimestamps())
{
fixInodeTimestamp(dir, dirTimestamps);
if (!unlinkedInode)
{
auto [file, referenceRes] = metaStore->referenceFile(&fileInfo);
if (file)
{
fixInodeTimestamp(*file, fileTimestamps, &fileInfo);
metaStore->releaseFile(dir.getID(), file);
}
}
}
metaStore->releaseDir(dir.getID());
return boost::make_unique<ResponseState>(unlinkMetaRes);
}
void UnlinkFileMsgEx::forwardToSecondary(ResponseContext& ctx)
{
sendToSecondary(ctx, *this, NETMSGTYPE_UnlinkFileResp);
}

View File

@@ -0,0 +1,45 @@
#pragma once
#include <common/net/message/storage/creating/UnlinkFileMsg.h>
#include <common/net/message/storage/creating/UnlinkFileRespMsg.h>
#include <session/EntryLock.h>
#include <net/message/MirroredMessage.h>
class UnlinkFileMsgEx : public MirroredMessage<UnlinkFileMsg,
std::tuple<HashDirLock, FileIDLock, ParentNameLock, FileIDLock>>
{
public:
typedef ErrorCodeResponseState<UnlinkFileRespMsg, NETMSGTYPE_UnlinkFile> ResponseState;
virtual bool processIncoming(ResponseContext& ctx) override;
std::tuple<HashDirLock, FileIDLock, ParentNameLock, FileIDLock> lock(EntryLockStore& store) override;
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
bool isSecondary) override;
bool isMirrored() override { return getParentInfo()->getIsBuddyMirrored(); }
private:
/**
* Execute unlink operation on primary and secondary metadata nodes.
* Primary handles metadata removal, chunk cleanup, and event logging.
* Secondary handles only metadata operations.
*
* @param ctx Response context
* @param dir Parent directory reference. Functions guarantee directory reference
* release before return, including error paths.
* @return Response state containing operation result.
*/
std::unique_ptr<ResponseState> executePrimary(ResponseContext& ctx, DirInode& dir);
std::unique_ptr<ResponseState> executeSecondary(ResponseContext& ctx, DirInode& dir);
void forwardToSecondary(ResponseContext& ctx) override;
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
{
return (FhgfsOpsErr) static_cast<UnlinkFileRespMsg&>(resp).getValue();
}
const char* mirrorLogContext() const override { return "UnlinkFileMsgEx/forward"; }
};

View File

@@ -0,0 +1,61 @@
#include <program/Program.h>
#include <common/toolkit/MetaStorageTk.h>
#include <net/msghelpers/MsgHelperUnlink.h>
#include "UnlinkLocalFileInodeMsgEx.h"
std::tuple<HashDirLock, FileIDLock> UnlinkLocalFileInodeMsgEx::lock(EntryLockStore& store)
{
// we must not lock hash dir and inode if it is owned by current node. if it is a locally
// generated message which is sent by unlinkmsg and renamev2msg sends then aforementioned
// locks are already held by them
if (rctx->isLocallyGenerated())
return {};
HashDirLock hashLock;
if (resyncJob && resyncJob->isRunning())
HashDirLock hashLock = {&store, MetaStorageTk::getMetaInodeHash(getDelEntryInfo()->getEntryID())};
return std::make_tuple(
std::move(hashLock),
FileIDLock(&store, getDelEntryInfo()->getEntryID(), true));
}
bool UnlinkLocalFileInodeMsgEx::processIncoming(ResponseContext& ctx)
{
rctx = &ctx;
return BaseType::processIncoming(ctx);
}
std::unique_ptr<MirroredMessageResponseState> UnlinkLocalFileInodeMsgEx::executeLocally(ResponseContext& ctx,
bool isSecondary)
{
// Create a copy of the passed-in entryInfo and use it!
// Reason:
// Some fields of EntryInfo, such as parentEntryID and isInlined, can be modified
// by MetaStore::unlinkInodeLater() during unlink processing. Using a copy ensures
// that the original (and unmodified) entryInfo is forwarded to the secondary buddy
// to avoid different states on meta-buddies.
EntryInfo entryInfo;
entryInfo.set(getDelEntryInfo());
UnlinkLocalFileInodeResponseState resp;
unsigned outLinkCount = 0;
std::unique_ptr<FileInode> unlinkedInode;
FhgfsOpsErr unlinkInodeRes = MsgHelperUnlink::unlinkFileInode(&entryInfo, &unlinkedInode, outLinkCount);
resp.setResult(unlinkInodeRes);
resp.setPreUnlinkHardlinkCount(outLinkCount);
if ((unlinkInodeRes == FhgfsOpsErr_SUCCESS) && unlinkedInode && !isSecondary)
{
MsgHelperUnlink::unlinkChunkFiles(unlinkedInode.release(), getMsgHeaderUserID());
}
return boost::make_unique<ResponseState>(std::move(resp));
}
void UnlinkLocalFileInodeMsgEx::forwardToSecondary(ResponseContext& ctx)
{
sendToSecondary(ctx, *this, NETMSGTYPE_UnlinkLocalFileInodeResp);
}

View File

@@ -0,0 +1,86 @@
#pragma once
#include <common/net/message/storage/creating/UnlinkLocalFileInodeMsg.h>
#include <common/net/message/storage/creating/UnlinkLocalFileInodeRespMsg.h>
#include <session/EntryLock.h>
#include <storage/MetaStore.h>
#include <net/message/MirroredMessage.h>
class UnlinkLocalFileInodeResponseState : public MirroredMessageResponseState
{
public:
UnlinkLocalFileInodeResponseState() : result(FhgfsOpsErr_INTERNAL), preUnlinkHardlinkCount(0) {}
UnlinkLocalFileInodeResponseState(FhgfsOpsErr result, unsigned linkCount) :
result(result), preUnlinkHardlinkCount(linkCount) {}
explicit UnlinkLocalFileInodeResponseState(Deserializer& des)
{
serialize(this, des);
}
UnlinkLocalFileInodeResponseState(UnlinkLocalFileInodeResponseState&& other) :
result(other.result), preUnlinkHardlinkCount(other.preUnlinkHardlinkCount) {}
void sendResponse(NetMessage::ResponseContext& ctx) override
{
UnlinkLocalFileInodeRespMsg resp(result, preUnlinkHardlinkCount);
ctx.sendResponse(resp);
}
void setResult(FhgfsOpsErr res) { this->result = res; }
void setPreUnlinkHardlinkCount(unsigned linkCount) { this->preUnlinkHardlinkCount = linkCount; }
bool changesObservableState() const override { return true; }
protected:
uint32_t serializerTag() const override { return NETMSGTYPE_UnlinkLocalFileInodeResp; }
template<typename This, typename Ctx>
static void serialize(This obj, Ctx& ctx)
{
ctx
% obj->result
% obj->preUnlinkHardlinkCount;
}
void serializeContents(Serializer& ser) const override
{
serialize(this, ser);
}
private:
FhgfsOpsErr result;
// The preUnlinkHardlinkCount represents the number of hardlinks that existed before
// the actual unlink happens. This is mainly needed for event logging purposes.
unsigned preUnlinkHardlinkCount;
};
class UnlinkLocalFileInodeMsgEx : public MirroredMessage<UnlinkLocalFileInodeMsg,
std::tuple<HashDirLock, FileIDLock>>
{
public:
typedef UnlinkLocalFileInodeResponseState ResponseState;
virtual bool processIncoming(ResponseContext& ctx) override;
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
bool isSecondary) override;
std::tuple<HashDirLock, FileIDLock> lock(EntryLockStore& store) override;
bool isMirrored() override { return getDelEntryInfo()->getIsBuddyMirrored(); }
private:
ResponseContext* rctx;
void forwardToSecondary(ResponseContext& ctx) override;
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
{
return (FhgfsOpsErr) static_cast<UnlinkLocalFileInodeRespMsg&>(resp).getResult();
}
const char* mirrorLogContext() const override
{
return "UnlinkLocalFileInodeMsgEx/forward";
}
};