Commit 0e22941a authored by Phoebe Buckheister's avatar Phoebe Buckheister 🦎 Committed by Christian Mohrbacher

meta: lock overwritten file during rename in same dir

if a file is overwritten by a rename it must be locked before it is
actually touched in any way. without the lock concurrent operations on
the same inode (like close, setxattr, or others) can race with the
unlink. this race is normally inconsequential, but during mod resync it
allows an inlined inode to be deleted before all "file changed" modsync
events have been processed, causing the resync to resync to fail with
modsync errors.

(cherry picked from commit 2e4d732ca2a343844fbf76cdec94107e040a406a)
parent 6baf0fbb
......@@ -25,32 +25,12 @@
#include <boost/scoped_array.hpp>
std::tuple<DirIDLock, DirIDLock, ParentNameLock, ParentNameLock, FileIDLock> RenameV2MsgEx::lock(
EntryLockStore& store)
RenameV2Locks RenameV2MsgEx::lock(EntryLockStore& store)
{
ParentNameLock fromNameLock;
ParentNameLock toNameLock;
DirIDLock fromDirLock;
DirIDLock toDirLock;
// we have to lock the file as well, because concurrent modifications of file attributes may
// race with the moving operation between two servers.
FileIDLock fileLock;
EntryInfo fromFileInfo;
RenameV2Locks result;
MetaStore* metaStore = Program::getApp()->getMetaStore();
DirInode* fromDir = metaStore->referenceDir(getFromDirInfo()->getEntryID(),
getFromDirInfo()->getIsBuddyMirrored(), true);
if (!fromDir)
return {};
else
{
fromDir->getFileEntryInfo(getOldName(), fromFileInfo);
metaStore->releaseDir(getFromDirInfo()->getEntryID());
}
// 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.
......@@ -58,32 +38,67 @@ std::tuple<DirIDLock, DirIDLock, ParentNameLock, ParentNameLock, FileIDLock> Ren
|| (getFromDirInfo()->getEntryID() == getToDirInfo()->getEntryID()
&& getOldName() < getNewName()))
{
fromDirLock = {&store, getFromDirInfo()->getEntryID(), true};
result.fromDirLock = {&store, getFromDirInfo()->getEntryID(), true};
if (getFromDirInfo()->getEntryID() != getToDirInfo()->getEntryID())
toDirLock = {&store, getToDirInfo()->getEntryID(), true};
result.toDirLock = {&store, getToDirInfo()->getEntryID(), true};
fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
if (getOldName() != getNewName())
toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
result.toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
}
else
{
toDirLock = {&store, getToDirInfo()->getEntryID(), true};
result.toDirLock = {&store, getToDirInfo()->getEntryID(), true};
if (getFromDirInfo()->getEntryID() != getToDirInfo()->getEntryID())
fromDirLock = {&store, getFromDirInfo()->getEntryID(), true};
result.fromDirLock = {&store, getFromDirInfo()->getEntryID(), true};
toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
result.toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
if (getOldName() != getNewName())
fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
}
EntryInfo fromFileInfo;
EntryInfo toFileInfo;
DirInode* fromDir = metaStore->referenceDir(getFromDirInfo()->getEntryID(),
getFromDirInfo()->getIsBuddyMirrored(), true);
if (!fromDir)
return {};
else
{
fromDir->getFileEntryInfo(getOldName(), fromFileInfo);
if (getFromDirInfo()->getEntryID() == getToDirInfo()->getEntryID())
fromDir->getFileEntryInfo(getNewName(), toFileInfo);
metaStore->releaseDir(getFromDirInfo()->getEntryID());
}
if (DirEntryType_ISFILE(fromFileInfo.getEntryType()) && fromFileInfo.getIsInlined())
fileLock = {&store, fromFileInfo.getEntryID()};
{
if (DirEntryType_ISFILE(toFileInfo.getEntryType()) && toFileInfo.getIsInlined())
{
if (fromFileInfo.getEntryID() < toFileInfo.getEntryID())
{
result.fromFileLock = {&store, fromFileInfo.getEntryID()};
result.unlinkedFileLock = {&store, toFileInfo.getEntryID()};
}
else if (fromFileInfo.getEntryID() < toFileInfo.getEntryID())
{
result.fromFileLock = {&store, fromFileInfo.getEntryID()};
}
else
{
result.unlinkedFileLock = {&store, toFileInfo.getEntryID()};
result.fromFileLock = {&store, fromFileInfo.getEntryID()};
}
}
else
{
result.fromFileLock = {&store, fromFileInfo.getEntryID()};
}
}
return std::make_tuple(
std::move(fromDirLock), std::move(toDirLock),
std::move(fromNameLock), std::move(toNameLock),
std::move(fileLock));
return result;
}
bool RenameV2MsgEx::processIncoming(ResponseContext& ctx)
......
......@@ -8,16 +8,28 @@
#include <storage/DirEntry.h>
#include <storage/MetaStore.h>
class RenameV2MsgEx : public MirroredMessage<RenameMsg,
std::tuple<DirIDLock, DirIDLock, ParentNameLock, ParentNameLock, FileIDLock>>
struct RenameV2Locks
{
ParentNameLock fromNameLock;
ParentNameLock toNameLock;
DirIDLock fromDirLock;
DirIDLock toDirLock;
// source file must be locked because concurrent modifications of file attributes may
// race with the moving operation between two servers.
FileIDLock fromFileLock;
// if target exists, the target file must be unlocked to exclude concurrent operations on
// target (eg close, setxattr, ...)
FileIDLock unlinkedFileLock;
};
class RenameV2MsgEx : public MirroredMessage<RenameMsg, RenameV2Locks>
{
public:
typedef ErrorCodeResponseState<RenameRespMsg, NETMSGTYPE_Rename> ResponseState;
virtual bool processIncoming(ResponseContext& ctx) override;
std::tuple<DirIDLock, DirIDLock, ParentNameLock, ParentNameLock, FileIDLock> lock(
EntryLockStore& store) override;
RenameV2Locks lock(EntryLockStore& store) override;
bool isMirrored() override { return getFromDirInfo()->getIsBuddyMirrored(); }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment