From aeb28400e9e814ead4e7be3da482f80e9f71c72a Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Mon, 29 Apr 2024 19:32:53 +0400 Subject: [PATCH] core: take entity lock before processing file (#4105) --- src/Simplex/Chat.hs | 18 +++++++++--- src/Simplex/Chat/Store/Files.hs | 44 ++++++++++++++++++++++-------- src/Simplex/Chat/Store/Messages.hs | 1 - 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 7b82d997cd..f8285770e0 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -3577,14 +3577,19 @@ processAgentMessageNoConn = \case processAgentMsgSndFile :: ACorrId -> SndFileId -> ACommand 'Agent 'AESndFile -> CM () processAgentMsgSndFile _corrId aFileId msg = do - fileId <- withStore (`getXFTPSndFileDBId` AgentSndFileId aFileId) - withFileLock "processAgentMsgSndFile" fileId $ + (cRef_, fileId) <- withStore (`getXFTPSndFileDBIds` AgentSndFileId aFileId) + withEntityLock_ cRef_ $ withFileLock "processAgentMsgSndFile" fileId $ withStore' (`getUserByASndFileId` AgentSndFileId aFileId) >>= \case Just user -> process user fileId `catchChatError` (toView . CRChatError (Just user)) _ -> do lift $ withAgent' (`xftpDeleteSndFileInternal` aFileId) throwChatError $ CENoSndFileUser $ AgentSndFileId aFileId where + withEntityLock_ :: Maybe ChatRef -> CM a -> CM a + withEntityLock_ cRef_ = case cRef_ of + Just (ChatRef CTDirect contactId) -> withContactLock "processAgentMsgSndFile" contactId + Just (ChatRef CTGroup groupId) -> withGroupLock "processAgentMsgSndFile" groupId + _ -> id process :: User -> FileTransferId -> CM () process user fileId = do (ft@FileTransferMeta {xftpRedirectFor, cancelled}, sfts) <- withStore $ \db -> getSndFileTransfer db user fileId @@ -3699,14 +3704,19 @@ splitFileDescr rfdText = do processAgentMsgRcvFile :: ACorrId -> RcvFileId -> ACommand 'Agent 'AERcvFile -> CM () processAgentMsgRcvFile _corrId aFileId msg = do - fileId <- withStore (`getXFTPRcvFileDBId` AgentRcvFileId aFileId) - withFileLock "processAgentMsgRcvFile" fileId $ + (cRef_, fileId) <- withStore (`getXFTPRcvFileDBIds` AgentRcvFileId aFileId) + withEntityLock_ cRef_ $ withFileLock "processAgentMsgRcvFile" fileId $ withStore' (`getUserByARcvFileId` AgentRcvFileId aFileId) >>= \case Just user -> process user fileId `catchChatError` (toView . CRChatError (Just user)) _ -> do lift $ withAgent' (`xftpDeleteRcvFile` aFileId) throwChatError $ CENoRcvFileUser $ AgentRcvFileId aFileId where + withEntityLock_ :: Maybe ChatRef -> CM a -> CM a + withEntityLock_ cRef_ = case cRef_ of + Just (ChatRef CTDirect contactId) -> withContactLock "processAgentMsgRcvFile" contactId + Just (ChatRef CTGroup groupId) -> withGroupLock "processAgentMsgRcvFile" groupId + _ -> id process :: User -> FileTransferId -> CM () process user fileId = do ft <- withStore $ \db -> getRcvFileTransfer db user fileId diff --git a/src/Simplex/Chat/Store/Files.hs b/src/Simplex/Chat/Store/Files.hs index 81a5897c89..528290aa59 100644 --- a/src/Simplex/Chat/Store/Files.hs +++ b/src/Simplex/Chat/Store/Files.hs @@ -29,8 +29,8 @@ module Simplex.Chat.Store.Files createExtraSndFTDescrs, updateSndFTDeliveryXFTP, setSndFTAgentDeleted, - getXFTPSndFileDBId, - getXFTPRcvFileDBId, + getXFTPSndFileDBIds, + getXFTPRcvFileDBIds, updateFileCancelled, updateCIFileStatus, getSharedMsgIdByFileId, @@ -109,7 +109,7 @@ import Simplex.Chat.Store.Shared import Simplex.Chat.Types import Simplex.Chat.Util (week) import Simplex.Messaging.Agent.Protocol (AgentMsgId, ConnId, UserId) -import Simplex.Messaging.Agent.Store.SQLite (firstRow, maybeFirstRow) +import Simplex.Messaging.Agent.Store.SQLite (firstRow, firstRow', maybeFirstRow) import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..)) @@ -336,15 +336,37 @@ setSndFTAgentDeleted db User {userId} fileId = do "UPDATE files SET agent_snd_file_deleted = 1, updated_at = ? WHERE user_id = ? AND file_id = ?" (currentTs, userId, fileId) -getXFTPSndFileDBId :: DB.Connection -> AgentSndFileId -> ExceptT StoreError IO FileTransferId -getXFTPSndFileDBId db aSndFileId = - ExceptT . firstRow fromOnly (SESndFileNotFoundXFTP aSndFileId) $ - DB.query db "SELECT file_id FROM files WHERE agent_snd_file_id = ?" (Only aSndFileId) +getXFTPSndFileDBIds :: DB.Connection -> AgentSndFileId -> ExceptT StoreError IO (Maybe ChatRef, FileTransferId) +getXFTPSndFileDBIds db aSndFileId = + ExceptT . firstRow' toFileRef (SESndFileNotFoundXFTP aSndFileId) $ + DB.query + db + [sql| + SELECT file_id, contact_id, group_id, note_folder_id + FROM files + WHERE agent_snd_file_id = ? + |] + (Only aSndFileId) -getXFTPRcvFileDBId :: DB.Connection -> AgentRcvFileId -> ExceptT StoreError IO FileTransferId -getXFTPRcvFileDBId db aRcvFileId = - ExceptT . firstRow fromOnly (SERcvFileNotFoundXFTP aRcvFileId) $ - DB.query db "SELECT file_id FROM rcv_files WHERE agent_rcv_file_id = ?" (Only aRcvFileId) +getXFTPRcvFileDBIds :: DB.Connection -> AgentRcvFileId -> ExceptT StoreError IO (Maybe ChatRef, FileTransferId) +getXFTPRcvFileDBIds db aRcvFileId = + ExceptT . firstRow' toFileRef (SERcvFileNotFoundXFTP aRcvFileId) $ + DB.query + db + [sql| + SELECT rf.file_id, f.contact_id, f.group_id, f.note_folder_id + FROM rcv_files rf + JOIN files f ON f.file_id = rf.file_id + WHERE rf.agent_rcv_file_id = ? + |] + (Only aRcvFileId) + +toFileRef :: (FileTransferId, Maybe Int64, Maybe Int64, Maybe Int64) -> Either StoreError (Maybe ChatRef, FileTransferId) +toFileRef = \case + (fileId, Just contactId, Nothing, Nothing) -> Right (Just $ ChatRef CTDirect contactId, fileId) + (fileId, Nothing, Just groupId, Nothing) -> Right (Just $ ChatRef CTGroup groupId, fileId) + (fileId, Nothing, Nothing, Just folderId) -> Right (Just $ ChatRef CTLocal folderId, fileId) + (fileId, _, _, _) -> Right (Nothing, fileId) updateFileCancelled :: MsgDirectionI d => DB.Connection -> User -> Int64 -> CIFileStatus d -> IO () updateFileCancelled db User {userId} fileId ciFileStatus = do diff --git a/src/Simplex/Chat/Store/Messages.hs b/src/Simplex/Chat/Store/Messages.hs index 0d6592a311..8163bd336c 100644 --- a/src/Simplex/Chat/Store/Messages.hs +++ b/src/Simplex/Chat/Store/Messages.hs @@ -146,7 +146,6 @@ import Simplex.Messaging.Agent.Store.SQLite (firstRow, firstRow', maybeFirstRow) import qualified Simplex.Messaging.Agent.Store.SQLite.DB as DB import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..)) -import Simplex.Messaging.Crypto.Ratchet (PQSupport) import Simplex.Messaging.Util (eitherToMaybe) import UnliftIO.STM