#include #include #include #include #include #include #include #include "MsgHelperUnlink.h" /** * Wrapper for unlinkMetaFile() and unlinkChunkFiles(). * * @param msgUserID only used in msg header info. */ FhgfsOpsErr MsgHelperUnlink::unlinkFile(DirInode& parentDir, const std::string& removeName, unsigned msgUserID) { std::unique_ptr unlinkedInode; unsigned numHardlinks; // Not used here! FhgfsOpsErr unlinkMetaRes = unlinkMetaFile(parentDir, removeName, &unlinkedInode, numHardlinks); /* note: if the file is still opened or if there are/were hardlinks then unlinkedInode will be NULL even on FhgfsOpsErr_SUCCESS */ if (unlinkMetaRes == FhgfsOpsErr_SUCCESS && unlinkedInode) unlinkMetaRes = unlinkChunkFiles(unlinkedInode.release(), msgUserID); return unlinkMetaRes; } /** * Unlink file in metadata store. * * @param outInitialHardlinkCount will be set to the initial hardlink count of the file * inode before unlinking. * * @return if this returns success and outUnlinkedFile is set, then the caller also needs to unlink * the chunk files via unlinkChunkFiles(). */ FhgfsOpsErr MsgHelperUnlink::unlinkMetaFile(DirInode& parentDir, const std::string& removeName, std::unique_ptr* outUnlinkedFile, unsigned& outInitialHardlinkCount) { MetaStore* metaStore = Program::getApp()->getMetaStore(); ModificationEventFlusher* modEventFlusher = Program::getApp()->getModificationEventFlusher(); bool modEventLoggingEnabled = modEventFlusher->isLoggingEnabled(); EntryInfo entryInfo; FhgfsOpsErr unlinkMetaRes = metaStore->unlinkFile(parentDir, removeName, &entryInfo, outUnlinkedFile, outInitialHardlinkCount); if (modEventLoggingEnabled) { std::string entryID = entryInfo.getEntryID(); modEventFlusher->add(ModificationEvent_FILEREMOVED, entryID); } return unlinkMetaRes; } /** * Decrement hardlink count and * Unlink file's inode if hardlink count reaches zero * * @return Success if hardlink count decrement is successful * and if it became zero then fileinode removal is also sucessful (outUnlinkInode will be * set in this case which will be later used to remove chunk files). If file is in use and * its last entry getting removed then inode will be linked with disposal directory for later * removal * */ FhgfsOpsErr MsgHelperUnlink::unlinkFileInode(EntryInfo* delFileInfo, std::unique_ptr* outUnlinkedFile, unsigned& outInitialHardlinkCount) { MetaStore* metaStore = Program::getApp()->getMetaStore(); FhgfsOpsErr unlinkRes = metaStore->unlinkFileInode(delFileInfo, outUnlinkedFile, outInitialHardlinkCount); return unlinkRes; } /** * Unlink (storage) chunk files. * * Note: If chunk files unlink fails, this method will create a disposal entry. * * @param unlinkedInode will be deleted inside this method or owned by another object, so caller * may no longer access it after calling this. * @param msgUserID only used in msg header info. */ FhgfsOpsErr MsgHelperUnlink::unlinkChunkFiles(FileInode* unlinkedInode, unsigned msgUserID) { const char* logContext = "Delete chunk files"; MetaStore* metaStore = Program::getApp()->getMetaStore(); FhgfsOpsErr retVal; retVal = unlinkChunkFilesInternal(*unlinkedInode, msgUserID); if(retVal != FhgfsOpsErr_SUCCESS) { /* Failed to unlink storage chunk files => add file to the disposable store to try * again later. */ LogContext(logContext).logErr("Failed to delete all chunk files of ID: " + unlinkedInode->getEntryID() + ". Added disposal entry."); retVal = metaStore->insertDisposableFile(unlinkedInode); // destructs unlinkedInode } else { // success (local files unlinked) delete(unlinkedInode); } return retVal; } /** * Wrapper to decide parallel or sequential chunks unlink. * * @param msgUserID only used in msg header info. */ FhgfsOpsErr MsgHelperUnlink::unlinkChunkFilesInternal(FileInode& file, unsigned msgUserID) { StripePattern* pattern = file.getStripePattern(); if( (pattern->getStripeTargetIDs()->size() > 1) || (pattern->getPatternType() == StripePatternType_BuddyMirror) ) return unlinkChunkFileParallel(file, msgUserID); else return unlinkChunkFileSequential(file, msgUserID); } /** * Note: This method does not work for mirrored files; use unlinkChunkFileParallel() for those. * * @param msgUserID only used in msg header info. */ FhgfsOpsErr MsgHelperUnlink::unlinkChunkFileSequential(FileInode& inode, unsigned msgUserID) { std::string logContext("Unlink Helper (unlink chunk file S [" + inode.getEntryID() + "])"); StripePattern* pattern = inode.getStripePattern(); const UInt16Vector* targetIDs = pattern->getStripeTargetIDs(); TargetMapper* targetMapper = Program::getApp()->getTargetMapper(); TargetStateStore* targetStates = Program::getApp()->getTargetStateStore(); NodeStore* nodes = Program::getApp()->getStorageNodes(); FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS; std::string fileID(inode.getEntryID()); PathInfo pathInfo; inode.getPathInfo(&pathInfo); // send request to each node and receive the response message for(UInt16VectorConstIter iter = targetIDs->begin(); iter != targetIDs->end(); iter++) { uint16_t targetID = *iter; UnlinkLocalFileMsg unlinkMsg(fileID, targetID, &pathInfo); unlinkMsg.setMsgHeaderUserID(msgUserID); RequestResponseArgs rrArgs(NULL, &unlinkMsg, NETMSGTYPE_UnlinkLocalFileResp); RequestResponseTarget rrTarget(targetID, targetMapper, nodes); rrTarget.setTargetStates(targetStates); // send request to node and receive response FhgfsOpsErr requestRes = MessagingTk::requestResponseTarget(&rrTarget, &rrArgs); if(requestRes != FhgfsOpsErr_SUCCESS) { // communication failed if( (requestRes == FhgfsOpsErr_UNKNOWNNODE) || (requestRes == FhgfsOpsErr_UNKNOWNTARGET) ) { /* special case: for unlink, we don't treat this as error to allow easy deletion of files after intentional target removal. */ LogContext(logContext).log(Log_WARNING, "Unable to resolve storage node targetID: " + StringTk::uintToStr(targetID) ); continue; } LogContext(logContext).log(Log_WARNING, "Communication with storage target failed: " + StringTk::uintToStr(targetID) + "; " "fileID: " + inode.getEntryID()); if(retVal == FhgfsOpsErr_SUCCESS) retVal = requestRes; continue; } // correct response type received UnlinkLocalFileRespMsg* unlinkRespMsg = (UnlinkLocalFileRespMsg*)rrArgs.outRespMsg.get(); FhgfsOpsErr unlinkResult = unlinkRespMsg->getResult(); if(unlinkResult != FhgfsOpsErr_SUCCESS) { // error: local inode not unlinked LogContext(logContext).log(Log_WARNING, "Storage target failed to unlink chunk file: " + StringTk::uintToStr(targetID) + "; " "fileID: " + inode.getEntryID()); if(retVal == FhgfsOpsErr_SUCCESS) retVal = unlinkResult; continue; } // success: local inode unlinked LOG_DEBUG(logContext, Log_DEBUG, "Storage targed unlinked chunk file: " + StringTk::uintToStr(targetID) + "; " "fileID: " + inode.getEntryID()); } if(unlikely(retVal != FhgfsOpsErr_SUCCESS) ) LogContext(logContext).log(Log_WARNING, "Problems occurred during unlinking of the chunk files. " "fileID: " + inode.getEntryID()); return retVal; } /** * @param msgUserID only used in msg header info. */ FhgfsOpsErr MsgHelperUnlink::unlinkChunkFileParallel(FileInode& inode, unsigned msgUserID) { std::string logContext("Unlink Helper (unlink chunk file [" + inode.getEntryID() + "])"); App* app = Program::getApp(); MultiWorkQueue* slaveQ = app->getCommSlaveQueue(); StripePattern* pattern = inode.getStripePattern(); const UInt16Vector* targetIDs = pattern->getStripeTargetIDs(); size_t numTargetWorks = targetIDs->size(); FhgfsOpsErr retVal = FhgfsOpsErr_SUCCESS; FhgfsOpsErrVec nodeResults(numTargetWorks); SynchronizedCounter counter; PathInfo pathInfo; inode.getPathInfo(&pathInfo); // generate work for storage targets... for(size_t i=0; i < numTargetWorks; i++) { UnlinkChunkFileWork* work = new UnlinkChunkFileWork(inode.getEntryID(), pattern, (*targetIDs)[i], &pathInfo, &(nodeResults[i]), &counter); work->setMsgUserID(msgUserID); slaveQ->addDirectWork(work); } // wait for work completion... counter.waitForCount(numTargetWorks); // check target results... for(size_t i=0; i < numTargetWorks; i++) { if(unlikely(nodeResults[i] != FhgfsOpsErr_SUCCESS) ) { if( (nodeResults[i] == FhgfsOpsErr_UNKNOWNNODE) || (nodeResults[i] == FhgfsOpsErr_UNKNOWNTARGET) ) { /* we don't return this as an error to the user, because the node/target was probably removed intentionally (and either way the rest of this inode is lost now) */ continue; } LogContext(logContext).log(Log_WARNING, "Problems occurred during unlinking of chunk files."); retVal = nodeResults[i]; goto error_exit; } } error_exit: return retVal; }