2013-10-26 20:54:49 +00:00
|
|
|
{- git-annex assistant repository repair
|
|
|
|
-
|
2015-01-21 16:50:09 +00:00
|
|
|
- Copyright 2013 Joey Hess <id@joeyh.name>
|
2013-10-26 20:54:49 +00:00
|
|
|
-
|
2016-11-22 03:46:59 +00:00
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
2013-10-26 20:54:49 +00:00
|
|
|
-}
|
|
|
|
|
2013-10-26 21:16:29 +00:00
|
|
|
{-# LANGUAGE CPP #-}
|
2013-10-26 20:54:49 +00:00
|
|
|
|
|
|
|
module Assistant.Repair where
|
|
|
|
|
|
|
|
import Assistant.Common
|
2013-12-10 20:17:49 +00:00
|
|
|
import Command.Repair (repairAnnexBranch, trackingOrSyncBranch)
|
2013-10-27 19:38:59 +00:00
|
|
|
import Git.Fsck (FsckResults, foundBroken)
|
2013-10-26 20:54:49 +00:00
|
|
|
import Git.Repair (runRepairOf)
|
2013-10-27 19:38:59 +00:00
|
|
|
import qualified Git
|
|
|
|
import qualified Remote
|
|
|
|
import qualified Types.Remote as Remote
|
2013-10-26 20:54:49 +00:00
|
|
|
import Logs.FsckResults
|
|
|
|
import Annex.UUID
|
|
|
|
import Utility.Batch
|
2015-02-28 21:23:13 +00:00
|
|
|
import Annex.Path
|
2013-10-26 20:54:49 +00:00
|
|
|
import Assistant.Sync
|
2013-10-26 21:16:29 +00:00
|
|
|
import Assistant.Alert
|
|
|
|
import Assistant.DaemonStatus
|
|
|
|
import Assistant.Types.UrlRenderer
|
|
|
|
#ifdef WITH_WEBAPP
|
|
|
|
import Assistant.WebApp.Types
|
2013-10-28 15:24:25 +00:00
|
|
|
import qualified Data.Text as T
|
2013-10-26 21:16:29 +00:00
|
|
|
#endif
|
2013-10-29 17:41:44 +00:00
|
|
|
import qualified Utility.Lsof as Lsof
|
|
|
|
import Utility.ThreadScheduler
|
2013-10-26 20:54:49 +00:00
|
|
|
|
|
|
|
import Control.Concurrent.Async
|
|
|
|
|
2013-10-27 19:38:59 +00:00
|
|
|
{- When the FsckResults require a repair, tries to do a non-destructive
|
|
|
|
- repair. If that fails, pops up an alert. -}
|
2013-10-27 20:42:13 +00:00
|
|
|
repairWhenNecessary :: UrlRenderer -> UUID -> Maybe Remote -> FsckResults -> Assistant Bool
|
2013-10-27 19:38:59 +00:00
|
|
|
repairWhenNecessary urlrenderer u mrmt fsckresults
|
|
|
|
| foundBroken fsckresults = do
|
|
|
|
liftAnnex $ writeFsckResults u fsckresults
|
|
|
|
repodesc <- liftAnnex $ Remote.prettyUUID u
|
2013-10-27 20:42:13 +00:00
|
|
|
ok <- alertWhile (repairingAlert repodesc)
|
2013-10-27 19:38:59 +00:00
|
|
|
(runRepair u mrmt False)
|
2013-10-26 21:16:29 +00:00
|
|
|
#ifdef WITH_WEBAPP
|
2013-10-27 20:42:13 +00:00
|
|
|
unless ok $ do
|
|
|
|
button <- mkAlertButton True (T.pack "Click Here") urlrenderer $
|
|
|
|
RepairRepositoryR u
|
2013-11-24 18:04:03 +00:00
|
|
|
void $ addAlert $ brokenRepositoryAlert [button]
|
2013-10-26 21:16:29 +00:00
|
|
|
#endif
|
2013-10-28 15:24:25 +00:00
|
|
|
return ok
|
2013-10-27 20:42:13 +00:00
|
|
|
| otherwise = return False
|
2013-10-26 21:16:29 +00:00
|
|
|
|
2013-10-27 19:38:59 +00:00
|
|
|
runRepair :: UUID -> Maybe Remote -> Bool -> Assistant Bool
|
|
|
|
runRepair u mrmt destructiverepair = do
|
2013-10-26 20:54:49 +00:00
|
|
|
fsckresults <- liftAnnex $ readFsckResults u
|
|
|
|
myu <- liftAnnex getUUID
|
2013-10-26 21:16:29 +00:00
|
|
|
ok <- if u == myu
|
2013-10-26 20:54:49 +00:00
|
|
|
then localrepair fsckresults
|
|
|
|
else remoterepair fsckresults
|
2013-11-30 18:29:11 +00:00
|
|
|
liftAnnex $ clearFsckResults u
|
2013-10-27 19:38:59 +00:00
|
|
|
debug [ "Repaired", show u, show ok ]
|
2013-10-26 21:16:29 +00:00
|
|
|
|
|
|
|
return ok
|
2013-10-26 20:54:49 +00:00
|
|
|
where
|
2014-10-09 18:53:13 +00:00
|
|
|
localrepair fsckresults = do
|
2013-10-27 19:38:59 +00:00
|
|
|
-- Stop the watcher from running while running repairs.
|
|
|
|
changeSyncable Nothing False
|
|
|
|
|
2013-10-26 20:54:49 +00:00
|
|
|
-- This intentionally runs the repair inside the Annex
|
|
|
|
-- monad, which is not strictly necessary, but keeps
|
|
|
|
-- other threads that might be trying to use the Annex
|
|
|
|
-- from running until it completes.
|
2013-10-27 19:38:59 +00:00
|
|
|
ok <- liftAnnex $ repair fsckresults Nothing
|
|
|
|
|
2013-10-26 21:16:29 +00:00
|
|
|
-- Run a background fast fsck if a destructive repair had
|
|
|
|
-- to be done, to ensure that the git-annex branch
|
|
|
|
-- reflects the current state of the repo.
|
2013-10-27 19:38:59 +00:00
|
|
|
when destructiverepair $
|
2013-10-26 20:54:49 +00:00
|
|
|
backgroundfsck [ Param "--fast" ]
|
2013-10-27 19:38:59 +00:00
|
|
|
|
|
|
|
-- Start the watcher running again. This also triggers it to
|
|
|
|
-- do a startup scan, which is especially important if the
|
|
|
|
-- git repo repair removed files from the index file. Those
|
|
|
|
-- files will be seen as new, and re-added to the repository.
|
|
|
|
when (ok || destructiverepair) $
|
|
|
|
changeSyncable Nothing True
|
|
|
|
|
2013-10-26 21:16:29 +00:00
|
|
|
return ok
|
2013-10-26 20:54:49 +00:00
|
|
|
|
2013-10-27 19:38:59 +00:00
|
|
|
remoterepair fsckresults = case Remote.repairRepo =<< mrmt of
|
|
|
|
Nothing -> return False
|
|
|
|
Just mkrepair -> do
|
|
|
|
thisrepopath <- liftIO . absPath
|
|
|
|
=<< liftAnnex (fromRepo Git.repoPath)
|
|
|
|
a <- liftAnnex $ mkrepair $
|
|
|
|
repair fsckresults (Just thisrepopath)
|
|
|
|
liftIO $ catchBoolIO a
|
|
|
|
|
|
|
|
repair fsckresults referencerepo = do
|
2013-12-10 19:40:01 +00:00
|
|
|
(ok, modifiedbranches) <- inRepo $
|
2013-12-10 20:17:49 +00:00
|
|
|
runRepairOf fsckresults trackingOrSyncBranch destructiverepair referencerepo
|
2013-10-27 19:38:59 +00:00
|
|
|
when destructiverepair $
|
2013-12-10 19:40:01 +00:00
|
|
|
repairAnnexBranch modifiedbranches
|
2013-10-27 19:38:59 +00:00
|
|
|
return ok
|
2013-10-26 20:54:49 +00:00
|
|
|
|
|
|
|
backgroundfsck params = liftIO $ void $ async $ do
|
2015-02-28 21:23:13 +00:00
|
|
|
program <- programPath
|
2013-10-26 20:54:49 +00:00
|
|
|
batchCommand program (Param "fsck" : params)
|
2013-10-29 17:41:44 +00:00
|
|
|
|
|
|
|
{- Detect when a git lock file exists and has no git process currently
|
|
|
|
- writing to it. This strongly suggests it is a stale lock file.
|
|
|
|
-
|
|
|
|
- However, this could be on a network filesystem. Which is not very safe
|
|
|
|
- anyway (the assistant relies on being able to check when files have
|
2014-01-02 02:05:33 +00:00
|
|
|
- no writers to know when to commit them). Also, a few lock-file-ish
|
|
|
|
- things used by git are not kept open, particularly MERGE_HEAD.
|
|
|
|
-
|
|
|
|
- So, just in case, when the lock file appears stale, we delay for one
|
|
|
|
- minute, and check its size. If the size changed, delay for another
|
|
|
|
- minute, and so on. This will at work to detect when another machine
|
|
|
|
- is writing out a new index file, since git does so by writing the
|
|
|
|
- new content to index.lock.
|
2013-10-29 18:22:56 +00:00
|
|
|
-
|
|
|
|
- Returns true if locks were cleaned up.
|
2013-10-29 17:41:44 +00:00
|
|
|
-}
|
2013-10-29 18:22:56 +00:00
|
|
|
repairStaleGitLocks :: Git.Repo -> Assistant Bool
|
2013-10-29 17:52:19 +00:00
|
|
|
repairStaleGitLocks r = do
|
2014-01-02 02:05:33 +00:00
|
|
|
lockfiles <- liftIO $ filter islock <$> findgitfiles r
|
2013-10-29 17:52:19 +00:00
|
|
|
repairStaleLocks lockfiles
|
2013-10-29 18:22:56 +00:00
|
|
|
return $ not $ null lockfiles
|
2013-10-29 17:52:19 +00:00
|
|
|
where
|
2013-12-18 19:05:29 +00:00
|
|
|
findgitfiles = dirContentsRecursiveSkipping (== dropTrailingPathSeparator annexDir) True . Git.localGitDir
|
2014-01-02 02:05:33 +00:00
|
|
|
islock f
|
|
|
|
| "gc.pid" `isInfixOf` f = False
|
|
|
|
| ".lock" `isSuffixOf` f = True
|
|
|
|
| takeFileName f == "MERGE_HEAD" = True
|
|
|
|
| otherwise = False
|
|
|
|
|
2013-10-29 17:52:19 +00:00
|
|
|
repairStaleLocks :: [FilePath] -> Assistant ()
|
|
|
|
repairStaleLocks lockfiles = go =<< getsizes
|
2013-10-29 17:41:44 +00:00
|
|
|
where
|
2015-01-20 20:58:48 +00:00
|
|
|
getsize lf = catchMaybeIO $ (\s -> (lf, s)) <$> getFileSize lf
|
2014-10-09 18:53:13 +00:00
|
|
|
getsizes = liftIO $ catMaybes <$> mapM getsize lockfiles
|
2013-10-29 17:41:44 +00:00
|
|
|
go [] = return ()
|
|
|
|
go l = ifM (liftIO $ null <$> Lsof.query ("--" : map fst l))
|
|
|
|
( do
|
|
|
|
waitforit "to check stale git lock file"
|
|
|
|
l' <- getsizes
|
|
|
|
if l' == l
|
|
|
|
then liftIO $ mapM_ nukeFile (map fst l)
|
|
|
|
else go l'
|
|
|
|
, do
|
|
|
|
waitforit "for git lock file writer"
|
|
|
|
go =<< getsizes
|
|
|
|
)
|
|
|
|
waitforit why = do
|
|
|
|
notice ["Waiting for 60 seconds", why]
|
|
|
|
liftIO $ threadDelaySeconds $ Seconds 60
|