git-annex/Command/ReKey.hs
Joey Hess 3c7fd09ec8
get many more commands building again
about half are building now
2019-12-05 11:40:10 -04:00

129 lines
4.2 KiB
Haskell

{- git-annex command
-
- Copyright 2012-2016 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
module Command.ReKey where
import Command
import qualified Annex
import Annex.Content
import Annex.Ingest
import Annex.Link
import Annex.Perms
import Annex.ReplaceFile
import Logs.Location
import Git.FilePath
import qualified Database.Keys
import Annex.InodeSentinal
import Utility.InodeCache
cmd :: Command
cmd = command "rekey" SectionPlumbing
"change keys used for files"
(paramRepeating $ paramPair paramPath paramKey)
(seek <$$> optParser)
data ReKeyOptions = ReKeyOptions
{ reKeyThese :: CmdParams
, batchOption :: BatchMode
}
optParser :: CmdParamsDesc -> Parser ReKeyOptions
optParser desc = ReKeyOptions
<$> cmdParams desc
<*> parseBatchOption
-- Split on the last space, since a FilePath can contain whitespace,
-- but a Key very rarely does.
batchParser :: String -> Either String (RawFilePath, Key)
batchParser s = case separate (== ' ') (reverse s) of
(rk, rf)
| null rk || null rf -> Left "Expected: \"file key\""
| otherwise -> case deserializeKey (reverse rk) of
Nothing -> Left "bad key"
Just k -> Right (toRawFilePath (reverse rf), k)
seek :: ReKeyOptions -> CommandSeek
seek o = case batchOption o of
Batch fmt -> batchInput fmt batchParser (batchCommandAction . start)
NoBatch -> withPairs (commandAction . start . parsekey) (reKeyThese o)
where
parsekey (file, skey) =
(toRawFilePath file, fromMaybe (giveup "bad key") (deserializeKey skey))
start :: (RawFilePath, Key) -> CommandStart
start (file, newkey) = ifAnnexed file go stop
where
go oldkey
| oldkey == newkey = stop
| otherwise = starting "rekey" (ActionItemWorkTreeFile file) $
perform file oldkey newkey
perform :: RawFilePath -> Key -> Key -> CommandPerform
perform file oldkey newkey = do
ifM (inAnnex oldkey)
( unlessM (linkKey file oldkey newkey) $
giveup "failed creating link from old to new key"
, unlessM (Annex.getState Annex.force) $
giveup $ fromRawFilePath file ++ " is not available (use --force to override)"
)
next $ cleanup file oldkey newkey
{- Make a hard link to the old key content (when supported),
- to avoid wasting disk space. -}
linkKey :: RawFilePath -> Key -> Key -> Annex Bool
linkKey file oldkey newkey = ifM (isJust <$> isAnnexLink file)
{- If the object file is already hardlinked to elsewhere, a hard
- link won't be made by getViaTmpFromDisk, but a copy instead.
- This avoids hard linking to content linked to an
- unlocked file, which would leave the new key unlocked
- and vulnerable to corruption. -}
( getViaTmpFromDisk RetrievalAllKeysSecure DefaultVerify newkey $ \tmp -> unVerified $ do
oldobj <- calcRepo (gitAnnexLocation oldkey)
isJust <$> linkOrCopy' (return True) newkey oldobj tmp Nothing
, do
{- The file being rekeyed is itself an unlocked file; if
- it's hard linked to the old key, that link must be broken. -}
oldobj <- calcRepo (gitAnnexLocation oldkey)
v <- tryNonAsync $ do
st <- liftIO $ getFileStatus (fromRawFilePath file)
when (linkCount st > 1) $ do
freezeContent oldobj
replaceFile (fromRawFilePath file) $ \tmp -> do
unlessM (checkedCopyFile oldkey oldobj tmp Nothing) $
error "can't lock old key"
thawContent tmp
ic <- withTSDelta (liftIO . genInodeCache (fromRawFilePath file))
case v of
Left e -> do
warning (show e)
return False
Right () -> do
r <- linkToAnnex newkey (fromRawFilePath file) ic
return $ case r of
LinkAnnexFailed -> False
LinkAnnexOk -> True
LinkAnnexNoop -> True
)
cleanup :: RawFilePath -> Key -> Key -> CommandCleanup
cleanup file oldkey newkey = do
ifM (isJust <$> isAnnexLink file)
( do
-- Update symlink to use the new key.
liftIO $ removeFile (fromRawFilePath file)
addLink (fromRawFilePath file) newkey Nothing
, do
mode <- liftIO $ catchMaybeIO $ fileMode <$> getFileStatus (fromRawFilePath file)
liftIO $ whenM (isJust <$> isPointerFile file) $
writePointerFile file newkey mode
stagePointerFile file mode =<< hashPointerFile newkey
Database.Keys.removeAssociatedFile oldkey
=<< inRepo (toTopFilePath (fromRawFilePath file))
)
whenM (inAnnex newkey) $
logStatus newkey InfoPresent
return True