fix deadlock

Fix a deadlock that could occur after git-annex got an unlocked file,
causing the command to hang indefinitely.

Known to happen on vfat filesystems, possibly others.

Note that a deadlock is still theoretically possible, if anything
smudge --clean does causes it to run the git queue for some other
reason.

Apparently that doesn't happen, but will need to keep an eye on it.
This commit is contained in:
Joey Hess 2020-06-18 12:56:29 -04:00
parent 82381211fa
commit d5451afc8f
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
4 changed files with 36 additions and 15 deletions

View file

@ -151,6 +151,7 @@ data AnnexState = AnnexState
, cachedcurrentbranch :: (Maybe (Maybe Git.Branch, Maybe Adjustment)) , cachedcurrentbranch :: (Maybe (Maybe Git.Branch, Maybe Adjustment))
, cachedgitenv :: Maybe (AltIndexFile, FilePath, [(String, String)]) , cachedgitenv :: Maybe (AltIndexFile, FilePath, [(String, String)])
, urloptions :: Maybe UrlOptions , urloptions :: Maybe UrlOptions
, insmudgecleanfilter :: Bool
} }
newState :: GitConfig -> Git.Repo -> IO AnnexState newState :: GitConfig -> Git.Repo -> IO AnnexState
@ -209,6 +210,7 @@ newState c r = do
, cachedcurrentbranch = Nothing , cachedcurrentbranch = Nothing
, cachedgitenv = Nothing , cachedgitenv = Nothing
, urloptions = Nothing , urloptions = Nothing
, insmudgecleanfilter = False
} }
{- Makes an Annex state object for the specified git repo. {- Makes an Annex state object for the specified git repo.

View file

@ -177,12 +177,18 @@ newtype Restage = Restage Bool
restagePointerFile :: Restage -> RawFilePath -> InodeCache -> Annex () restagePointerFile :: Restage -> RawFilePath -> InodeCache -> Annex ()
restagePointerFile (Restage False) f _ = restagePointerFile (Restage False) f _ =
toplevelWarning True $ unableToRestage $ Just $ fromRawFilePath f toplevelWarning True $ unableToRestage $ Just $ fromRawFilePath f
restagePointerFile (Restage True) f orig = withTSDelta $ \tsd -> do restagePointerFile (Restage True) f orig = withTSDelta $ \tsd ->
-- update-index is documented as picky about "./file" and it -- Avoid refreshing the index if run by the
-- fails on "../../repo/path/file" when cwd is not in the repo -- smudge clean filter, because git uses that when
-- being acted on. Avoid these problems with an absolute path. -- it's already refreshing the index, probably because
absf <- liftIO $ absPath $ fromRawFilePath f -- this very action is running. Running it again would likely
Annex.Queue.addInternalAction runner [(absf, isunmodified tsd)] -- deadlock.
unlessM (Annex.getState Annex.insmudgecleanfilter) $ do
-- update-index is documented as picky about "./file" and it
-- fails on "../../repo/path/file" when cwd is not in the repo
-- being acted on. Avoid these problems with an absolute path.
absf <- liftIO $ absPath $ fromRawFilePath f
Annex.Queue.addInternalAction runner [(absf, isunmodified tsd)]
where where
isunmodified tsd = genInodeCache f tsd >>= return . \case isunmodified tsd = genInodeCache f tsd >>= return . \case
Nothing -> False Nothing -> False

View file

@ -1,3 +1,11 @@
git-annex (8.20200618) UNRELEASED; urgency=medium
* Fix a deadlock that could occur after git-annex got an unlocked
file, causing the command to hang indefinitely. Known to happen on
vfat filesystems, possibly others.
-- Joey Hess <id@joeyh.name> Thu, 18 Jun 2020 12:21:14 -0400
git-annex (8.20200617) upstream; urgency=medium git-annex (8.20200617) upstream; urgency=medium
* Added annex.skipunknown git config, that can be set to false to change * Added annex.skipunknown git config, that can be set to false to change

View file

@ -88,18 +88,23 @@ clean file = do
b <- liftIO $ L.hGetContents stdin b <- liftIO $ L.hGetContents stdin
ifM fileoutsiderepo ifM fileoutsiderepo
( liftIO $ L.hPut stdout b ( liftIO $ L.hPut stdout b
, case parseLinkTargetOrPointerLazy b of , do
Just k -> do -- Avoid a potential deadlock.
getMoveRaceRecovery k (toRawFilePath file) Annex.changeState $ \s -> s
liftIO $ L.hPut stdout b { Annex.insmudgecleanfilter = True }
Nothing -> do go b
let fileref = Git.Ref.fileRef (toRawFilePath file)
indexmeta <- catObjectMetaData fileref
go b indexmeta =<< catKey' fileref indexmeta
) )
stop stop
where where
go b indexmeta oldkey = ifM (shouldAnnex file indexmeta oldkey) go b = case parseLinkTargetOrPointerLazy b of
Just k -> do
getMoveRaceRecovery k (toRawFilePath file)
liftIO $ L.hPut stdout b
Nothing -> do
let fileref = Git.Ref.fileRef (toRawFilePath file)
indexmeta <- catObjectMetaData fileref
go' b indexmeta =<< catKey' fileref indexmeta
go' b indexmeta oldkey = ifM (shouldAnnex file indexmeta oldkey)
( do ( do
-- Before git 2.5, failing to consume all stdin here -- Before git 2.5, failing to consume all stdin here
-- would cause a SIGPIPE and crash it. -- would cause a SIGPIPE and crash it.