make linkAnnex detect when the file changes as it's being copied/linked in
This fixes a race where the modified file ended up in annex/objects, and the InodeCache stored in the database was for the modified version, so git-annex didn't know it had gotten modified. The race could occur when the smudge filter was running; now it gets the InodeCache before generating the Key, which avoids the race.
This commit is contained in:
parent
8e9608d7f0
commit
4392140946
4 changed files with 39 additions and 17 deletions
|
@ -521,30 +521,43 @@ populatePointerFile k obj f = go =<< isPointerFile f
|
||||||
- prevent losing the content if the source file is deleted, but does not
|
- prevent losing the content if the source file is deleted, but does not
|
||||||
- guard against modifications.
|
- guard against modifications.
|
||||||
-}
|
-}
|
||||||
linkAnnex :: Key -> FilePath -> Annex LinkAnnexResult
|
linkAnnex :: Key -> FilePath -> Maybe InodeCache -> Annex LinkAnnexResult
|
||||||
linkAnnex key src = do
|
linkAnnex key src srcic = do
|
||||||
dest <- calcRepo (gitAnnexLocation key)
|
dest <- calcRepo (gitAnnexLocation key)
|
||||||
modifyContent dest $ linkAnnex' key src dest
|
modifyContent dest $ linkAnnex' key src srcic dest
|
||||||
|
|
||||||
{- Hard links (or copies) src to dest, one of which should be the
|
{- Hard links (or copies) src to dest, one of which should be the
|
||||||
- annex object. Updates inode cache for src and for dest when it's
|
- annex object. Updates inode cache for src and for dest when it's
|
||||||
- changed. -}
|
- changed. -}
|
||||||
linkAnnex' :: Key -> FilePath -> FilePath -> Annex LinkAnnexResult
|
linkAnnex' :: Key -> FilePath -> Maybe InodeCache -> FilePath -> Annex LinkAnnexResult
|
||||||
linkAnnex' key src dest =
|
linkAnnex' _ _ Nothing _ = return LinkAnnexFailed
|
||||||
|
linkAnnex' key src (Just srcic) dest =
|
||||||
ifM (liftIO $ doesFileExist dest)
|
ifM (liftIO $ doesFileExist dest)
|
||||||
( do
|
( do
|
||||||
Database.Keys.storeInodeCaches key [src]
|
Database.Keys.addInodeCaches key [srcic]
|
||||||
return LinkAnnexNoop
|
return LinkAnnexNoop
|
||||||
, ifM (linkAnnex'' key src dest)
|
, ifM (linkAnnex'' key src dest)
|
||||||
( do
|
( do
|
||||||
thawContent dest
|
thawContent dest
|
||||||
Database.Keys.storeInodeCaches key [dest, src]
|
-- src could have changed while being copied
|
||||||
return LinkAnnexOk
|
-- to dest
|
||||||
, do
|
mcache <- withTSDelta (liftIO . genInodeCache src)
|
||||||
Database.Keys.storeInodeCaches key [src]
|
case mcache of
|
||||||
return LinkAnnexFailed
|
Just srcic' | compareStrong srcic srcic' -> do
|
||||||
|
destic <- withTSDelta (liftIO . genInodeCache dest)
|
||||||
|
Database.Keys.addInodeCaches key $
|
||||||
|
catMaybes [destic, Just srcic]
|
||||||
|
return LinkAnnexOk
|
||||||
|
_ -> do
|
||||||
|
liftIO $ nukeFile dest
|
||||||
|
failed
|
||||||
|
, failed
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
where
|
||||||
|
failed = do
|
||||||
|
Database.Keys.addInodeCaches key [srcic]
|
||||||
|
return LinkAnnexFailed
|
||||||
|
|
||||||
data LinkAnnexResult = LinkAnnexOk | LinkAnnexFailed | LinkAnnexNoop
|
data LinkAnnexResult = LinkAnnexOk | LinkAnnexFailed | LinkAnnexNoop
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ import Annex.Link
|
||||||
import Annex.MetaData
|
import Annex.MetaData
|
||||||
import Annex.FileMatcher
|
import Annex.FileMatcher
|
||||||
import Annex.InodeSentinal
|
import Annex.InodeSentinal
|
||||||
|
import Utility.InodeCache
|
||||||
import Types.KeySource
|
import Types.KeySource
|
||||||
import Backend
|
import Backend
|
||||||
import Logs.Location
|
import Logs.Location
|
||||||
|
@ -86,7 +87,7 @@ clean file = do
|
||||||
|
|
||||||
-- If the file being cleaned was hard linked to the old key's annex object,
|
-- If the file being cleaned was hard linked to the old key's annex object,
|
||||||
-- modifying the file will have caused the object to have the wrong content.
|
-- modifying the file will have caused the object to have the wrong content.
|
||||||
-- Clean up from that, making the
|
-- Clean up from that.
|
||||||
cleanOldKey :: FilePath -> Key -> Annex ()
|
cleanOldKey :: FilePath -> Key -> Annex ()
|
||||||
cleanOldKey modifiedfile key = do
|
cleanOldKey modifiedfile key = do
|
||||||
obj <- calcRepo (gitAnnexLocation key)
|
obj <- calcRepo (gitAnnexLocation key)
|
||||||
|
@ -99,7 +100,9 @@ cleanOldKey modifiedfile key = do
|
||||||
case fs' of
|
case fs' of
|
||||||
-- If linkAnnex fails, the file with the content
|
-- If linkAnnex fails, the file with the content
|
||||||
-- is still present, so no need for any recovery.
|
-- is still present, so no need for any recovery.
|
||||||
(f:_) -> void $ linkAnnex key f
|
(f:_) -> do
|
||||||
|
ic <- withTSDelta (liftIO . genInodeCache f)
|
||||||
|
void $ linkAnnex key f ic
|
||||||
_ -> lostcontent
|
_ -> lostcontent
|
||||||
where
|
where
|
||||||
lostcontent = logStatus key InfoMissing
|
lostcontent = logStatus key InfoMissing
|
||||||
|
@ -112,17 +115,18 @@ shouldAnnex file = do
|
||||||
ingest :: FilePath -> Annex Key
|
ingest :: FilePath -> Annex Key
|
||||||
ingest file = do
|
ingest file = do
|
||||||
backend <- chooseBackend file
|
backend <- chooseBackend file
|
||||||
|
ic <- withTSDelta (liftIO . genInodeCache file)
|
||||||
let source = KeySource
|
let source = KeySource
|
||||||
{ keyFilename = file
|
{ keyFilename = file
|
||||||
, contentLocation = file
|
, contentLocation = file
|
||||||
, inodeCache = Nothing
|
, inodeCache = ic
|
||||||
}
|
}
|
||||||
k <- fst . fromMaybe (error "failed to generate a key")
|
k <- fst . fromMaybe (error "failed to generate a key")
|
||||||
<$> genKey source backend
|
<$> genKey source backend
|
||||||
-- Hard link (or copy) file content to annex object
|
-- Hard link (or copy) file content to annex object
|
||||||
-- to prevent it from being lost when git checks out
|
-- to prevent it from being lost when git checks out
|
||||||
-- a branch not containing this file.
|
-- a branch not containing this file.
|
||||||
r <- linkAnnex k file
|
r <- linkAnnex k file ic
|
||||||
case r of
|
case r of
|
||||||
LinkAnnexFailed -> error "Problem adding file to the annex"
|
LinkAnnexFailed -> error "Problem adding file to the annex"
|
||||||
LinkAnnexOk -> logStatus k InfoPresent
|
LinkAnnexOk -> logStatus k InfoPresent
|
||||||
|
|
|
@ -14,6 +14,8 @@ import Annex.CatFile
|
||||||
import Annex.Version
|
import Annex.Version
|
||||||
import Annex.Link
|
import Annex.Link
|
||||||
import Annex.ReplaceFile
|
import Annex.ReplaceFile
|
||||||
|
import Annex.InodeSentinal
|
||||||
|
import Utility.InodeCache
|
||||||
import Utility.CopyFile
|
import Utility.CopyFile
|
||||||
|
|
||||||
cmd :: Command
|
cmd :: Command
|
||||||
|
@ -51,8 +53,9 @@ start file key = ifM (isJust <$> isAnnexLink file)
|
||||||
performNew :: FilePath -> Key -> CommandPerform
|
performNew :: FilePath -> Key -> CommandPerform
|
||||||
performNew dest key = do
|
performNew dest key = do
|
||||||
src <- calcRepo (gitAnnexLocation key)
|
src <- calcRepo (gitAnnexLocation key)
|
||||||
|
srcic <- withTSDelta (liftIO . genInodeCache src)
|
||||||
replaceFile dest $ \tmp -> do
|
replaceFile dest $ \tmp -> do
|
||||||
r <- linkAnnex' key src tmp
|
r <- linkAnnex' key src srcic tmp
|
||||||
case r of
|
case r of
|
||||||
LinkAnnexOk -> return ()
|
LinkAnnexOk -> return ()
|
||||||
_ -> error "linkAnnex failed"
|
_ -> error "linkAnnex failed"
|
||||||
|
|
|
@ -20,6 +20,7 @@ import qualified Git
|
||||||
import qualified Git.LsFiles
|
import qualified Git.LsFiles
|
||||||
import qualified Git.Branch
|
import qualified Git.Branch
|
||||||
import Git.FileMode
|
import Git.FileMode
|
||||||
|
import Utility.InodeCache
|
||||||
|
|
||||||
upgrade :: Bool -> Annex Bool
|
upgrade :: Bool -> Annex Bool
|
||||||
upgrade automatic = do
|
upgrade automatic = do
|
||||||
|
@ -88,7 +89,8 @@ upgradeDirectWorkTree = do
|
||||||
-- not populated with it. Since the work tree file
|
-- not populated with it. Since the work tree file
|
||||||
-- is recorded as an associated file, things will still
|
-- is recorded as an associated file, things will still
|
||||||
-- work that way, it's just not ideal.
|
-- work that way, it's just not ideal.
|
||||||
void $ linkAnnex k f
|
ic <- withTSDelta (liftIO . genInodeCache f)
|
||||||
|
void $ linkAnnex k f ic
|
||||||
writepointer f k = liftIO $ do
|
writepointer f k = liftIO $ do
|
||||||
nukeFile f
|
nukeFile f
|
||||||
writeFile f (formatPointer k)
|
writeFile f (formatPointer k)
|
||||||
|
|
Loading…
Reference in a new issue