New upstream version 8.1.0
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/moving/MovingDirInsertMsg.h>
|
||||
#include <common/net/message/storage/moving/MovingDirInsertRespMsg.h>
|
||||
#include <common/storage/striping/Raid0Pattern.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include "MovingDirInsertMsgEx.h"
|
||||
|
||||
|
||||
bool MovingDirInsertMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
rctx = &ctx;
|
||||
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> MovingDirInsertMsgEx::executeLocally(
|
||||
ResponseContext& ctx, bool isSecondary)
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
FhgfsOpsErr retVal;
|
||||
|
||||
EntryInfo* toDirInfo = this->getToDirInfo();
|
||||
|
||||
// reference parent
|
||||
DirInode* parentDir = metaStore->referenceDir(toDirInfo->getEntryID(),
|
||||
toDirInfo->getIsBuddyMirrored(), true);
|
||||
if(!parentDir)
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
|
||||
|
||||
/* create dir-entry and add information about its inode from the given buffer */
|
||||
|
||||
const std::string& newName = this->getNewName();
|
||||
const char* buf = this->getSerialBuf();
|
||||
Deserializer des(buf, getSerialBufLen());
|
||||
DirEntry newDirEntry(newName);
|
||||
|
||||
newDirEntry.deserializeDentry(des);
|
||||
if (!des.good())
|
||||
{
|
||||
LogContext("File rename").logErr("Bug: Deserialization of remote buffer failed. Are all "
|
||||
"meta servers running the same version?" );
|
||||
|
||||
retVal = FhgfsOpsErr_INTERNAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (newDirEntry.getEntryID() == parentDir->getID() )
|
||||
{ // attempt to rename a dir into itself
|
||||
retVal = FhgfsOpsErr_INVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
retVal = parentDir->makeDirEntry(newDirEntry);
|
||||
|
||||
if (retVal == FhgfsOpsErr_SUCCESS && shouldFixTimestamps())
|
||||
fixInodeTimestamp(*parentDir, dirTimestamps);
|
||||
|
||||
if (retVal != FhgfsOpsErr_SUCCESS && retVal != FhgfsOpsErr_EXISTS)
|
||||
retVal = FhgfsOpsErr_INTERNAL;
|
||||
|
||||
out:
|
||||
metaStore->releaseDir(toDirInfo->getEntryID());
|
||||
return boost::make_unique<ResponseState>(retVal);
|
||||
}
|
||||
|
||||
void MovingDirInsertMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_MovingDirInsertResp);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/moving/MovingDirInsertMsg.h>
|
||||
#include <common/net/message/storage/moving/MovingDirInsertRespMsg.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
|
||||
// Move directory to another meta-data server
|
||||
|
||||
class MovingDirInsertMsgEx : public MirroredMessage<MovingDirInsertMsg,
|
||||
std::tuple<FileIDLock, ParentNameLock>>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<MovingDirInsertRespMsg, NETMSGTYPE_MovingDirInsert>
|
||||
ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<FileIDLock, ParentNameLock> lock(EntryLockStore& store) override
|
||||
{
|
||||
// we must not lock the directory if it is owned by the current node. if it is, the
|
||||
// current message was also sent by the local node, specifically by a RmDirMsgEx, which
|
||||
// also locks the directory for write
|
||||
if (rctx->isLocallyGenerated())
|
||||
return {};
|
||||
|
||||
FileIDLock dirLock(&store, getToDirInfo()->getEntryID(), true);
|
||||
ParentNameLock nameLock(&store, getToDirInfo()->getEntryID(), getNewName());
|
||||
|
||||
return std::make_tuple(std::move(dirLock), std::move(nameLock));
|
||||
}
|
||||
|
||||
bool isMirrored() override { return getToDirInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
ResponseContext* rctx;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return static_cast<MovingDirInsertRespMsg&>(resp).getResult();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "MovingDirInsertMsgEx/forward"; }
|
||||
};
|
||||
|
||||
173
meta/source/net/message/storage/moving/MovingFileInsertMsgEx.cpp
Normal file
173
meta/source/net/message/storage/moving/MovingFileInsertMsgEx.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
#include <program/Program.h>
|
||||
#include <common/net/message/storage/moving/MovingFileInsertMsg.h>
|
||||
#include <common/net/message/storage/moving/MovingFileInsertRespMsg.h>
|
||||
#include <common/storage/striping/Raid0Pattern.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <net/msghelpers/MsgHelperUnlink.h>
|
||||
#include <net/msghelpers/MsgHelperXAttr.h>
|
||||
|
||||
#include "MovingFileInsertMsgEx.h"
|
||||
|
||||
std::tuple<FileIDLock, FileIDLock, FileIDLock, ParentNameLock> MovingFileInsertMsgEx::lock(
|
||||
EntryLockStore& store)
|
||||
{
|
||||
// we must not lock the directory if it is owned by the current node. if it is, the
|
||||
// current message was also sent by the local node, specifically by a rename msg, which
|
||||
// also locks the directory for write
|
||||
if (rctx->isLocallyGenerated())
|
||||
return {};
|
||||
|
||||
FileIDLock dirLock(&store, getToDirInfo()->getEntryID(), true);
|
||||
|
||||
ParentNameLock nameLock(&store, getToDirInfo()->getEntryID(), getNewName());
|
||||
|
||||
FileIDLock newLock;
|
||||
FileIDLock unlinkedLock;
|
||||
|
||||
auto dir = Program::getApp()->getMetaStore()->referenceDir(getToDirInfo()->getEntryID(),
|
||||
getToDirInfo()->getIsBuddyMirrored(), true);
|
||||
if (dir)
|
||||
{
|
||||
FileInode newInode;
|
||||
Deserializer des(getSerialBuf(), getSerialBufLen());
|
||||
newInode.deserializeMetaData(des);
|
||||
if (des.good())
|
||||
{
|
||||
std::string unlinkedID = newInode.getEntryID();
|
||||
|
||||
EntryInfo unlinkedInfo;
|
||||
dir->getFileEntryInfo(getNewName(), unlinkedInfo);
|
||||
if (DirEntryType_ISFILE(unlinkedInfo.getEntryType()))
|
||||
unlinkedID = unlinkedInfo.getEntryID();
|
||||
|
||||
if (newInode.getEntryID() < unlinkedID)
|
||||
{
|
||||
newLock = {&store, newInode.getEntryID(), true};
|
||||
unlinkedLock = {&store, unlinkedID, true};
|
||||
}
|
||||
else if (newInode.getEntryID() == unlinkedID)
|
||||
{
|
||||
newLock = {&store, newInode.getEntryID(), true};
|
||||
}
|
||||
else
|
||||
{
|
||||
unlinkedLock = {&store, unlinkedID, true};
|
||||
newLock = {&store, newInode.getEntryID(), true};
|
||||
}
|
||||
}
|
||||
|
||||
Program::getApp()->getMetaStore()->releaseDir(dir->getID());
|
||||
}
|
||||
|
||||
return std::make_tuple(
|
||||
std::move(newLock),
|
||||
std::move(unlinkedLock),
|
||||
std::move(dirLock),
|
||||
std::move(nameLock));
|
||||
}
|
||||
|
||||
bool MovingFileInsertMsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
rctx = &ctx;
|
||||
|
||||
return BaseType::processIncoming(ctx);
|
||||
}
|
||||
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> MovingFileInsertMsgEx::executeLocally(
|
||||
ResponseContext& ctx, bool isSecondary)
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
EntryInfo* fromFileInfo = this->getFromFileInfo();
|
||||
EntryInfo* toDirInfo = this->getToDirInfo();
|
||||
std::string newName = this->getNewName();
|
||||
|
||||
EntryInfo overWrittenEntryInfo;
|
||||
std::unique_ptr<FileInode> unlinkInode;
|
||||
unsigned inodeBufLen;
|
||||
std::unique_ptr<char[]> inodeBuf;
|
||||
|
||||
DirInode* toDir = metaStore->referenceDir(toDirInfo->getEntryID(),
|
||||
toDirInfo->getIsBuddyMirrored(), true);
|
||||
if (!toDir)
|
||||
return boost::make_unique<MovingFileInsertResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
|
||||
|
||||
FhgfsOpsErr moveRes = metaStore->moveRemoteFileInsert(
|
||||
fromFileInfo, *toDir, newName, getSerialBuf(), getSerialBufLen(), &unlinkInode,
|
||||
&overWrittenEntryInfo, newFileInfo);
|
||||
if (moveRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
metaStore->releaseDir(toDir->getID());
|
||||
return boost::make_unique<MovingFileInsertResponseState>(moveRes);
|
||||
}
|
||||
|
||||
std::string xattrName;
|
||||
CharVector xattrValue;
|
||||
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
|
||||
|
||||
while (isMsgHeaderFeatureFlagSet(MOVINGFILEINSERTMSG_FLAG_HAS_XATTRS))
|
||||
{
|
||||
retVal = MsgHelperXAttr::StreamXAttrState::readNextXAttr(ctx.getSocket(), xattrName,
|
||||
xattrValue);
|
||||
if (retVal == FhgfsOpsErr_SUCCESS)
|
||||
break;
|
||||
else if (retVal != FhgfsOpsErr_AGAIN)
|
||||
goto xattr_error;
|
||||
|
||||
retVal = MsgHelperXAttr::setxattr(&newFileInfo, xattrName, xattrValue, 0);
|
||||
if (retVal != FhgfsOpsErr_SUCCESS)
|
||||
goto xattr_error;
|
||||
|
||||
xattrNames.push_back(xattrName);
|
||||
}
|
||||
|
||||
if(unlinkInode)
|
||||
{
|
||||
inodeBuf.reset(new (std::nothrow) char[META_SERBUF_SIZE]);
|
||||
if (unlikely(!inodeBuf) )
|
||||
{ // out of memory, we are going to leak an inode and chunks
|
||||
inodeBufLen = 0;
|
||||
LOG(GENERAL, ERR, "Malloc failed, leaking chunks", ("inodeID", unlinkInode->getEntryID()));
|
||||
}
|
||||
else
|
||||
{
|
||||
Serializer ser(inodeBuf.get(), META_SERBUF_SIZE);
|
||||
unlinkInode->serializeMetaData(ser);
|
||||
inodeBufLen = ser.size();
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // no file overwritten
|
||||
inodeBufLen = 0;
|
||||
}
|
||||
|
||||
if (shouldFixTimestamps())
|
||||
{
|
||||
fixInodeTimestamp(*toDir, dirTimestamps);
|
||||
auto [newFile, referenceRes] = metaStore->referenceFile(&newFileInfo);
|
||||
if (newFile)
|
||||
{
|
||||
fixInodeTimestamp(*newFile, fileTimestamps, &newFileInfo);
|
||||
metaStore->releaseFile(toDir->getID(), newFile);
|
||||
}
|
||||
}
|
||||
|
||||
metaStore->releaseDir(toDir->getID());
|
||||
|
||||
return boost::make_unique<MovingFileInsertResponseState>(FhgfsOpsErr_SUCCESS, inodeBufLen,
|
||||
std::move(inodeBuf), overWrittenEntryInfo);
|
||||
|
||||
xattr_error:
|
||||
unsigned outNumHardlinks; // Not used here!
|
||||
MsgHelperUnlink::unlinkMetaFile(*toDir, newName, NULL, outNumHardlinks);
|
||||
metaStore->releaseDir(toDir->getID());
|
||||
|
||||
return boost::make_unique<MovingFileInsertResponseState>(retVal);
|
||||
}
|
||||
|
||||
void MovingFileInsertMsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_MovingFileInsertResp);
|
||||
}
|
||||
114
meta/source/net/message/storage/moving/MovingFileInsertMsgEx.h
Normal file
114
meta/source/net/message/storage/moving/MovingFileInsertMsgEx.h
Normal file
@@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/moving/MovingFileInsertMsg.h>
|
||||
#include <common/net/message/storage/moving/MovingFileInsertRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
#include <storage/MetaStore.h>
|
||||
#include <net/msghelpers/MsgHelperXAttr.h>
|
||||
|
||||
// this class is used on the server where the file is moved to
|
||||
|
||||
class MovingFileInsertResponseState : public MirroredMessageResponseState
|
||||
{
|
||||
public:
|
||||
explicit MovingFileInsertResponseState(FhgfsOpsErr result)
|
||||
: result(result), inodeBufLen(0)
|
||||
{
|
||||
}
|
||||
|
||||
explicit MovingFileInsertResponseState(Deserializer& des)
|
||||
{
|
||||
des
|
||||
% serdes::as<int32_t>(result)
|
||||
% inodeBufLen;
|
||||
|
||||
if (inodeBufLen > META_SERBUF_SIZE)
|
||||
des.setBad();
|
||||
else
|
||||
{
|
||||
inodeBuf.reset(new char[inodeBufLen]);
|
||||
des.getBlock(inodeBuf.get(), inodeBufLen);
|
||||
}
|
||||
|
||||
des % overWrittenEntryInfo;
|
||||
}
|
||||
|
||||
MovingFileInsertResponseState(FhgfsOpsErr result, unsigned inodeBufLen,
|
||||
std::unique_ptr<char[]> inodeBuf, EntryInfo entryInfo)
|
||||
: result(result), inodeBufLen(inodeBufLen), inodeBuf(std::move(inodeBuf)),
|
||||
overWrittenEntryInfo(entryInfo)
|
||||
{
|
||||
}
|
||||
|
||||
void sendResponse(NetMessage::ResponseContext& ctx) override
|
||||
{
|
||||
ctx.sendResponse(MovingFileInsertRespMsg(result, inodeBufLen, inodeBuf.get(), overWrittenEntryInfo));
|
||||
}
|
||||
|
||||
bool changesObservableState() const override
|
||||
{
|
||||
return result == FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t serializerTag() const override { return NETMSGTYPE_MovingFileInsert; }
|
||||
|
||||
void serializeContents(Serializer& ser) const override
|
||||
{
|
||||
ser
|
||||
% serdes::as<int32_t>(result)
|
||||
% inodeBufLen;
|
||||
|
||||
ser.putBlock(inodeBuf.get(), inodeBufLen);
|
||||
}
|
||||
|
||||
private:
|
||||
FhgfsOpsErr result;
|
||||
unsigned inodeBufLen;
|
||||
std::unique_ptr<char[]> inodeBuf;
|
||||
EntryInfo overWrittenEntryInfo;
|
||||
};
|
||||
|
||||
class MovingFileInsertMsgEx : public MirroredMessage<MovingFileInsertMsg,
|
||||
std::tuple<FileIDLock, FileIDLock, FileIDLock, ParentNameLock>>
|
||||
{
|
||||
public:
|
||||
typedef MovingFileInsertResponseState ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
std::tuple<FileIDLock, FileIDLock, FileIDLock, ParentNameLock>
|
||||
lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getToDirInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
ResponseContext* rctx;
|
||||
|
||||
StringVector xattrNames;
|
||||
EntryInfo newFileInfo;
|
||||
MsgHelperXAttr::StreamXAttrState streamState;
|
||||
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
void prepareMirrorRequestArgs(RequestResponseArgs& args) override
|
||||
{
|
||||
if (isMsgHeaderFeatureFlagSet(MOVINGFILEINSERTMSG_FLAG_HAS_XATTRS))
|
||||
{
|
||||
streamState = {newFileInfo, xattrNames};
|
||||
registerStreamoutHook(args, streamState);
|
||||
}
|
||||
}
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return static_cast<MovingFileInsertRespMsg&>(resp).getResult();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "MovingFileInsertMsgEx/forward"; }
|
||||
};
|
||||
|
||||
896
meta/source/net/message/storage/moving/RenameV2MsgEx.cpp
Normal file
896
meta/source/net/message/storage/moving/RenameV2MsgEx.cpp
Normal file
@@ -0,0 +1,896 @@
|
||||
#include <common/components/streamlistenerv2/IncomingPreprocessedMsgWork.h>
|
||||
#include <common/net/message/control/GenericResponseMsg.h>
|
||||
#include <common/net/message/storage/attribs/GetEntryInfoMsg.h>
|
||||
#include <common/net/message/storage/attribs/GetEntryInfoRespMsg.h>
|
||||
#include <common/net/message/storage/attribs/UpdateDirParentMsg.h>
|
||||
#include <common/net/message/storage/attribs/UpdateDirParentRespMsg.h>
|
||||
#include <common/net/message/storage/moving/MovingDirInsertMsg.h>
|
||||
#include <common/net/message/storage/moving/MovingDirInsertRespMsg.h>
|
||||
#include <common/net/message/storage/moving/MovingFileInsertMsg.h>
|
||||
#include <common/net/message/storage/moving/MovingFileInsertRespMsg.h>
|
||||
#include <common/net/message/storage/moving/RenameRespMsg.h>
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileMsg.h>
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileRespMsg.h>
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileInodeMsg.h>
|
||||
#include <common/net/message/storage/creating/UnlinkLocalFileInodeRespMsg.h>
|
||||
#include <common/net/message/storage/attribs/StatMsg.h>
|
||||
#include <common/net/message/storage/attribs/StatRespMsg.h>
|
||||
#include <common/toolkit/MessagingTk.h>
|
||||
#include <common/toolkit/MetadataTk.h>
|
||||
#include <components/FileEventLogger.h>
|
||||
#include <components/ModificationEventFlusher.h>
|
||||
#include <net/msghelpers/MsgHelperUnlink.h>
|
||||
#include <net/msghelpers/MsgHelperXAttr.h>
|
||||
#include <net/msghelpers/MsgHelperStat.h>
|
||||
#include <program/Program.h>
|
||||
#include <session/EntryLock.h>
|
||||
#include "RenameV2MsgEx.h"
|
||||
|
||||
#include <boost/scoped_array.hpp>
|
||||
|
||||
namespace {
|
||||
struct DirHandle {
|
||||
MetaStore* metaStore;
|
||||
const EntryInfo* ei;
|
||||
|
||||
DirHandle(MetaStore* metaStore, const EntryInfo* ei): metaStore(metaStore), ei(ei) {}
|
||||
|
||||
DirHandle(const DirHandle&) = delete;
|
||||
DirHandle(DirHandle&&) = delete;
|
||||
|
||||
DirHandle& operator=(const DirHandle&) = delete;
|
||||
DirHandle& operator=(DirHandle&&) = delete;
|
||||
|
||||
~DirHandle() {
|
||||
metaStore->releaseDir(ei->getEntryID());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
RenameV2Locks RenameV2MsgEx::lock(EntryLockStore& store)
|
||||
{
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
// if the directory could not be referenced it does not exist on the current node. this will
|
||||
// cause the operation to fail lateron during executeLocally() when we reference the same
|
||||
// directory again. since we cannot do anything without having access to the source directory,
|
||||
// and since no directory with the same id as the source directory can appear after the source
|
||||
// directory has been removed, we can safely unlock everything right here and continue without
|
||||
// blocking other workers on the (probably live) target directory.
|
||||
DirInode* fromDir = metaStore->referenceDir(getFromDirInfo()->getEntryID(),
|
||||
getFromDirInfo()->getIsBuddyMirrored(), true);
|
||||
if (!fromDir)
|
||||
return {};
|
||||
|
||||
const DirHandle _from(metaStore, getFromDirInfo());
|
||||
|
||||
DirInode* toDir = metaStore->referenceDir(getToDirInfo()->getEntryID(),
|
||||
getToDirInfo()->getIsBuddyMirrored(), true);
|
||||
|
||||
if (!toDir)
|
||||
return {};
|
||||
|
||||
const DirHandle _to(metaStore, getToDirInfo());
|
||||
|
||||
for (;;) {
|
||||
RenameV2Locks result;
|
||||
|
||||
EntryInfo fromFileInfo;
|
||||
EntryInfo toFileInfo;
|
||||
|
||||
fromDir->getFileEntryInfo(getOldName(), fromFileInfo);
|
||||
bool toFileExists = toDir->getFileEntryInfo(getNewName(), toFileInfo);
|
||||
|
||||
if (toFileExists && DirEntryType_ISFILE(toFileInfo.getEntryType()))
|
||||
{
|
||||
// lock hash dir of tofile inode only if:
|
||||
// 1) its a file and
|
||||
// 2) its a non-inlined inode and
|
||||
// 3) resynch job is running
|
||||
if (resyncJob && resyncJob->isRunning() && !toFileInfo.getIsInlined())
|
||||
result.toFileHashLock = {&store, MetaStorageTk::getMetaInodeHash(toFileInfo.getEntryID())};
|
||||
}
|
||||
|
||||
{
|
||||
std::map<std::string, FileIDLock*> lockOrder;
|
||||
|
||||
lockOrder.insert(std::make_pair(getFromDirInfo()->getEntryID(), &result.fromDirLock));
|
||||
lockOrder.insert(std::make_pair(getToDirInfo()->getEntryID(), &result.toDirLock));
|
||||
if (DirEntryType_ISDIR(fromFileInfo.getEntryType()))
|
||||
lockOrder.insert(std::make_pair(fromFileInfo.getEntryID(), &result.fromFileLockD));
|
||||
|
||||
for (auto it = lockOrder.begin(); it != lockOrder.end(); ++it)
|
||||
*it->second = {&store, it->first, true};
|
||||
}
|
||||
|
||||
// we might have locked fromFileLockD before fromDirLock due to ordering. resolve the source
|
||||
// once more and check that we still refer to the same id, otherwise retry until we have the
|
||||
// correct inode.
|
||||
// if the name went away we don't have to retry (it can't be created while the dir is locked),
|
||||
// but retrying is simpler to do.
|
||||
EntryInfo fromFileInfoCheck;
|
||||
fromDir->getFileEntryInfo(getOldName(), fromFileInfoCheck);
|
||||
if (fromFileInfo.getEntryID() != fromFileInfoCheck.getEntryID())
|
||||
continue;
|
||||
|
||||
// take care about lock ordering! see MirroredMessage::lock()
|
||||
// since directories are locked for read, and by the same id as the (parent,name) tuples,the
|
||||
// same ordering applies.
|
||||
if (getFromDirInfo()->getEntryID() < getToDirInfo()->getEntryID())
|
||||
{
|
||||
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
|
||||
result.toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
|
||||
}
|
||||
else if (getFromDirInfo()->getEntryID() == getToDirInfo()->getEntryID())
|
||||
{
|
||||
if (getOldName() < getNewName())
|
||||
{
|
||||
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
|
||||
result.toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
|
||||
}
|
||||
else if (getOldName() == getNewName())
|
||||
{
|
||||
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
|
||||
}
|
||||
else
|
||||
{
|
||||
result.toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
|
||||
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
|
||||
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
|
||||
}
|
||||
|
||||
if (DirEntryType_ISFILE(fromFileInfo.getEntryType()) && fromFileInfo.getIsInlined())
|
||||
{
|
||||
if (DirEntryType_ISFILE(toFileInfo.getEntryType()) && toFileInfo.getIsInlined())
|
||||
{
|
||||
if (fromFileInfo.getEntryID() < toFileInfo.getEntryID())
|
||||
{
|
||||
result.fromFileLockF = {&store, fromFileInfo.getEntryID(), true};
|
||||
result.unlinkedFileLock = {&store, toFileInfo.getEntryID(), true};
|
||||
}
|
||||
else if (fromFileInfo.getEntryID() == toFileInfo.getEntryID())
|
||||
{
|
||||
result.fromFileLockF = {&store, fromFileInfo.getEntryID(), true};
|
||||
}
|
||||
else
|
||||
{
|
||||
result.unlinkedFileLock = {&store, toFileInfo.getEntryID(), true};
|
||||
result.fromFileLockF = {&store, fromFileInfo.getEntryID(), true};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.fromFileLockF = {&store, fromFileInfo.getEntryID(), true};
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
bool RenameV2MsgEx::processIncoming(ResponseContext& ctx)
|
||||
{
|
||||
LOG_DEBUG(__func__, Log_DEBUG, "FromDirID: " + getFromDirInfo()->getEntryID() + "; "
|
||||
"oldName: '" + getOldName() + "'; "
|
||||
"ToDirID: " + getToDirInfo()->getEntryID() + "; "
|
||||
"newName: '" + getNewName() + "'");
|
||||
|
||||
BaseType::processIncoming(ctx);
|
||||
|
||||
// update operation counters
|
||||
updateNodeOp(ctx, MetaOpCounter_RENAME);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks existence of the from-part and calls movingPerform().
|
||||
*/
|
||||
std::unique_ptr<MirroredMessageResponseState> RenameV2MsgEx::executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary)
|
||||
{
|
||||
App* app = Program::getApp();
|
||||
MetaStore* metaStore = app->getMetaStore();
|
||||
|
||||
// reference fromParent
|
||||
DirInode* fromParent = metaStore->referenceDir(getFromDirInfo()->getEntryID(),
|
||||
getFromDirInfo()->getIsBuddyMirrored(), true);
|
||||
|
||||
if (unlikely(!fromParent))
|
||||
return boost::make_unique<ResponseState>(FhgfsOpsErr_PATHNOTEXISTS);
|
||||
|
||||
std::string unlinkedEntryID; // ID of potential overwritten destination file for cleanup
|
||||
EntryInfo srcEntryInfo; // EntryInfo of source file/directory being renamed
|
||||
unsigned srcEntryLinkCount = 0; // Hardlink count needed for event logging
|
||||
|
||||
ModificationEventFlusher* modEventFlusher = app->getModificationEventFlusher();
|
||||
const bool modEventLoggingEnabled = modEventFlusher->isLoggingEnabled();
|
||||
|
||||
const bool fileEventLogEnabled = !isSecondary && getFileEvent() && app->getFileEventLogger();
|
||||
|
||||
if (modEventLoggingEnabled || fileEventLogEnabled)
|
||||
{
|
||||
fromParent->getEntryInfo(getOldName(), srcEntryInfo);
|
||||
|
||||
// Fetch link count early before rename occurs:
|
||||
// - Inlined inode data might move to a different metadata node post-rename
|
||||
// - Early fetch avoids a StatMsg round trip to retrieve link count from the new node
|
||||
FhgfsOpsErr res;
|
||||
std::tie(res, srcEntryLinkCount) = getLinkCountForMovedEntry(&srcEntryInfo);
|
||||
if (res != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LogContext("RenameV2MsgEx::executeLocally").logErr(
|
||||
"Failed to get link count for entry: " + srcEntryInfo.getEntryID());
|
||||
// don't return an error to client
|
||||
}
|
||||
}
|
||||
|
||||
FhgfsOpsErr renameRes = movingPerform(*fromParent, getOldName(),
|
||||
getEntryType(), getToDirInfo(), getNewName(), unlinkedEntryID);
|
||||
|
||||
if ((renameRes == FhgfsOpsErr_SUCCESS) && shouldFixTimestamps())
|
||||
fixInodeTimestamp(*fromParent, fromDirTimestamps);
|
||||
|
||||
metaStore->releaseDir(getFromDirInfo()->getEntryID());
|
||||
|
||||
if ((renameRes == FhgfsOpsErr_SUCCESS) && fileEventLogEnabled)
|
||||
{
|
||||
EventContext eventCtx = makeEventContext(
|
||||
&srcEntryInfo,
|
||||
getFromDirInfo()->getEntryID(),
|
||||
getMsgHeaderUserID(),
|
||||
getToDirInfo()->getEntryID(),
|
||||
srcEntryLinkCount,
|
||||
isSecondary
|
||||
);
|
||||
|
||||
logEvent(app->getFileEventLogger(), *getFileEvent(), eventCtx);
|
||||
}
|
||||
|
||||
// clean-up
|
||||
if ((renameRes == FhgfsOpsErr_SUCCESS) && modEventLoggingEnabled)
|
||||
{
|
||||
if (DirEntryType_ISDIR(getEntryType()))
|
||||
modEventFlusher->add(ModificationEvent_DIRMOVED, srcEntryInfo.getEntryID());
|
||||
else
|
||||
{
|
||||
modEventFlusher->add(ModificationEvent_FILEMOVED, srcEntryInfo.getEntryID());
|
||||
if (!unlinkedEntryID.empty())
|
||||
modEventFlusher->add(ModificationEvent_FILEREMOVED, unlinkedEntryID);
|
||||
}
|
||||
}
|
||||
|
||||
return boost::make_unique<ResponseState>(renameRes);
|
||||
}
|
||||
|
||||
FhgfsOpsErr RenameV2MsgEx::movingPerform(DirInode& fromParent, const std::string& oldName,
|
||||
DirEntryType entryType, EntryInfo* toDirInfo, const std::string& newName, std::string& unlinkedEntryID)
|
||||
{
|
||||
const char* logContext = "RenameV2MsgEx::movingPerform";
|
||||
IGNORE_UNUSED_VARIABLE(logContext);
|
||||
App* app = Program::getApp();
|
||||
|
||||
// is this node the owner of the fromParent dir?
|
||||
MirrorBuddyGroupMapper* metaBuddyGroupMapper = app->getMetaBuddyGroupMapper();
|
||||
NumNodeID expectedOwnerID = fromParent.getIsBuddyMirrored() ?
|
||||
NumNodeID(metaBuddyGroupMapper->getLocalGroupID() ) : app->getLocalNode().getNumID();
|
||||
|
||||
if (fromParent.getOwnerNodeID() != expectedOwnerID)
|
||||
return FhgfsOpsErr_NOTOWNER;
|
||||
|
||||
if (unlikely(entryType == DirEntryType_INVALID) )
|
||||
{
|
||||
LOG_DEBUG(logContext, Log_SPAM, "Received an invalid entry type!");
|
||||
return FhgfsOpsErr_INTERNAL;
|
||||
}
|
||||
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_INTERNAL;
|
||||
|
||||
if (fromParent.getID() == toDirInfo->getEntryID())
|
||||
{ // simple rename (<= not a move && everything local)
|
||||
LOG_DEBUG(logContext, Log_SPAM, "Method: rename in same dir"); // debug in
|
||||
retVal = renameInSameDir(fromParent, oldName, newName, unlinkedEntryID);
|
||||
}
|
||||
else
|
||||
if (entryType == DirEntryType_DIRECTORY)
|
||||
retVal = renameDir(fromParent, oldName, toDirInfo, newName);
|
||||
else
|
||||
retVal = renameFile(fromParent, oldName, toDirInfo, newName, unlinkedEntryID);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void RenameV2MsgEx::forwardToSecondary(ResponseContext& ctx)
|
||||
{
|
||||
sendToSecondary(ctx, *this, NETMSGTYPE_RenameResp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a directory
|
||||
*/
|
||||
FhgfsOpsErr RenameV2MsgEx::renameDir(DirInode& fromParent, const std::string& oldName,
|
||||
EntryInfo* toDirInfo, const std::string& newName)
|
||||
{
|
||||
const char* logContext = "RenameV2MsgEx::renameDir";
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_INTERNAL;
|
||||
|
||||
DirEntry fromDirEntry(oldName);
|
||||
bool dirEntryCopyRes = fromParent.getDirDentry(oldName, fromDirEntry);
|
||||
if (!dirEntryCopyRes)
|
||||
{
|
||||
LOG_DEBUG("RenameV2MsgEx::movingPerform", Log_SPAM, "getDirEntryCopy() failed");
|
||||
return FhgfsOpsErr_NOTADIR;
|
||||
}
|
||||
|
||||
// when we we verified the 'oldName' is really a directory
|
||||
LOG_DEBUG(logContext, Log_SPAM, "Method: remote dir move."); // debug in
|
||||
|
||||
if (fromParent.getIsBuddyMirrored() && hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
|
||||
{
|
||||
retVal = FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
// prepare local part of the move operation
|
||||
boost::scoped_array<char> serialBuf(new char[META_SERBUF_SIZE]);
|
||||
Serializer ser(serialBuf.get(), META_SERBUF_SIZE);
|
||||
|
||||
/* Put all meta data of this dentry into the given buffer. The buffer then will be
|
||||
* used on the remote side to fill the new dentry */
|
||||
fromDirEntry.serializeDentry(ser);
|
||||
|
||||
if (!ser.good())
|
||||
LogContext(logContext).logErr("dentry too large: " + oldName);
|
||||
else
|
||||
retVal = remoteDirInsert(toDirInfo, newName, serialBuf.get(), ser.size());
|
||||
}
|
||||
|
||||
// finish local part of the move operation
|
||||
if (retVal == FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
DirEntry* rmDirEntry;
|
||||
|
||||
retVal = fromParent.removeDir(oldName, &rmDirEntry);
|
||||
|
||||
if (retVal == FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
std::string parentID = fromParent.getID();
|
||||
EntryInfo removedInfo;
|
||||
|
||||
rmDirEntry->getEntryInfo(parentID, 0, &removedInfo);
|
||||
|
||||
if (!hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
|
||||
updateRenamedDirInode(&removedInfo, toDirInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogContext(logContext).log(Log_CRITICAL,
|
||||
std::string("Failed to remove fromDir: ") + oldName +
|
||||
" Error: " + boost::lexical_cast<std::string>(retVal));
|
||||
}
|
||||
|
||||
SAFE_DELETE(rmDirEntry);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a file
|
||||
*/
|
||||
FhgfsOpsErr RenameV2MsgEx::renameFile(DirInode& fromParent, const std::string& oldName,
|
||||
EntryInfo* toDirInfo, const std::string& newName, std::string& unlinkedEntryID)
|
||||
{
|
||||
const char* logContext = "RenameV2MsgEx::renameFile";
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_INTERNAL;
|
||||
|
||||
EntryInfo fromFileInfo;
|
||||
bool getRes = fromParent.getFileEntryInfo(oldName, fromFileInfo);
|
||||
if (!getRes)
|
||||
{
|
||||
LOG_DEBUG(logContext, Log_SPAM, "Error: fromDir does not exist.");
|
||||
return FhgfsOpsErr_PATHNOTEXISTS;
|
||||
}
|
||||
|
||||
// when we are here we verified the file to be renamed is really a file
|
||||
|
||||
if (fromParent.getIsBuddyMirrored() && hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
|
||||
{
|
||||
retVal = FhgfsOpsErr_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the buffer is used to transfer all data of the dir-entry
|
||||
boost::scoped_array<char> serialBuf(new char[META_SERBUF_SIZE]);
|
||||
size_t usedSerialBufLen;
|
||||
|
||||
retVal = metaStore->moveRemoteFileBegin(
|
||||
fromParent, &fromFileInfo, serialBuf.get(), META_SERBUF_SIZE, &usedSerialBufLen);
|
||||
if (retVal != FhgfsOpsErr_SUCCESS)
|
||||
return retVal;
|
||||
|
||||
LOG_DEBUG(logContext, Log_SPAM, "Method: remote file move."); // debug in
|
||||
|
||||
StringVector xattrNames;
|
||||
if (Program::getApp()->getConfig()->getStoreClientXAttrs() && fromFileInfo.getIsInlined())
|
||||
{
|
||||
FhgfsOpsErr listXAttrRes;
|
||||
|
||||
std::tie(listXAttrRes, xattrNames) = MsgHelperXAttr::listxattr(&fromFileInfo);
|
||||
if (listXAttrRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
metaStore->moveRemoteFileComplete(fromParent, fromFileInfo.getEntryID());
|
||||
return FhgfsOpsErr_TOOBIG;
|
||||
}
|
||||
}
|
||||
|
||||
// Do the remote operation (insert and possible unlink of an existing toFile)
|
||||
retVal = remoteFileInsertAndUnlink(&fromFileInfo, toDirInfo, newName, serialBuf.get(),
|
||||
usedSerialBufLen, std::move(xattrNames), unlinkedEntryID);
|
||||
}
|
||||
|
||||
// finish local part of the owned file move operation (+ remove local file-link)
|
||||
if (retVal == FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
FhgfsOpsErr unlinkRes;
|
||||
|
||||
if (fromFileInfo.getIsInlined())
|
||||
{
|
||||
// We are not interested in the inode here , as we are not going to delete storage chunks.
|
||||
EntryInfo entryInfo;
|
||||
unsigned outNumHardlinks; // Not used here!
|
||||
unlinkRes = metaStore->unlinkFile(fromParent, oldName, &entryInfo, NULL, outNumHardlinks);
|
||||
}
|
||||
else
|
||||
{
|
||||
// only unlink dentry-by-filename for nonInlined inode(s)
|
||||
DirEntry oldDentry(oldName);
|
||||
if (fromParent.getDentry(oldName, oldDentry))
|
||||
unlinkRes = fromParent.unlinkDirEntry(oldName, &oldDentry, DirEntry_UNLINK_FILENAME);
|
||||
else
|
||||
unlinkRes = FhgfsOpsErr_PATHNOTEXISTS;
|
||||
}
|
||||
|
||||
if (unlikely (unlinkRes) )
|
||||
{
|
||||
LogContext(logContext).logErr(std::string("Error: Failed to unlink fromFile: ") +
|
||||
"DirID: " + fromFileInfo.getParentEntryID() + " "
|
||||
"entryName: " + oldName + ". " +
|
||||
"Remote toFile was successfully created.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!fromParent.getIsBuddyMirrored() || !hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
|
||||
metaStore->moveRemoteFileComplete(fromParent, fromFileInfo.getEntryID() );
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames a directory or a file (no moving between different directories involved).
|
||||
*/
|
||||
FhgfsOpsErr RenameV2MsgEx::renameInSameDir(DirInode& fromParent, const std::string& oldName,
|
||||
const std::string& toName, std::string& unlinkedEntryID)
|
||||
{
|
||||
const char* logContext = "RenameV2MsgEx::renameInSameDir";
|
||||
MetaStore* metaStore = Program::getApp()->getMetaStore();
|
||||
|
||||
/* we are passing here the very same fromParent pointer also a toParent pointer, which is
|
||||
* essential in order not to dead-lock */
|
||||
|
||||
std::unique_ptr<FileInode> unlinkInode; // inode belong to a possibly existing toName file
|
||||
DirEntry* overWrittenEntry = NULL;
|
||||
bool wasInlined; // to determine if overwritten inode was inlined or not
|
||||
|
||||
FhgfsOpsErr renameRes = metaStore->renameInSameDir(fromParent, oldName, toName,
|
||||
&unlinkInode, overWrittenEntry, wasInlined);
|
||||
|
||||
if (renameRes == FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
if (shouldFixTimestamps())
|
||||
{
|
||||
DirEntry dentry(toName);
|
||||
if (fromParent.getDentry(toName, dentry))
|
||||
{
|
||||
EntryInfo info;
|
||||
dentry.getEntryInfo(fromParent.getID(), 0, &info);
|
||||
|
||||
if (DirEntryType_ISDIR(info.getEntryType()))
|
||||
{
|
||||
auto dir = metaStore->referenceDir(info.getEntryID(), info.getIsBuddyMirrored(),
|
||||
true);
|
||||
if (dir)
|
||||
{
|
||||
fixInodeTimestamp(*dir, renamedInodeTimestamps);
|
||||
metaStore->releaseDir(dir->getID());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto [file, referenceRes] = metaStore->referenceFile(&info);
|
||||
if (file)
|
||||
{
|
||||
fixInodeTimestamp(*file, renamedInodeTimestamps, &info);
|
||||
metaStore->releaseFile(fromParent.getID(), file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (overWrittenEntry && !overWrittenEntry->getIsInodeInlined())
|
||||
{
|
||||
EntryInfo overWrittenEntryInfo;
|
||||
overWrittenEntry->getEntryInfo(fromParent.getID(), 0, &overWrittenEntryInfo);
|
||||
|
||||
if (wasInlined)
|
||||
{
|
||||
// if overwritten file previously had an inlined inode which got de-inlined because
|
||||
// it was IN_USE during rename operation - in this case, its inode should already be
|
||||
// linked to disposal directory and will be removed upon close()
|
||||
return renameRes;
|
||||
}
|
||||
else if (!hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
|
||||
{
|
||||
// if overwritten file had a non-inlined inode - update hardlink count and remove
|
||||
// inode and chunk files if link count becomes zero
|
||||
unlinkRemoteFileInode(&overWrittenEntryInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// handle unlinkInode for inlined inode(s)
|
||||
if (unlinkInode && !hasFlag(NetMessageHeader::Flag_BuddyMirrorSecond))
|
||||
{
|
||||
unlinkedEntryID = unlinkInode->getEntryID();
|
||||
|
||||
FhgfsOpsErr chunkUnlinkRes = MsgHelperUnlink::unlinkChunkFiles(
|
||||
unlinkInode.release(), getMsgHeaderUserID() );
|
||||
|
||||
if (chunkUnlinkRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LogContext(logContext).logErr(std::string("Rename succeeded, but unlinking storage ") +
|
||||
"chunk files of the overwritten targetFileName (" + toName + ") failed. " +
|
||||
"Entry-ID: " + unlinkedEntryID);
|
||||
|
||||
// we can't do anything about it, so we won't even inform the user
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return renameRes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: This method not only sends the insertion message, but also unlinks an overwritten local
|
||||
* file if it is contained in the response.
|
||||
*
|
||||
* @param serialBuf the inode values serialized into this buffer.
|
||||
*/
|
||||
FhgfsOpsErr RenameV2MsgEx::remoteFileInsertAndUnlink(EntryInfo* fromFileInfo, EntryInfo* toDirInfo,
|
||||
std::string newName, char* serialBuf, size_t serialBufLen, StringVector xattrs,
|
||||
std::string& unlinkedEntryID)
|
||||
{
|
||||
LogContext log("RenameV2MsgEx::remoteFileInsert");
|
||||
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
|
||||
|
||||
App* app = Program::getApp();
|
||||
NumNodeID toNodeID = toDirInfo->getOwnerNodeID();
|
||||
|
||||
LOG_DEBUG_CONTEXT(log, Log_DEBUG,
|
||||
"Inserting remote parentID: " + toDirInfo->getEntryID() + "; "
|
||||
"newName: '" + newName + "'; "
|
||||
"entryID: '" + fromFileInfo->getEntryID() + "'; "
|
||||
"node: " + toNodeID.str() );
|
||||
|
||||
// prepare request
|
||||
|
||||
MovingFileInsertMsg insertMsg(fromFileInfo, toDirInfo, newName, serialBuf, serialBufLen);
|
||||
|
||||
RequestResponseArgs rrArgs(NULL, &insertMsg, NETMSGTYPE_MovingFileInsertResp);
|
||||
|
||||
RequestResponseNode rrNode(toNodeID, app->getMetaNodes() );
|
||||
rrNode.setTargetStates(app->getMetaStateStore() );
|
||||
if (toDirInfo->getIsBuddyMirrored())
|
||||
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
|
||||
|
||||
// send request and receive response
|
||||
|
||||
MsgHelperXAttr::StreamXAttrState streamState(*fromFileInfo, std::move(xattrs));
|
||||
insertMsg.registerStreamoutHook(rrArgs, streamState);
|
||||
|
||||
FhgfsOpsErr requestRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
|
||||
if(requestRes != FhgfsOpsErr_SUCCESS)
|
||||
{ // communication error
|
||||
log.log(Log_WARNING,
|
||||
"Communication with metadata sever failed. nodeID: " + toNodeID.str() );
|
||||
return requestRes;
|
||||
}
|
||||
|
||||
// correct response type received
|
||||
const auto insertRespMsg = (MovingFileInsertRespMsg*)rrArgs.outRespMsg.get();
|
||||
retVal = insertRespMsg->getResult();
|
||||
|
||||
// handle unlink of chunk files for inlined inode(s)
|
||||
// handle unlink of inode for non-inlined inode(s)
|
||||
EntryInfo* overWrittenInfo = insertRespMsg->getOverWrittenEntryInfo();
|
||||
if (overWrittenInfo->getEntryType() != DirEntryType_INVALID)
|
||||
{
|
||||
if (overWrittenInfo->getIsInlined())
|
||||
{
|
||||
unsigned unlinkedInodeBufLen = insertRespMsg->getInodeBufLen();
|
||||
if (unlinkedInodeBufLen)
|
||||
{
|
||||
const char* unlinkedInodeBuf = insertRespMsg->getInodeBuf();
|
||||
FileInode* toUnlinkInode = new FileInode();
|
||||
|
||||
Deserializer des(unlinkedInodeBuf, unlinkedInodeBufLen);
|
||||
toUnlinkInode->deserializeMetaData(des);
|
||||
if(unlikely(!des.good()))
|
||||
{ // deserialization of received inode failed (should never happen)
|
||||
log.logErr("Failed to deserialize unlinked file inode. nodeID: " + toNodeID.str() );
|
||||
delete(toUnlinkInode);
|
||||
}
|
||||
else if (toUnlinkInode->getIsInlined())
|
||||
{
|
||||
MsgHelperUnlink::unlinkChunkFiles(
|
||||
toUnlinkInode, getMsgHeaderUserID() ); // destructs toUnlinkInode
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
unlinkRemoteFileInode(overWrittenInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (retVal != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
// error: remote file not inserted
|
||||
LOG_DEBUG_CONTEXT(log, Log_NOTICE,
|
||||
"Metadata server was unable to insert file. "
|
||||
"nodeID: " + toNodeID.str() + "; "
|
||||
"Error: " + boost::lexical_cast<std::string>(retVal));
|
||||
}
|
||||
else
|
||||
{
|
||||
// success: remote file inserted
|
||||
LOG_DEBUG_CONTEXT(log, Log_DEBUG, "Metadata server inserted file. "
|
||||
"nodeID: " + toNodeID.str() );
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert a remote directory dir-entry
|
||||
*/
|
||||
FhgfsOpsErr RenameV2MsgEx::remoteDirInsert(EntryInfo* toDirInfo, const std::string& newName,
|
||||
char* serialBuf, size_t serialBufLen)
|
||||
{
|
||||
LogContext log("RenameV2MsgEx::remoteDirInsert");
|
||||
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
|
||||
|
||||
App* app = Program::getApp();
|
||||
NumNodeID toNodeID = toDirInfo->getOwnerNodeID();
|
||||
|
||||
LOG_DEBUG_CONTEXT(log, Log_DEBUG,
|
||||
"Inserting remote parentID: " + toDirInfo->getEntryID() + "; "
|
||||
"newName: '" + newName + "'; "
|
||||
"node: " + toNodeID.str() );
|
||||
|
||||
// prepare request
|
||||
|
||||
MovingDirInsertMsg insertMsg(toDirInfo, newName, serialBuf, serialBufLen);
|
||||
|
||||
RequestResponseArgs rrArgs(NULL, &insertMsg, NETMSGTYPE_MovingDirInsertResp);
|
||||
|
||||
RequestResponseNode rrNode(toNodeID, app->getMetaNodes() );
|
||||
rrNode.setTargetStates(app->getMetaStateStore() );
|
||||
if (toDirInfo->getIsBuddyMirrored())
|
||||
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
|
||||
|
||||
|
||||
// send request and receive response
|
||||
|
||||
FhgfsOpsErr requestRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
|
||||
if(requestRes != FhgfsOpsErr_SUCCESS)
|
||||
{ // communication error
|
||||
log.log(Log_WARNING, "Communication with metadata server failed. nodeID: " + toNodeID.str() );
|
||||
return requestRes;
|
||||
}
|
||||
|
||||
// correct response type received
|
||||
const auto insertRespMsg = (const MovingDirInsertRespMsg*)rrArgs.outRespMsg.get();
|
||||
|
||||
retVal = insertRespMsg->getResult();
|
||||
if(retVal != FhgfsOpsErr_SUCCESS)
|
||||
{ // error: remote dir not inserted
|
||||
LOG_DEBUG_CONTEXT(log, Log_NOTICE,
|
||||
"Metdata server was unable to insert directory. "
|
||||
"nodeID: " + toNodeID.str() + "; "
|
||||
"Error: " + boost::lexical_cast<std::string>(retVal));
|
||||
}
|
||||
else
|
||||
{
|
||||
// success: remote dir inserted
|
||||
LOG_DEBUG_CONTEXT(log, Log_DEBUG,
|
||||
"Metadata server inserted directory. "
|
||||
"nodeID: " + toNodeID.str() );
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the new parent information to the inode of renamedDirEntryInfo
|
||||
*/
|
||||
FhgfsOpsErr RenameV2MsgEx::updateRenamedDirInode(EntryInfo* renamedDirEntryInfo,
|
||||
EntryInfo* toDirInfo)
|
||||
{
|
||||
LogContext log("RenameV2MsgEx::updateRenamedDirInode");
|
||||
|
||||
const std::string& parentEntryID = toDirInfo->getEntryID();
|
||||
renamedDirEntryInfo->setParentEntryID(parentEntryID);
|
||||
|
||||
App* app = Program::getApp();
|
||||
NumNodeID toNodeID = renamedDirEntryInfo->getOwnerNodeID();
|
||||
|
||||
LOG_DEBUG_CONTEXT(log, Log_DEBUG,
|
||||
"Update remote inode: " + renamedDirEntryInfo->getEntryID() + "; "
|
||||
"node: " + toNodeID.str() );
|
||||
|
||||
// prepare request
|
||||
|
||||
UpdateDirParentMsg updateMsg(renamedDirEntryInfo, toDirInfo->getOwnerNodeID() );
|
||||
|
||||
RequestResponseArgs rrArgs(NULL, &updateMsg, NETMSGTYPE_UpdateDirParentResp);
|
||||
|
||||
RequestResponseNode rrNode(toNodeID, app->getMetaNodes() );
|
||||
rrNode.setTargetStates(app->getMetaStateStore() );
|
||||
if (renamedDirEntryInfo->getIsBuddyMirrored())
|
||||
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
|
||||
|
||||
// send request and receive response
|
||||
|
||||
FhgfsOpsErr requestRes = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
|
||||
if(requestRes != FhgfsOpsErr_SUCCESS)
|
||||
{ // communication error
|
||||
log.log(Log_WARNING,
|
||||
"Communication with metadata server failed. nodeID: " + toNodeID.str() );
|
||||
return requestRes;
|
||||
}
|
||||
|
||||
// correct response type received
|
||||
const auto updateRespMsg = (const UpdateDirParentRespMsg*)rrArgs.outRespMsg.get();
|
||||
|
||||
FhgfsOpsErr retVal = (FhgfsOpsErr)updateRespMsg->getValue();
|
||||
if(retVal != FhgfsOpsErr_SUCCESS)
|
||||
{ // error
|
||||
LOG_DEBUG_CONTEXT(log, Log_NOTICE,
|
||||
"Failed to update ParentEntryID: " + renamedDirEntryInfo->getEntryID() + "; "
|
||||
"nodeID: " + toNodeID.str() + "; "
|
||||
"Error: " + boost::lexical_cast<std::string>(retVal));
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle unlink for a non-inlined inode
|
||||
* Decrement hardLink count, if linkCount becomes zero then remove inode and chunk files
|
||||
*/
|
||||
FhgfsOpsErr RenameV2MsgEx::unlinkRemoteFileInode(EntryInfo* entryInfo)
|
||||
{
|
||||
const char* logContext = "Unlink remote file inode";
|
||||
App* app = Program::getApp();
|
||||
FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS;
|
||||
|
||||
NumNodeID ownerNodeID = entryInfo->getOwnerNodeID();
|
||||
|
||||
UnlinkLocalFileInodeMsg unlinkInodeMsg(entryInfo);
|
||||
RequestResponseArgs rrArgs(NULL, &unlinkInodeMsg, NETMSGTYPE_UnlinkLocalFileInodeResp);
|
||||
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes());
|
||||
|
||||
if (entryInfo->getIsBuddyMirrored())
|
||||
rrNode.setMirrorInfo(Program::getApp()->getMetaBuddyGroupMapper(), false);
|
||||
|
||||
do
|
||||
{
|
||||
retVal = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
|
||||
if (unlikely(retVal != FhgfsOpsErr_SUCCESS))
|
||||
{
|
||||
LogContext(logContext).log(Log_WARNING,
|
||||
"Communication with metadata server failed. "
|
||||
"nodeID: " + ownerNodeID.str() + "; " +
|
||||
"entryID: " + entryInfo->getEntryID().c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
// response received
|
||||
const auto unlinkFileInodeRespMsg = (UnlinkLocalFileInodeRespMsg*) rrArgs.outRespMsg.get();
|
||||
retVal = unlinkFileInodeRespMsg->getResult();
|
||||
if (retVal != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
// error: either inode file doesn't exists or some other error happened
|
||||
LogContext(logContext).logErr("unlink of inode failed! "
|
||||
"nodeID: " + ownerNodeID.str() + "; " +
|
||||
"entryID: " + entryInfo->getEntryID().c_str());
|
||||
break;
|
||||
}
|
||||
} while (false);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
std::pair<FhgfsOpsErr, unsigned> RenameV2MsgEx::getLinkCountForMovedEntry(EntryInfo* entryInfo)
|
||||
{
|
||||
const char* logContext = "RenameV2Msg (Get Link Count)";
|
||||
|
||||
App* app = Program::getApp();
|
||||
FhgfsOpsErr statRes = FhgfsOpsErr_SUCCESS;
|
||||
unsigned linkCount = 0;
|
||||
|
||||
NumNodeID ownerNodeID = entryInfo->getOwnerNodeID();
|
||||
bool isLocalOwner = ((!isMirrored() && ownerNodeID == app->getLocalNode().getNumID()) ||
|
||||
(isMirrored() && ownerNodeID.val() == app->getMetaBuddyGroupMapper()->getLocalGroupID()));
|
||||
|
||||
if (isLocalOwner)
|
||||
{
|
||||
StatData statData;
|
||||
statRes = MsgHelperStat::stat(entryInfo, true, getMsgHeaderUserID(), statData);
|
||||
|
||||
if (statRes == FhgfsOpsErr_SUCCESS)
|
||||
linkCount = statData.getNumHardlinks();
|
||||
else
|
||||
LogContext(logContext).logErr("Stat Failed!. entryID: " + entryInfo->getEntryID());
|
||||
}
|
||||
else
|
||||
{
|
||||
// send StatMsg to remote meta node/buddygroup
|
||||
StatMsg statMsg(entryInfo);
|
||||
RequestResponseArgs rrArgs(NULL, &statMsg, NETMSGTYPE_StatResp);
|
||||
RequestResponseNode rrNode(ownerNodeID, app->getMetaNodes());
|
||||
rrNode.setTargetStates(app->getMetaStateStore());
|
||||
|
||||
if (entryInfo->getIsBuddyMirrored())
|
||||
rrNode.setMirrorInfo(app->getMetaBuddyGroupMapper(), false);
|
||||
|
||||
do
|
||||
{
|
||||
FhgfsOpsErr resp = MessagingTk::requestResponseNode(&rrNode, &rrArgs);
|
||||
if (unlikely(resp != FhgfsOpsErr_SUCCESS))
|
||||
{
|
||||
LogContext(logContext).logErr("Communication with metadata server failed. "
|
||||
"nodeID: " + ownerNodeID.str());
|
||||
statRes = resp;
|
||||
break;
|
||||
}
|
||||
|
||||
// response received
|
||||
const auto statRespMsg = (StatRespMsg*) rrArgs.outRespMsg.get();
|
||||
statRes = (FhgfsOpsErr) statRespMsg->getResult();
|
||||
if (statRes != FhgfsOpsErr_SUCCESS)
|
||||
{
|
||||
LogContext(logContext).logErr("Stat Failed!. nodeID: " + ownerNodeID.str()
|
||||
+ "; entryID: " + entryInfo->getEntryID());
|
||||
break;
|
||||
}
|
||||
|
||||
// success
|
||||
linkCount = statRespMsg->getStatData()->getNumHardlinks();
|
||||
} while (false);
|
||||
}
|
||||
|
||||
return {statRes, linkCount};
|
||||
}
|
||||
100
meta/source/net/message/storage/moving/RenameV2MsgEx.h
Normal file
100
meta/source/net/message/storage/moving/RenameV2MsgEx.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include <common/storage/StorageErrors.h>
|
||||
#include <common/net/message/storage/moving/RenameMsg.h>
|
||||
#include <common/net/message/storage/moving/RenameRespMsg.h>
|
||||
#include <net/message/MirroredMessage.h>
|
||||
#include <storage/DirEntry.h>
|
||||
#include <storage/MetaStore.h>
|
||||
|
||||
struct RenameV2Locks
|
||||
{
|
||||
HashDirLock toFileHashLock;
|
||||
|
||||
ParentNameLock fromNameLock;
|
||||
ParentNameLock toNameLock;
|
||||
FileIDLock fromDirLock;
|
||||
FileIDLock toDirLock;
|
||||
// source file must be locked because concurrent modifications of file attributes may
|
||||
// race with the moving operation between two servers.
|
||||
FileIDLock fromFileLockF;
|
||||
FileIDLock fromFileLockD;
|
||||
// if target exists, the target file must be unlocked to exclude concurrent operations on
|
||||
// target (eg close, setxattr, ...)
|
||||
FileIDLock unlinkedFileLock;
|
||||
|
||||
RenameV2Locks() = default;
|
||||
|
||||
RenameV2Locks(const RenameV2Locks&) = delete;
|
||||
RenameV2Locks& operator=(const RenameV2Locks&) = delete;
|
||||
|
||||
RenameV2Locks(RenameV2Locks&& other)
|
||||
{
|
||||
swap(other);
|
||||
}
|
||||
|
||||
RenameV2Locks& operator=(RenameV2Locks&& other)
|
||||
{
|
||||
RenameV2Locks(std::move(other)).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(RenameV2Locks& other)
|
||||
{
|
||||
std::swap(toFileHashLock, other.toFileHashLock);
|
||||
std::swap(fromNameLock, other.fromNameLock);
|
||||
std::swap(toNameLock, other.toNameLock);
|
||||
std::swap(fromDirLock, other.fromDirLock);
|
||||
std::swap(toDirLock, other.toDirLock);
|
||||
std::swap(fromFileLockF, other.fromFileLockF);
|
||||
std::swap(fromFileLockD, other.fromFileLockD);
|
||||
std::swap(unlinkedFileLock, other.unlinkedFileLock);
|
||||
}
|
||||
};
|
||||
|
||||
class RenameV2MsgEx : public MirroredMessage<RenameMsg, RenameV2Locks>
|
||||
{
|
||||
public:
|
||||
typedef ErrorCodeResponseState<RenameRespMsg, NETMSGTYPE_Rename> ResponseState;
|
||||
|
||||
virtual bool processIncoming(ResponseContext& ctx) override;
|
||||
|
||||
RenameV2Locks lock(EntryLockStore& store) override;
|
||||
|
||||
bool isMirrored() override { return getFromDirInfo()->getIsBuddyMirrored(); }
|
||||
|
||||
private:
|
||||
std::unique_ptr<MirroredMessageResponseState> executeLocally(ResponseContext& ctx,
|
||||
bool isSecondary) override;
|
||||
|
||||
FhgfsOpsErr movingPerform(DirInode& fromParent, const std::string& oldName,
|
||||
DirEntryType entryType, EntryInfo* toDirInfo, const std::string& newName,
|
||||
std::string& unlinkedEntryID);
|
||||
|
||||
FhgfsOpsErr renameInSameDir(DirInode& fromParent, const std::string& oldName,
|
||||
const std::string& toName, std::string& unlinkedEntryID);
|
||||
FhgfsOpsErr renameDir(DirInode& fromParent, const std::string& oldName,
|
||||
EntryInfo* toDirInfo, const std::string& newName);
|
||||
FhgfsOpsErr renameFile(DirInode& fromParent, const std::string& oldName, EntryInfo* toDirInfo,
|
||||
const std::string& newName, std::string& unlinkedEntryID);
|
||||
|
||||
FhgfsOpsErr remoteFileInsertAndUnlink(EntryInfo* fromFileInfo, EntryInfo* toDirInfo,
|
||||
const std::string newName, char* serialBuf, size_t serialBufLen,
|
||||
StringVector xattrs, std::string& unlinkedEntryID);
|
||||
FhgfsOpsErr remoteDirInsert(EntryInfo* toDirInfo, const std::string& newName,
|
||||
char* serialBuf, size_t serialBufLen);
|
||||
FhgfsOpsErr updateRenamedDirInode(EntryInfo* renamedDirEntryInfo, EntryInfo* toDirInfo);
|
||||
FhgfsOpsErr unlinkRemoteFileInode(EntryInfo* entryInfo);
|
||||
std::pair<FhgfsOpsErr, unsigned> getLinkCountForMovedEntry(EntryInfo* entryInfo);
|
||||
|
||||
void forwardToSecondary(ResponseContext& ctx) override;
|
||||
|
||||
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
|
||||
{
|
||||
return (FhgfsOpsErr) static_cast<RenameRespMsg&>(resp).getValue();
|
||||
}
|
||||
|
||||
const char* mirrorLogContext() const override { return "RenameV2MsgEx/forward"; }
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user