897 lines
32 KiB
C++
897 lines
32 KiB
C++
#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};
|
|
}
|