populatePointerFile,
linkToAnnex,
linkFromAnnex,
+ linkFromAnnex',
LinkAnnexResult(..),
unlinkAnnex,
checkedCopyFile,
import Annex.LockPool
import Annex.UUID
import Annex.InodeSentinal
+import Annex.ReplaceFile
import Annex.AdjustedBranch (adjustedBranchRefresh)
import Messages.Progress
import Types.Remote (RetrievalSecurityPolicy(..))
, 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
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
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
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