diff --git a/Annex/Content.hs b/Annex/Content.hs index 50522a2098..3a4759a22b 100644 --- a/Annex/Content.hs +++ b/Annex/Content.hs @@ -30,6 +30,7 @@ module Annex.Content ( populatePointerFile, linkToAnnex, linkFromAnnex, + linkFromAnnex', LinkAnnexResult(..), unlinkAnnex, checkedCopyFile, @@ -78,6 +79,7 @@ import Annex.Link import Annex.LockPool import Annex.UUID import Annex.InodeSentinal +import Annex.ReplaceFile import Annex.AdjustedBranch (adjustedBranchRefresh) import Messages.Progress import Types.Remote (RetrievalSecurityPolicy(..)) @@ -382,9 +384,23 @@ linkToAnnex key src srcic = ifM (checkSecureHashes' key) , return LinkAnnexFailed ) -{- Makes a destination file be a link or copy from the annex object. -} +{- Makes a destination file be a link or copy from the annex object. + - + - linkAnnex stats the file after copying it to add to the inode + - cache. But dest may be a file in the working tree, which could + - get modified immediately after being populated. To avoid such a + - race, call linkAnnex on a temporary file and move it into place + - afterwards. Note that a consequence of this is that, if the file + - already exists, it will be overwritten. + -} linkFromAnnex :: Key -> RawFilePath -> Maybe FileMode -> Annex LinkAnnexResult -linkFromAnnex key dest destmode = do +linkFromAnnex key dest destmode = + replaceFile (const noop) (fromRawFilePath dest) $ \tmp -> + linkFromAnnex' key (toRawFilePath tmp) destmode + +{- This is only safe to use when dest is not a worktree file. -} +linkFromAnnex' :: Key -> RawFilePath -> Maybe FileMode -> Annex LinkAnnexResult +linkFromAnnex' key dest destmode = do src <- calcRepo (gitAnnexLocation key) srcic <- withTSDelta (liftIO . genInodeCache src) linkAnnex From key src srcic dest destmode diff --git a/Annex/WorkTree.hs b/Annex/WorkTree.hs index 9a3dd50777..60b460e813 100644 --- a/Annex/WorkTree.hs +++ b/Annex/WorkTree.hs @@ -131,7 +131,7 @@ scanAnnexedFiles initscan = do fileMode <$> R.getFileStatus f ic <- replaceWorkTreeFile (fromRawFilePath f) $ \tmp -> do let tmp' = toRawFilePath tmp - linkFromAnnex k tmp' destmode >>= \case + linkFromAnnex' k tmp' destmode >>= \case LinkAnnexOk -> withTSDelta (liftIO . genInodeCache tmp') LinkAnnexNoop -> return Nothing diff --git a/Command/Fix.hs b/Command/Fix.hs index 49e5ca9786..da2f5dd4ce 100644 --- a/Command/Fix.hs +++ b/Command/Fix.hs @@ -85,7 +85,7 @@ makeHardLink :: RawFilePath -> Key -> CommandPerform makeHardLink file key = do replaceWorkTreeFile (fromRawFilePath file) $ \tmp -> do mode <- liftIO $ catchMaybeIO $ fileMode <$> R.getFileStatus file - linkFromAnnex key (toRawFilePath tmp) mode >>= \case + linkFromAnnex' key (toRawFilePath tmp) mode >>= \case LinkAnnexFailed -> error "unable to make hard link" _ -> noop next $ return True diff --git a/Command/Fsck.hs b/Command/Fsck.hs index 7338a362e2..0d0cf11e05 100644 --- a/Command/Fsck.hs +++ b/Command/Fsck.hs @@ -357,7 +357,7 @@ verifyWorkTree key file = do let tmp' = toRawFilePath tmp mode <- liftIO $ catchMaybeIO $ fileMode <$> R.getFileStatus file ifM (annexThin <$> Annex.getGitConfig) - ( void $ linkFromAnnex key tmp' mode + ( void $ linkFromAnnex' key tmp' mode , do obj <- calcRepo (gitAnnexLocation key) void $ checkedCopyFile key obj tmp' mode diff --git a/Command/Unlock.hs b/Command/Unlock.hs index cfe49f19ee..0d3fedd4fb 100644 --- a/Command/Unlock.hs +++ b/Command/Unlock.hs @@ -50,7 +50,7 @@ perform dest key = do replaceWorkTreeFile (fromRawFilePath dest) $ \tmp -> ifM (inAnnex key) ( do - r <- linkFromAnnex key (toRawFilePath tmp) destmode + r <- linkFromAnnex' key (toRawFilePath tmp) destmode case r of LinkAnnexOk -> return () LinkAnnexNoop -> return ()