/** * Create a new file on this (remote) meta-server. This is the 'toFile' on a rename() client call. * * Note: Replaces existing entry. * * @param buf serialized inode object * @param outUnlinkedInode the unlinked (owned) file (in case a file was overwritten * by the move operation); the caller is responsible for the deletion of the local file and the * corresponding object; may not be NULL */ FhgfsOpsErr MetaStore::moveRemoteFileInsert(EntryInfo* fromFileInfo, std::string toParentID, std::string newEntryName, const char* buf, FileInode** outUnlinkedInode) { // note: we do not allow newEntry to be a file if the old entry was a directory (and vice versa) const char* logContext = "rename(): Insert remote entry"; FhgfsOpsErr retVal = FhgfsOpsErr_INTERNAL; *outUnlinkedInode = NULL; SafeRWLock safeMetaStoreLock(&rwlock, SafeRWLock_READ); // L O C K DirInode* toParent = referenceDirUnlocked(toParentID, true); if(!toParent) { retVal = FhgfsOpsErr_PATHNOTEXISTS; safeMetaStoreLock.unlock(); // U N L O C K return retVal; } // toParent exists SafeRWLock toParentMutexLock(&toParent->rwlock, SafeRWLock_WRITE); // L O C K ( T O ) DirEntry* overWrittenEntry = toParent->dirEntryCreateFromFileUnlocked(newEntryName); if (overWrittenEntry) { EntryInfo overWriteInfo; std::string parentID = overWrittenEntry->getID(); overWrittenEntry->getEntryInfo(parentID, 0, &overWriteInfo); bool isSameInode; FhgfsOpsErr checkRes = checkRenameOverwrite(fromFileInfo, &overWriteInfo, isSameInode); if ((checkRes != FhgfsOpsErr_SUCCESS) || ((checkRes == FhgfsOpsErr_SUCCESS) && isSameInode) ) { retVal = checkRes; goto outUnlock; } // only unlink the dir-entry-name here, so we can still restore it from dir-entry-id FhgfsOpsErr unlinkRes = toParent->unlinkDirEntryUnlocked(newEntryName, overWrittenEntry, DirEntry_UNLINK_FILENAME); if (unlikely(unlinkRes != FhgfsOpsErr_SUCCESS) ) { if (unlikely (unlinkRes == FhgfsOpsErr_PATHNOTEXISTS) ) LogContext(logContext).log(Log_WARNING, "Unexpectedly failed to unlink file: " + toParent->entries.getDirEntryPathUnlocked() + newEntryName + ". "); else { LogContext(logContext).logErr("Failed to unlink existing file. Aborting rename()."); retVal = unlinkRes; goto outUnlock; } } } { // create new dirEntry with inlined inode FileInode* inode = new FileInode(); // the deserialized inode bool deserializeRes = inode->deserializeMetaData(buf); if (deserializeRes == false) { LogContext("File rename").logErr("Bug: Deserialization of remote buffer failed. Are all " "meta servers running with the same version?" ); retVal = FhgfsOpsErr_INTERNAL; delete inode; goto outUnlock; } // destructs inode retVal = mkMetaFileUnlocked(toParent, newEntryName, fromFileInfo->getEntryType(), inode); } if (overWrittenEntry && retVal == FhgfsOpsErr_SUCCESS) { // unlink the overwritten entry, will unlock, release and return bool unlinkedWasInlined = overWrittenEntry->getIsInodeInlined(); FhgfsOpsErr unlinkRes = unlinkOverwrittenEntryUnlocked(toParent, overWrittenEntry, outUnlinkedInode); EntryInfo unlinkEntryInfo; overWrittenEntry->getEntryInfo(toParentID, 0, &unlinkEntryInfo); // unlock everything here, but do not release toParent yet. toParentMutexLock.unlock(); // U N L O C K ( T O ) safeMetaStoreLock.unlock(); // unlinkInodeLater() requires that everything was unlocked! if (unlinkRes == FhgfsOpsErr_INUSE) { unlinkRes = unlinkInodeLater(&unlinkEntryInfo, unlinkedWasInlined ); if (unlinkRes == FhgfsOpsErr_AGAIN) unlinkRes = unlinkOverwrittenEntry(toParent, overWrittenEntry, outUnlinkedInode); if (unlinkRes != FhgfsOpsErr_SUCCESS && unlinkRes != FhgfsOpsErr_PATHNOTEXISTS) LogContext(logContext).logErr("Failed to unlink overwritten entry:" " FileName: " + newEntryName + " ParentEntryID: " + toParent->getID() + " entryID: " + overWrittenEntry->getEntryID() + " Error: " + FhgfsOpsErrTk::toErrString(unlinkRes) ); } delete overWrittenEntry; releaseDir(toParentID); return retVal; } else if (overWrittenEntry) { // TODO: Restore the overwritten entry } outUnlock: toParentMutexLock.unlock(); // U N L O C K ( T O ) dirStore.releaseDir(toParent->getID() ); safeMetaStoreLock.unlock(); SAFE_DELETE(overWrittenEntry); return retVal; }
/** * Simple rename on the same server in the same directory. * * @param outUnlinkInode is the inode of a dirEntry being possibly overwritten (toName already * existed). */ FhgfsOpsErr MetaStore::renameInSameDir(DirInode* parentDir, std::string fromName, std::string toName, FileInode** outUnlinkInode) { const char* logContext = "Rename in dir"; SafeRWLock safeLock(&rwlock, SafeRWLock_READ); // L O C K SafeRWLock fromMutexLock(&parentDir->rwlock, SafeRWLock_WRITE); // L O C K ( F R O M ) FhgfsOpsErr retVal; FhgfsOpsErr unlinkRes; DirEntry* overWrittenEntry = NULL; retVal = performRenameEntryInSameDir(parentDir, fromName, toName, &overWrittenEntry); if (retVal != FhgfsOpsErr_SUCCESS) { fromMutexLock.unlock(); safeLock.unlock(); SAFE_DELETE(overWrittenEntry); return retVal; } EntryInfo unlinkEntryInfo; bool unlinkedWasInlined; if (overWrittenEntry) { std::string parentDirID = parentDir->getID(); overWrittenEntry->getEntryInfo(parentDirID, 0, &unlinkEntryInfo); unlinkedWasInlined = overWrittenEntry->getIsInodeInlined(); unlinkRes = unlinkOverwrittenEntryUnlocked(parentDir, overWrittenEntry, outUnlinkInode); } else { *outUnlinkInode = NULL; // irrelevant values, just to please the compiler unlinkRes = FhgfsOpsErr_SUCCESS; unlinkedWasInlined = true; } /* Now update the ctime (attribChangeTime) of the renamed entry. * Only do that for Directory dentry after giving up the DirInodes (fromMutex) lock * as dirStore.setAttr() will aquire the InodeDirStore:: lock * and the lock order is InodeDirStore:: and then DirInode:: (risk of deadlock) */ DirEntry* entry = parentDir->dirEntryCreateFromFileUnlocked(toName); if (likely(entry) ) // entry was just renamed to, so very likely it exists { EntryInfo entryInfo; std::string parentID = parentDir->getID(); entry->getEntryInfo(parentID, 0, &entryInfo); fromMutexLock.unlock(); setAttrUnlocked(&entryInfo, 0, NULL); /* This will fail if the DirInode is on another * meta server, but as updating the ctime is not * a real posix requirement (but filesystems usually * do it) we simply ignore this issue for now. */ SAFE_DELETE(entry); } else fromMutexLock.unlock(); safeLock.unlock(); // unlink later must be called after releasing all locks if (overWrittenEntry) { if (unlinkRes == FhgfsOpsErr_INUSE) { unlinkRes = unlinkInodeLater(&unlinkEntryInfo, unlinkedWasInlined ); if (unlinkRes == FhgfsOpsErr_AGAIN) { unlinkRes = unlinkOverwrittenEntry(parentDir, overWrittenEntry, outUnlinkInode); } } if (unlinkRes != FhgfsOpsErr_SUCCESS && unlinkRes != FhgfsOpsErr_PATHNOTEXISTS) { LogContext(logContext).logErr("Failed to unlink overwritten entry:" " FileName: " + toName + " ParentEntryID: " + parentDir->getID() + " entryID: " + overWrittenEntry->getEntryID() + " Error: " + FhgfsOpsErrTk::toErrString(unlinkRes) ); // TODO: Restore the dentry } } SAFE_DELETE(overWrittenEntry); return retVal; }