where indenting
This commit is contained in:
parent
6a0756d2fb
commit
2172cc586e
42 changed files with 1193 additions and 1209 deletions
|
@ -57,24 +57,23 @@ shaN shasize file filesize = do
|
||||||
Left sha -> liftIO $ sha <$> L.readFile file
|
Left sha -> liftIO $ sha <$> L.readFile file
|
||||||
Right command -> liftIO $ parse command . lines <$>
|
Right command -> liftIO $ parse command . lines <$>
|
||||||
readsha command (toCommand [File file])
|
readsha command (toCommand [File file])
|
||||||
where
|
where
|
||||||
parse command [] = bad command
|
parse command [] = bad command
|
||||||
parse command (l:_)
|
parse command (l:_)
|
||||||
| null sha = bad command
|
| null sha = bad command
|
||||||
| otherwise = sha
|
| otherwise = sha
|
||||||
where
|
where
|
||||||
sha = fst $ separate (== ' ') l
|
sha = fst $ separate (== ' ') l
|
||||||
bad command = error $ command ++ " parse error"
|
bad command = error $ command ++ " parse error"
|
||||||
{- sha commands output the filename, so need to set fileEncoding -}
|
{- sha commands output the filename, so need to set fileEncoding -}
|
||||||
readsha command args =
|
readsha command args =
|
||||||
withHandle StdoutHandle createProcessSuccess p $ \h -> do
|
withHandle StdoutHandle createProcessSuccess p $ \h -> do
|
||||||
fileEncoding h
|
fileEncoding h
|
||||||
output <- hGetContentsStrict h
|
output <- hGetContentsStrict h
|
||||||
hClose h
|
hClose h
|
||||||
return output
|
return output
|
||||||
where
|
where
|
||||||
p = (proc command args)
|
p = (proc command args) { std_out = CreatePipe }
|
||||||
{ std_out = CreatePipe }
|
|
||||||
|
|
||||||
shaCommand :: SHASize -> Integer -> Either (L.ByteString -> String) String
|
shaCommand :: SHASize -> Integer -> Either (L.ByteString -> String) String
|
||||||
shaCommand shasize filesize
|
shaCommand shasize filesize
|
||||||
|
@ -84,14 +83,14 @@ shaCommand shasize filesize
|
||||||
| shasize == 384 = use SysConfig.sha384 sha384
|
| shasize == 384 = use SysConfig.sha384 sha384
|
||||||
| shasize == 512 = use SysConfig.sha512 sha512
|
| shasize == 512 = use SysConfig.sha512 sha512
|
||||||
| otherwise = error $ "bad sha size " ++ show shasize
|
| otherwise = error $ "bad sha size " ++ show shasize
|
||||||
where
|
where
|
||||||
use Nothing sha = Left $ showDigest . sha
|
use Nothing sha = Left $ showDigest . sha
|
||||||
use (Just c) sha
|
use (Just c) sha
|
||||||
-- use builtin, but slower sha for small files
|
{- use builtin, but slower sha for small files
|
||||||
-- benchmarking indicates it's faster up to
|
- benchmarking indicates it's faster up to
|
||||||
-- and slightly beyond 50 kb files
|
- and slightly beyond 50 kb files -}
|
||||||
| filesize < 51200 = use Nothing sha
|
| filesize < 51200 = use Nothing sha
|
||||||
| otherwise = Right c
|
| otherwise = Right c
|
||||||
|
|
||||||
{- A key is a checksum of its contents. -}
|
{- A key is a checksum of its contents. -}
|
||||||
keyValue :: SHASize -> KeySource -> Annex (Maybe Key)
|
keyValue :: SHASize -> KeySource -> Annex (Maybe Key)
|
||||||
|
@ -109,23 +108,23 @@ keyValue shasize source = do
|
||||||
{- Extension preserving keys. -}
|
{- Extension preserving keys. -}
|
||||||
keyValueE :: SHASize -> KeySource -> Annex (Maybe Key)
|
keyValueE :: SHASize -> KeySource -> Annex (Maybe Key)
|
||||||
keyValueE size source = keyValue size source >>= maybe (return Nothing) addE
|
keyValueE size source = keyValue size source >>= maybe (return Nothing) addE
|
||||||
where
|
where
|
||||||
addE k = return $ Just $ k
|
addE k = return $ Just $ k
|
||||||
{ keyName = keyName k ++ selectExtension (keyFilename source)
|
{ keyName = keyName k ++ selectExtension (keyFilename source)
|
||||||
, keyBackendName = shaNameE size
|
, keyBackendName = shaNameE size
|
||||||
}
|
}
|
||||||
|
|
||||||
selectExtension :: FilePath -> String
|
selectExtension :: FilePath -> String
|
||||||
selectExtension f
|
selectExtension f
|
||||||
| null es = ""
|
| null es = ""
|
||||||
| otherwise = join "." ("":es)
|
| otherwise = join "." ("":es)
|
||||||
where
|
where
|
||||||
es = filter (not . null) $ reverse $
|
es = filter (not . null) $ reverse $
|
||||||
take 2 $ takeWhile shortenough $
|
take 2 $ takeWhile shortenough $
|
||||||
reverse $ split "." $ takeExtensions f
|
reverse $ split "." $ takeExtensions f
|
||||||
shortenough e
|
shortenough e
|
||||||
| '\n' `elem` e = False -- newline in extension?!
|
| '\n' `elem` e = False -- newline in extension?!
|
||||||
| otherwise = length e <= 4 -- long enough for "jpeg"
|
| otherwise = length e <= 4 -- long enough for "jpeg"
|
||||||
|
|
||||||
{- A key's checksum is checked during fsck. -}
|
{- A key's checksum is checked during fsck. -}
|
||||||
checkKeyChecksum :: SHASize -> Key -> FilePath -> Annex Bool
|
checkKeyChecksum :: SHASize -> Key -> FilePath -> Annex Bool
|
||||||
|
@ -137,7 +136,7 @@ checkKeyChecksum size key file = do
|
||||||
let filesize = fromIntegral $ fileSize stat
|
let filesize = fromIntegral $ fileSize stat
|
||||||
check <$> shaN size file filesize
|
check <$> shaN size file filesize
|
||||||
_ -> return True
|
_ -> return True
|
||||||
where
|
where
|
||||||
check s
|
check s
|
||||||
| s == dropExtensions (keyName key) = True
|
| s == dropExtensions (keyName key) = True
|
||||||
| otherwise = False
|
| otherwise = False
|
||||||
|
|
|
@ -32,10 +32,10 @@ fromUrl url size = stubKey
|
||||||
, keyBackendName = "URL"
|
, keyBackendName = "URL"
|
||||||
, keySize = size
|
, keySize = size
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
-- when it's not too long, use the url as the key name
|
{- when it's not too long, use the url as the key name
|
||||||
-- 256 is the absolute filename max, but use a shorter
|
- 256 is the absolute filename max, but use a shorter
|
||||||
-- length because this is not the entire key filename.
|
- length because this is not the entire key filename. -}
|
||||||
key
|
key
|
||||||
| length url < 128 = url
|
| length url < 128 = url
|
||||||
| otherwise = take 128 url ++ "-" ++ md5s (Str url)
|
| otherwise = take 128 url ++ "-" ++ md5s (Str url)
|
||||||
|
|
|
@ -45,19 +45,18 @@ tests =
|
||||||
- known-good hashes. -}
|
- known-good hashes. -}
|
||||||
shaTestCases :: [(Int, String)] -> [TestCase]
|
shaTestCases :: [(Int, String)] -> [TestCase]
|
||||||
shaTestCases l = map make l
|
shaTestCases l = map make l
|
||||||
where
|
where
|
||||||
make (n, knowngood) =
|
make (n, knowngood) = TestCase key $ maybeSelectCmd key $
|
||||||
TestCase key $ maybeSelectCmd key $
|
zip (shacmds n) (repeat check)
|
||||||
zip (shacmds n) (repeat check)
|
where
|
||||||
where
|
key = "sha" ++ show n
|
||||||
key = "sha" ++ show n
|
check = "</dev/null | grep -q '" ++ knowngood ++ "'"
|
||||||
check = "</dev/null | grep -q '" ++ knowngood ++ "'"
|
shacmds n = concatMap (\x -> [x, 'g':x, osxpath </> x]) $
|
||||||
shacmds n = concatMap (\x -> [x, 'g':x, osxpath </> x]) $
|
map (\x -> "sha" ++ show n ++ x) ["sum", ""]
|
||||||
map (\x -> "sha" ++ show n ++ x) ["sum", ""]
|
{- Max OSX sometimes puts GNU tools outside PATH, so look in
|
||||||
{- Max OSX sometimes puts GNU tools outside PATH, so look in
|
- the location it uses, and remember where to run them
|
||||||
- the location it uses, and remember where to run them
|
- from. -}
|
||||||
- from. -}
|
osxpath = "/opt/local/libexec/gnubin"
|
||||||
osxpath = "/opt/local/libexec/gnubin"
|
|
||||||
|
|
||||||
tmpDir :: String
|
tmpDir :: String
|
||||||
tmpDir = "tmp"
|
tmpDir = "tmp"
|
||||||
|
@ -67,9 +66,9 @@ testFile = tmpDir ++ "/testfile"
|
||||||
|
|
||||||
testCp :: ConfigKey -> String -> TestCase
|
testCp :: ConfigKey -> String -> TestCase
|
||||||
testCp k option = TestCase cmd $ testCmd k cmdline
|
testCp k option = TestCase cmd $ testCmd k cmdline
|
||||||
where
|
where
|
||||||
cmd = "cp " ++ option
|
cmd = "cp " ++ option
|
||||||
cmdline = cmd ++ " " ++ testFile ++ " " ++ testFile ++ ".new"
|
cmdline = cmd ++ " " ++ testFile ++ " " ++ testFile ++ ".new"
|
||||||
|
|
||||||
{- Pulls package version out of the changelog. -}
|
{- Pulls package version out of the changelog. -}
|
||||||
getVersion :: Test
|
getVersion :: Test
|
||||||
|
@ -82,8 +81,8 @@ getVersionString = do
|
||||||
changelog <- readFile "CHANGELOG"
|
changelog <- readFile "CHANGELOG"
|
||||||
let verline = head $ lines changelog
|
let verline = head $ lines changelog
|
||||||
return $ middle (words verline !! 1)
|
return $ middle (words verline !! 1)
|
||||||
where
|
where
|
||||||
middle = drop 1 . init
|
middle = drop 1 . init
|
||||||
|
|
||||||
getGitVersion :: Test
|
getGitVersion :: Test
|
||||||
getGitVersion = do
|
getGitVersion = do
|
||||||
|
@ -104,14 +103,14 @@ cabalSetup = do
|
||||||
map (setfield "Version" version) $
|
map (setfield "Version" version) $
|
||||||
lines cabal
|
lines cabal
|
||||||
renameFile tmpcabalfile cabalfile
|
renameFile tmpcabalfile cabalfile
|
||||||
where
|
where
|
||||||
cabalfile = "git-annex.cabal"
|
cabalfile = "git-annex.cabal"
|
||||||
tmpcabalfile = cabalfile++".tmp"
|
tmpcabalfile = cabalfile++".tmp"
|
||||||
setfield field value s
|
setfield field value s
|
||||||
| fullfield `isPrefixOf` s = fullfield ++ value
|
| fullfield `isPrefixOf` s = fullfield ++ value
|
||||||
| otherwise = s
|
| otherwise = s
|
||||||
where
|
where
|
||||||
fullfield = field ++ ": "
|
fullfield = field ++ ": "
|
||||||
|
|
||||||
setup :: IO ()
|
setup :: IO ()
|
||||||
setup = do
|
setup = do
|
||||||
|
|
|
@ -46,11 +46,11 @@ autostart command = genDesktopEntry
|
||||||
|
|
||||||
systemwideInstall :: IO Bool
|
systemwideInstall :: IO Bool
|
||||||
systemwideInstall = isroot <||> destdirset
|
systemwideInstall = isroot <||> destdirset
|
||||||
where
|
where
|
||||||
isroot = do
|
isroot = do
|
||||||
uid <- fromIntegral <$> getRealUserID
|
uid <- fromIntegral <$> getRealUserID
|
||||||
return $ uid == (0 :: Int)
|
return $ uid == (0 :: Int)
|
||||||
destdirset = isJust <$> catchMaybeIO (getEnv "DESTDIR")
|
destdirset = isJust <$> catchMaybeIO (getEnv "DESTDIR")
|
||||||
|
|
||||||
inDestDir :: FilePath -> IO FilePath
|
inDestDir :: FilePath -> IO FilePath
|
||||||
inDestDir f = do
|
inDestDir f = do
|
||||||
|
@ -91,6 +91,6 @@ install command = do
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = getArgs >>= go
|
main = getArgs >>= go
|
||||||
where
|
where
|
||||||
go [] = error "specify git-annex command"
|
go [] = error "specify git-annex command"
|
||||||
go (command:_) = install command
|
go (command:_) = install command
|
||||||
|
|
|
@ -29,22 +29,22 @@ instance Show Config where
|
||||||
[ key ++ " :: " ++ valuetype value
|
[ key ++ " :: " ++ valuetype value
|
||||||
, key ++ " = " ++ show value
|
, key ++ " = " ++ show value
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
valuetype (BoolConfig _) = "Bool"
|
valuetype (BoolConfig _) = "Bool"
|
||||||
valuetype (StringConfig _) = "String"
|
valuetype (StringConfig _) = "String"
|
||||||
valuetype (MaybeStringConfig _) = "Maybe String"
|
valuetype (MaybeStringConfig _) = "Maybe String"
|
||||||
valuetype (MaybeBoolConfig _) = "Maybe Bool"
|
valuetype (MaybeBoolConfig _) = "Maybe Bool"
|
||||||
|
|
||||||
writeSysConfig :: [Config] -> IO ()
|
writeSysConfig :: [Config] -> IO ()
|
||||||
writeSysConfig config = writeFile "Build/SysConfig.hs" body
|
writeSysConfig config = writeFile "Build/SysConfig.hs" body
|
||||||
where
|
where
|
||||||
body = unlines $ header ++ map show config ++ footer
|
body = unlines $ header ++ map show config ++ footer
|
||||||
header = [
|
header = [
|
||||||
"{- Automatically generated. -}"
|
"{- Automatically generated. -}"
|
||||||
, "module Build.SysConfig where"
|
, "module Build.SysConfig where"
|
||||||
, ""
|
, ""
|
||||||
]
|
]
|
||||||
footer = []
|
footer = []
|
||||||
|
|
||||||
runTests :: [TestCase] -> IO [Config]
|
runTests :: [TestCase] -> IO [Config]
|
||||||
runTests [] = return []
|
runTests [] = return []
|
||||||
|
@ -60,12 +60,12 @@ requireCmd :: ConfigKey -> String -> Test
|
||||||
requireCmd k cmdline = do
|
requireCmd k cmdline = do
|
||||||
ret <- testCmd k cmdline
|
ret <- testCmd k cmdline
|
||||||
handle ret
|
handle ret
|
||||||
where
|
where
|
||||||
handle r@(Config _ (BoolConfig True)) = return r
|
handle r@(Config _ (BoolConfig True)) = return r
|
||||||
handle r = do
|
handle r = do
|
||||||
testEnd r
|
testEnd r
|
||||||
error $ "** the " ++ c ++ " command is required"
|
error $ "** the " ++ c ++ " command is required"
|
||||||
c = head $ words cmdline
|
c = head $ words cmdline
|
||||||
|
|
||||||
{- Checks if a command is available by running a command line. -}
|
{- Checks if a command is available by running a command line. -}
|
||||||
testCmd :: ConfigKey -> String -> Test
|
testCmd :: ConfigKey -> String -> Test
|
||||||
|
@ -90,13 +90,13 @@ maybeSelectCmd k = searchCmd
|
||||||
|
|
||||||
searchCmd :: (String -> Test) -> ([String] -> Test) -> [(String, String)] -> Test
|
searchCmd :: (String -> Test) -> ([String] -> Test) -> [(String, String)] -> Test
|
||||||
searchCmd success failure cmdsparams = search cmdsparams
|
searchCmd success failure cmdsparams = search cmdsparams
|
||||||
where
|
where
|
||||||
search [] = failure $ fst $ unzip cmdsparams
|
search [] = failure $ fst $ unzip cmdsparams
|
||||||
search ((c, params):cs) = do
|
search ((c, params):cs) = do
|
||||||
ret <- system $ quiet $ c ++ " " ++ params
|
ret <- system $ quiet $ c ++ " " ++ params
|
||||||
if ret == ExitSuccess
|
if ret == ExitSuccess
|
||||||
then success c
|
then success c
|
||||||
else search cs
|
else search cs
|
||||||
|
|
||||||
quiet :: String -> String
|
quiet :: String -> String
|
||||||
quiet s = s ++ " >/dev/null 2>&1"
|
quiet s = s ++ " >/dev/null 2>&1"
|
||||||
|
|
64
CmdLine.hs
64
CmdLine.hs
|
@ -44,13 +44,13 @@ dispatch fuzzyok allargs allcmds commonoptions fields header getgitrepo = do
|
||||||
sequence_ flags
|
sequence_ flags
|
||||||
prepCommand cmd params
|
prepCommand cmd params
|
||||||
tryRun state' cmd $ [startup] ++ actions ++ [shutdown $ cmdnocommit cmd]
|
tryRun state' cmd $ [startup] ++ actions ++ [shutdown $ cmdnocommit cmd]
|
||||||
where
|
where
|
||||||
err msg = msg ++ "\n\n" ++ usage header allcmds commonoptions
|
err msg = msg ++ "\n\n" ++ usage header allcmds commonoptions
|
||||||
cmd = Prelude.head cmds
|
cmd = Prelude.head cmds
|
||||||
(fuzzy, cmds, name, args) = findCmd fuzzyok allargs allcmds err
|
(fuzzy, cmds, name, args) = findCmd fuzzyok allargs allcmds err
|
||||||
(flags, params) = getOptCmd args cmd commonoptions err
|
(flags, params) = getOptCmd args cmd commonoptions err
|
||||||
checkfuzzy = when fuzzy $
|
checkfuzzy = when fuzzy $
|
||||||
inRepo $ Git.AutoCorrect.prepare name cmdname cmds
|
inRepo $ Git.AutoCorrect.prepare name cmdname cmds
|
||||||
|
|
||||||
{- Parses command line params far enough to find the Command to run, and
|
{- Parses command line params far enough to find the Command to run, and
|
||||||
- returns the remaining params.
|
- returns the remaining params.
|
||||||
|
@ -61,25 +61,25 @@ findCmd fuzzyok argv cmds err
|
||||||
| not (null exactcmds) = (False, exactcmds, fromJust name, args)
|
| not (null exactcmds) = (False, exactcmds, fromJust name, args)
|
||||||
| fuzzyok && not (null inexactcmds) = (True, inexactcmds, fromJust name, args)
|
| fuzzyok && not (null inexactcmds) = (True, inexactcmds, fromJust name, args)
|
||||||
| otherwise = error $ err $ "unknown command " ++ fromJust name
|
| otherwise = error $ err $ "unknown command " ++ fromJust name
|
||||||
where
|
where
|
||||||
(name, args) = findname argv []
|
(name, args) = findname argv []
|
||||||
findname [] c = (Nothing, reverse c)
|
findname [] c = (Nothing, reverse c)
|
||||||
findname (a:as) c
|
findname (a:as) c
|
||||||
| "-" `isPrefixOf` a = findname as (a:c)
|
| "-" `isPrefixOf` a = findname as (a:c)
|
||||||
| otherwise = (Just a, reverse c ++ as)
|
| otherwise = (Just a, reverse c ++ as)
|
||||||
exactcmds = filter (\c -> name == Just (cmdname c)) cmds
|
exactcmds = filter (\c -> name == Just (cmdname c)) cmds
|
||||||
inexactcmds = case name of
|
inexactcmds = case name of
|
||||||
Nothing -> []
|
Nothing -> []
|
||||||
Just n -> Git.AutoCorrect.fuzzymatches n cmdname cmds
|
Just n -> Git.AutoCorrect.fuzzymatches n cmdname cmds
|
||||||
|
|
||||||
{- Parses command line options, and returns actions to run to configure flags
|
{- Parses command line options, and returns actions to run to configure flags
|
||||||
- and the remaining parameters for the command. -}
|
- and the remaining parameters for the command. -}
|
||||||
getOptCmd :: Params -> Command -> [Option] -> (String -> String) -> (Flags, Params)
|
getOptCmd :: Params -> Command -> [Option] -> (String -> String) -> (Flags, Params)
|
||||||
getOptCmd argv cmd commonoptions err = check $
|
getOptCmd argv cmd commonoptions err = check $
|
||||||
getOpt Permute (commonoptions ++ cmdoptions cmd) argv
|
getOpt Permute (commonoptions ++ cmdoptions cmd) argv
|
||||||
where
|
where
|
||||||
check (flags, rest, []) = (flags, rest)
|
check (flags, rest, []) = (flags, rest)
|
||||||
check (_, _, errs) = error $ err $ concat errs
|
check (_, _, errs) = error $ err $ concat errs
|
||||||
|
|
||||||
{- Runs a list of Annex actions. Catches IO errors and continues
|
{- Runs a list of Annex actions. Catches IO errors and continues
|
||||||
- (but explicitly thrown errors terminate the whole command).
|
- (but explicitly thrown errors terminate the whole command).
|
||||||
|
@ -93,18 +93,18 @@ tryRun' errnum _ cmd []
|
||||||
tryRun' errnum state cmd (a:as) = do
|
tryRun' errnum state cmd (a:as) = do
|
||||||
r <- run
|
r <- run
|
||||||
handle $! r
|
handle $! r
|
||||||
where
|
where
|
||||||
run = tryIO $ Annex.run state $ do
|
run = tryIO $ Annex.run state $ do
|
||||||
Annex.Queue.flushWhenFull
|
Annex.Queue.flushWhenFull
|
||||||
a
|
a
|
||||||
handle (Left err) = showerr err >> cont False state
|
handle (Left err) = showerr err >> cont False state
|
||||||
handle (Right (success, state')) = cont success state'
|
handle (Right (success, state')) = cont success state'
|
||||||
cont success s = do
|
cont success s = do
|
||||||
let errnum' = if success then errnum else errnum + 1
|
let errnum' = if success then errnum else errnum + 1
|
||||||
(tryRun' $! errnum') s cmd as
|
(tryRun' $! errnum') s cmd as
|
||||||
showerr err = Annex.eval state $ do
|
showerr err = Annex.eval state $ do
|
||||||
showErr err
|
showErr err
|
||||||
showEndFail
|
showEndFail
|
||||||
|
|
||||||
{- Actions to perform each time ran. -}
|
{- Actions to perform each time ran. -}
|
||||||
startup :: Annex Bool
|
startup :: Annex Bool
|
||||||
|
|
46
Command.hs
46
Command.hs
|
@ -80,14 +80,14 @@ prepCommand Command { cmdseek = seek, cmdcheck = c } params = do
|
||||||
{- Runs a command through the start, perform and cleanup stages -}
|
{- Runs a command through the start, perform and cleanup stages -}
|
||||||
doCommand :: CommandStart -> CommandCleanup
|
doCommand :: CommandStart -> CommandCleanup
|
||||||
doCommand = start
|
doCommand = start
|
||||||
where
|
where
|
||||||
start = stage $ maybe skip perform
|
start = stage $ maybe skip perform
|
||||||
perform = stage $ maybe failure cleanup
|
perform = stage $ maybe failure cleanup
|
||||||
cleanup = stage $ status
|
cleanup = stage $ status
|
||||||
stage = (=<<)
|
stage = (=<<)
|
||||||
skip = return True
|
skip = return True
|
||||||
failure = showEndFail >> return False
|
failure = showEndFail >> return False
|
||||||
status r = showEndResult r >> return r
|
status r = showEndResult r >> return r
|
||||||
|
|
||||||
{- Modifies an action to only act on files that are already annexed,
|
{- Modifies an action to only act on files that are already annexed,
|
||||||
- and passes the key and backend on to it. -}
|
- and passes the key and backend on to it. -}
|
||||||
|
@ -118,26 +118,26 @@ numCopies file = readish <$> checkAttr "annex.numcopies" file
|
||||||
-}
|
-}
|
||||||
autoCopies :: FilePath -> Key -> (Int -> Int -> Bool) -> CommandStart -> CommandStart
|
autoCopies :: FilePath -> Key -> (Int -> Int -> Bool) -> CommandStart -> CommandStart
|
||||||
autoCopies file key vs a = Annex.getState Annex.auto >>= go
|
autoCopies file key vs a = Annex.getState Annex.auto >>= go
|
||||||
where
|
where
|
||||||
go False = a
|
go False = a
|
||||||
go True = do
|
go True = do
|
||||||
numcopiesattr <- numCopies file
|
numcopiesattr <- numCopies file
|
||||||
needed <- getNumCopies numcopiesattr
|
needed <- getNumCopies numcopiesattr
|
||||||
(_, have) <- trustPartition UnTrusted =<< Remote.keyLocations key
|
have <- trustExclude UnTrusted =<< Remote.keyLocations key
|
||||||
if length have `vs` needed then a else stop
|
if length have `vs` needed then a else stop
|
||||||
|
|
||||||
autoCopiesWith :: FilePath -> Key -> (Int -> Int -> Bool) -> (Maybe Int -> CommandStart) -> CommandStart
|
autoCopiesWith :: FilePath -> Key -> (Int -> Int -> Bool) -> (Maybe Int -> CommandStart) -> CommandStart
|
||||||
autoCopiesWith file key vs a = do
|
autoCopiesWith file key vs a = do
|
||||||
numcopiesattr <- numCopies file
|
numcopiesattr <- numCopies file
|
||||||
Annex.getState Annex.auto >>= auto numcopiesattr
|
Annex.getState Annex.auto >>= auto numcopiesattr
|
||||||
where
|
where
|
||||||
auto numcopiesattr False = a numcopiesattr
|
auto numcopiesattr False = a numcopiesattr
|
||||||
auto numcopiesattr True = do
|
auto numcopiesattr True = do
|
||||||
needed <- getNumCopies numcopiesattr
|
needed <- getNumCopies numcopiesattr
|
||||||
(_, have) <- trustPartition UnTrusted =<< Remote.keyLocations key
|
have <- trustExclude UnTrusted =<< Remote.keyLocations key
|
||||||
if length have `vs` needed
|
if length have `vs` needed
|
||||||
then a numcopiesattr
|
then a numcopiesattr
|
||||||
else stop
|
else stop
|
||||||
|
|
||||||
checkAuto :: Annex Bool -> Annex Bool
|
checkAuto :: Annex Bool -> Annex Bool
|
||||||
checkAuto checker = ifM (Annex.getState Annex.auto)
|
checkAuto checker = ifM (Annex.getState Annex.auto)
|
||||||
|
|
8
Git.hs
8
Git.hs
|
@ -81,8 +81,8 @@ repoIsSsh Repo { location = Url url }
|
||||||
| scheme == "git+ssh:" = True
|
| scheme == "git+ssh:" = True
|
||||||
| scheme == "ssh+git:" = True
|
| scheme == "ssh+git:" = True
|
||||||
| otherwise = False
|
| otherwise = False
|
||||||
where
|
where
|
||||||
scheme = uriScheme url
|
scheme = uriScheme url
|
||||||
repoIsSsh _ = False
|
repoIsSsh _ = False
|
||||||
|
|
||||||
repoIsHttp :: Repo -> Bool
|
repoIsHttp :: Repo -> Bool
|
||||||
|
@ -126,5 +126,5 @@ hookPath script repo = do
|
||||||
let hook = localGitDir repo </> "hooks" </> script
|
let hook = localGitDir repo </> "hooks" </> script
|
||||||
ifM (catchBoolIO $ isexecutable hook)
|
ifM (catchBoolIO $ isexecutable hook)
|
||||||
( return $ Just hook , return Nothing )
|
( return $ Just hook , return Nothing )
|
||||||
where
|
where
|
||||||
isexecutable f = isExecutable . fileMode <$> getFileStatus f
|
isexecutable f = isExecutable . fileMode <$> getFileStatus f
|
||||||
|
|
13
GitAnnex.hs
13
GitAnnex.hs
|
@ -165,12 +165,13 @@ options = Option.common ++
|
||||||
, Option ['T'] ["time-limit"] (ReqArg Limit.addTimeLimit paramTime)
|
, Option ['T'] ["time-limit"] (ReqArg Limit.addTimeLimit paramTime)
|
||||||
"stop after the specified amount of time"
|
"stop after the specified amount of time"
|
||||||
] ++ Option.matcher
|
] ++ Option.matcher
|
||||||
where
|
where
|
||||||
setnumcopies v = Annex.changeState $ \s -> s { Annex.forcenumcopies = readish v }
|
setnumcopies v = Annex.changeState $
|
||||||
setgitconfig :: String -> Annex ()
|
\s -> s { Annex.forcenumcopies = readish v }
|
||||||
setgitconfig v = do
|
setgitconfig :: String -> Annex ()
|
||||||
newg <- inRepo $ Git.Config.store v
|
setgitconfig v = do
|
||||||
Annex.changeState $ \s -> s { Annex.repo = newg }
|
newg <- inRepo $ Git.Config.store v
|
||||||
|
Annex.changeState $ \s -> s { Annex.repo = newg }
|
||||||
|
|
||||||
header :: String
|
header :: String
|
||||||
header = "Usage: git-annex command [option ..]"
|
header = "Usage: git-annex command [option ..]"
|
||||||
|
|
|
@ -44,24 +44,22 @@ cmds_notreadonly = concat
|
||||||
|
|
||||||
cmds :: [Command]
|
cmds :: [Command]
|
||||||
cmds = map adddirparam $ cmds_readonly ++ cmds_notreadonly
|
cmds = map adddirparam $ cmds_readonly ++ cmds_notreadonly
|
||||||
where
|
where
|
||||||
adddirparam c = c
|
adddirparam c = c { cmdparamdesc = "DIRECTORY " ++ cmdparamdesc c }
|
||||||
{ cmdparamdesc = "DIRECTORY " ++ cmdparamdesc c
|
|
||||||
}
|
|
||||||
|
|
||||||
options :: [OptDescr (Annex ())]
|
options :: [OptDescr (Annex ())]
|
||||||
options = Option.common ++
|
options = Option.common ++
|
||||||
[ Option [] ["uuid"] (ReqArg checkuuid paramUUID) "local repository uuid"
|
[ Option [] ["uuid"] (ReqArg checkuuid paramUUID) "local repository uuid"
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
checkuuid expected = getUUID >>= check
|
checkuuid expected = getUUID >>= check
|
||||||
where
|
where
|
||||||
check u | u == toUUID expected = noop
|
check u | u == toUUID expected = noop
|
||||||
check NoUUID = unexpected "uninitialized repository"
|
check NoUUID = unexpected "uninitialized repository"
|
||||||
check u = unexpected $ "UUID " ++ fromUUID u
|
check u = unexpected $ "UUID " ++ fromUUID u
|
||||||
unexpected s = error $
|
unexpected s = error $
|
||||||
"expected repository UUID " ++
|
"expected repository UUID " ++
|
||||||
expected ++ " but found " ++ s
|
expected ++ " but found " ++ s
|
||||||
|
|
||||||
header :: String
|
header :: String
|
||||||
header = "Usage: git-annex-shell [-c] command [parameters ...] [option ..]"
|
header = "Usage: git-annex-shell [-c] command [parameters ...] [option ..]"
|
||||||
|
@ -152,20 +150,20 @@ checkDirectory mdir = do
|
||||||
if d' `equalFilePath` dir'
|
if d' `equalFilePath` dir'
|
||||||
then noop
|
then noop
|
||||||
else req d' (Just dir')
|
else req d' (Just dir')
|
||||||
where
|
where
|
||||||
req d mdir' = error $ unwords
|
req d mdir' = error $ unwords
|
||||||
[ "Only allowed to access"
|
[ "Only allowed to access"
|
||||||
, d
|
, d
|
||||||
, maybe "and could not determine directory from command line" ("not " ++) mdir'
|
, maybe "and could not determine directory from command line" ("not " ++) mdir'
|
||||||
]
|
]
|
||||||
|
|
||||||
{- A directory may start with ~/ or in some cases, even /~/,
|
{- A directory may start with ~/ or in some cases, even /~/,
|
||||||
- or could just be relative to home, or of course could
|
- or could just be relative to home, or of course could
|
||||||
- be absolute. -}
|
- be absolute. -}
|
||||||
canondir home d
|
canondir home d
|
||||||
| "~/" `isPrefixOf` d = return d
|
| "~/" `isPrefixOf` d = return d
|
||||||
| "/~/" `isPrefixOf` d = return $ drop 1 d
|
| "/~/" `isPrefixOf` d = return $ drop 1 d
|
||||||
| otherwise = relHome $ absPathFrom home d
|
| otherwise = relHome $ absPathFrom home d
|
||||||
|
|
||||||
checkEnv :: String -> IO ()
|
checkEnv :: String -> IO ()
|
||||||
checkEnv var = do
|
checkEnv var = do
|
||||||
|
|
|
@ -64,10 +64,10 @@ groupMapLoad = do
|
||||||
|
|
||||||
makeGroupMap :: M.Map UUID (S.Set Group) -> GroupMap
|
makeGroupMap :: M.Map UUID (S.Set Group) -> GroupMap
|
||||||
makeGroupMap byuuid = GroupMap byuuid bygroup
|
makeGroupMap byuuid = GroupMap byuuid bygroup
|
||||||
where
|
where
|
||||||
bygroup = M.fromListWith S.union $
|
bygroup = M.fromListWith S.union $
|
||||||
concat $ map explode $ M.toList byuuid
|
concat $ map explode $ M.toList byuuid
|
||||||
explode (u, s) = map (\g -> (g, S.singleton u)) (S.toList s)
|
explode (u, s) = map (\g -> (g, S.singleton u)) (S.toList s)
|
||||||
|
|
||||||
{- If a repository is in exactly one standard group, returns it. -}
|
{- If a repository is in exactly one standard group, returns it. -}
|
||||||
getStandardGroup :: S.Set Group -> Maybe StandardGroup
|
getStandardGroup :: S.Set Group -> Maybe StandardGroup
|
||||||
|
|
|
@ -47,13 +47,13 @@ loggedKeys = mapMaybe (logFileKey . takeFileName) <$> Annex.Branch.files
|
||||||
- they are present for the specified repository. -}
|
- they are present for the specified repository. -}
|
||||||
loggedKeysFor :: UUID -> Annex [Key]
|
loggedKeysFor :: UUID -> Annex [Key]
|
||||||
loggedKeysFor u = filterM isthere =<< loggedKeys
|
loggedKeysFor u = filterM isthere =<< loggedKeys
|
||||||
where
|
where
|
||||||
{- This should run strictly to avoid the filterM
|
{- This should run strictly to avoid the filterM
|
||||||
- building many thunks containing keyLocations data. -}
|
- building many thunks containing keyLocations data. -}
|
||||||
isthere k = do
|
isthere k = do
|
||||||
us <- loggedLocations k
|
us <- loggedLocations k
|
||||||
let !there = u `elem` us
|
let !there = u `elem` us
|
||||||
return there
|
return there
|
||||||
|
|
||||||
{- The filename of the log file for a given key. -}
|
{- The filename of the log file for a given key. -}
|
||||||
logFile :: Key -> String
|
logFile :: Key -> String
|
||||||
|
@ -64,5 +64,5 @@ logFileKey :: FilePath -> Maybe Key
|
||||||
logFileKey file
|
logFileKey file
|
||||||
| ext == ".log" = fileKey base
|
| ext == ".log" = fileKey base
|
||||||
| otherwise = Nothing
|
| otherwise = Nothing
|
||||||
where
|
where
|
||||||
(base, ext) = splitAt (length file - 4) file
|
(base, ext) = splitAt (length file - 4) file
|
||||||
|
|
|
@ -90,8 +90,8 @@ makeMatcher groupmap u s
|
||||||
| s == "standard" = standardMatcher groupmap u
|
| s == "standard" = standardMatcher groupmap u
|
||||||
| null (lefts tokens) = Utility.Matcher.generate $ rights tokens
|
| null (lefts tokens) = Utility.Matcher.generate $ rights tokens
|
||||||
| otherwise = matchAll
|
| otherwise = matchAll
|
||||||
where
|
where
|
||||||
tokens = map (parseToken (Just u) groupmap) (tokenizeMatcher s)
|
tokens = map (parseToken (Just u) groupmap) (tokenizeMatcher s)
|
||||||
|
|
||||||
{- Standard matchers are pre-defined for some groups. If none is defined,
|
{- Standard matchers are pre-defined for some groups. If none is defined,
|
||||||
- or a repository is in multiple groups with standard matchers, match all. -}
|
- or a repository is in multiple groups with standard matchers, match all. -}
|
||||||
|
@ -124,17 +124,17 @@ parseToken mu groupmap t
|
||||||
, ("smallerthan", limitSize (<))
|
, ("smallerthan", limitSize (<))
|
||||||
, ("inallgroup", limitInAllGroup groupmap)
|
, ("inallgroup", limitInAllGroup groupmap)
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
(k, v) = separate (== '=') t
|
(k, v) = separate (== '=') t
|
||||||
use a = Utility.Matcher.Operation <$> a v
|
use a = Utility.Matcher.Operation <$> a v
|
||||||
|
|
||||||
{- This is really dumb tokenization; there's no support for quoted values.
|
{- This is really dumb tokenization; there's no support for quoted values.
|
||||||
- Open and close parens are always treated as standalone tokens;
|
- Open and close parens are always treated as standalone tokens;
|
||||||
- otherwise tokens must be separated by whitespace. -}
|
- otherwise tokens must be separated by whitespace. -}
|
||||||
tokenizeMatcher :: String -> [String]
|
tokenizeMatcher :: String -> [String]
|
||||||
tokenizeMatcher = filter (not . null ) . concatMap splitparens . words
|
tokenizeMatcher = filter (not . null ) . concatMap splitparens . words
|
||||||
where
|
where
|
||||||
splitparens = segmentDelim (`elem` "()")
|
splitparens = segmentDelim (`elem` "()")
|
||||||
|
|
||||||
{- Puts a UUID in a standard group, and sets its preferred content to use
|
{- Puts a UUID in a standard group, and sets its preferred content to use
|
||||||
- the standard expression for that group, unless something is already set. -}
|
- the standard expression for that group, unless something is already set. -}
|
||||||
|
|
|
@ -53,23 +53,23 @@ readLog = parseLog <$$> Annex.Branch.get
|
||||||
{- Parses a log file. Unparseable lines are ignored. -}
|
{- Parses a log file. Unparseable lines are ignored. -}
|
||||||
parseLog :: String -> [LogLine]
|
parseLog :: String -> [LogLine]
|
||||||
parseLog = mapMaybe (parseline . words) . lines
|
parseLog = mapMaybe (parseline . words) . lines
|
||||||
where
|
where
|
||||||
parseline (a:b:c:_) = do
|
parseline (a:b:c:_) = do
|
||||||
d <- parseTime defaultTimeLocale "%s%Qs" a
|
d <- parseTime defaultTimeLocale "%s%Qs" a
|
||||||
s <- parsestatus b
|
s <- parsestatus b
|
||||||
Just $ LogLine (utcTimeToPOSIXSeconds d) s c
|
Just $ LogLine (utcTimeToPOSIXSeconds d) s c
|
||||||
parseline _ = Nothing
|
parseline _ = Nothing
|
||||||
parsestatus "1" = Just InfoPresent
|
parsestatus "1" = Just InfoPresent
|
||||||
parsestatus "0" = Just InfoMissing
|
parsestatus "0" = Just InfoMissing
|
||||||
parsestatus _ = Nothing
|
parsestatus _ = Nothing
|
||||||
|
|
||||||
{- Generates a log file. -}
|
{- Generates a log file. -}
|
||||||
showLog :: [LogLine] -> String
|
showLog :: [LogLine] -> String
|
||||||
showLog = unlines . map genline
|
showLog = unlines . map genline
|
||||||
where
|
where
|
||||||
genline (LogLine d s i) = unwords [show d, genstatus s, i]
|
genline (LogLine d s i) = unwords [show d, genstatus s, i]
|
||||||
genstatus InfoPresent = "1"
|
genstatus InfoPresent = "1"
|
||||||
genstatus InfoMissing = "0"
|
genstatus InfoMissing = "0"
|
||||||
|
|
||||||
{- Generates a new LogLine with the current date. -}
|
{- Generates a new LogLine with the current date. -}
|
||||||
logNow :: LogStatus -> String -> Annex LogLine
|
logNow :: LogStatus -> String -> Annex LogLine
|
||||||
|
@ -102,7 +102,7 @@ mapLog :: LogLine -> LogMap -> LogMap
|
||||||
mapLog l m
|
mapLog l m
|
||||||
| better = M.insert i l m
|
| better = M.insert i l m
|
||||||
| otherwise = m
|
| otherwise = m
|
||||||
where
|
where
|
||||||
better = maybe True newer $ M.lookup i m
|
better = maybe True newer $ M.lookup i m
|
||||||
newer l' = date l' <= date l
|
newer l' = date l' <= date l
|
||||||
i = info l
|
i = info l
|
||||||
|
|
|
@ -48,40 +48,40 @@ showConfig = unwords . configToKeyVal
|
||||||
{- Given Strings like "key=value", generates a RemoteConfig. -}
|
{- Given Strings like "key=value", generates a RemoteConfig. -}
|
||||||
keyValToConfig :: [String] -> RemoteConfig
|
keyValToConfig :: [String] -> RemoteConfig
|
||||||
keyValToConfig ws = M.fromList $ map (/=/) ws
|
keyValToConfig ws = M.fromList $ map (/=/) ws
|
||||||
where
|
where
|
||||||
(/=/) s = (k, v)
|
(/=/) s = (k, v)
|
||||||
where
|
where
|
||||||
k = takeWhile (/= '=') s
|
k = takeWhile (/= '=') s
|
||||||
v = configUnEscape $ drop (1 + length k) s
|
v = configUnEscape $ drop (1 + length k) s
|
||||||
|
|
||||||
configToKeyVal :: M.Map String String -> [String]
|
configToKeyVal :: M.Map String String -> [String]
|
||||||
configToKeyVal m = map toword $ sort $ M.toList m
|
configToKeyVal m = map toword $ sort $ M.toList m
|
||||||
where
|
where
|
||||||
toword (k, v) = k ++ "=" ++ configEscape v
|
toword (k, v) = k ++ "=" ++ configEscape v
|
||||||
|
|
||||||
configEscape :: String -> String
|
configEscape :: String -> String
|
||||||
configEscape = concatMap escape
|
configEscape = concatMap escape
|
||||||
where
|
where
|
||||||
escape c
|
escape c
|
||||||
| isSpace c || c `elem` "&" = "&" ++ show (ord c) ++ ";"
|
| isSpace c || c `elem` "&" = "&" ++ show (ord c) ++ ";"
|
||||||
| otherwise = [c]
|
| otherwise = [c]
|
||||||
|
|
||||||
configUnEscape :: String -> String
|
configUnEscape :: String -> String
|
||||||
configUnEscape = unescape
|
configUnEscape = unescape
|
||||||
where
|
where
|
||||||
unescape [] = []
|
unescape [] = []
|
||||||
unescape (c:rest)
|
unescape (c:rest)
|
||||||
| c == '&' = entity rest
|
| c == '&' = entity rest
|
||||||
| otherwise = c : unescape rest
|
| otherwise = c : unescape rest
|
||||||
entity s
|
entity s
|
||||||
| not (null num) && ";" `isPrefixOf` r =
|
| not (null num) && ";" `isPrefixOf` r =
|
||||||
chr (Prelude.read num) : unescape rest
|
chr (Prelude.read num) : unescape rest
|
||||||
| otherwise =
|
| otherwise =
|
||||||
'&' : unescape s
|
'&' : unescape s
|
||||||
where
|
where
|
||||||
num = takeWhile isNumber s
|
num = takeWhile isNumber s
|
||||||
r = drop (length num) s
|
r = drop (length num) s
|
||||||
rest = drop 1 r
|
rest = drop 1 r
|
||||||
|
|
||||||
{- for quickcheck -}
|
{- for quickcheck -}
|
||||||
prop_idempotent_configEscape :: String -> Bool
|
prop_idempotent_configEscape :: String -> Bool
|
||||||
|
|
153
Logs/Transfer.hs
153
Logs/Transfer.hs
|
@ -109,43 +109,42 @@ runTransfer t file shouldretry a = do
|
||||||
bracketIO (prep tfile mode info) (cleanup tfile) (a meter)
|
bracketIO (prep tfile mode info) (cleanup tfile) (a meter)
|
||||||
unless ok $ failed info
|
unless ok $ failed info
|
||||||
return ok
|
return ok
|
||||||
where
|
where
|
||||||
prep tfile mode info = catchMaybeIO $ do
|
prep tfile mode info = catchMaybeIO $ do
|
||||||
fd <- openFd (transferLockFile tfile) ReadWrite (Just mode)
|
fd <- openFd (transferLockFile tfile) ReadWrite (Just mode)
|
||||||
defaultFileFlags { trunc = True }
|
defaultFileFlags { trunc = True }
|
||||||
locked <- catchMaybeIO $
|
locked <- catchMaybeIO $
|
||||||
setLock fd (WriteLock, AbsoluteSeek, 0, 0)
|
setLock fd (WriteLock, AbsoluteSeek, 0, 0)
|
||||||
when (locked == Nothing) $
|
when (locked == Nothing) $
|
||||||
error $ "transfer already in progress"
|
error $ "transfer already in progress"
|
||||||
writeTransferInfoFile info tfile
|
writeTransferInfoFile info tfile
|
||||||
return fd
|
return fd
|
||||||
cleanup _ Nothing = noop
|
cleanup _ Nothing = noop
|
||||||
cleanup tfile (Just fd) = do
|
cleanup tfile (Just fd) = do
|
||||||
void $ tryIO $ removeFile tfile
|
void $ tryIO $ removeFile tfile
|
||||||
void $ tryIO $ removeFile $ transferLockFile tfile
|
void $ tryIO $ removeFile $ transferLockFile tfile
|
||||||
closeFd fd
|
closeFd fd
|
||||||
failed info = do
|
failed info = do
|
||||||
failedtfile <- fromRepo $ failedTransferFile t
|
failedtfile <- fromRepo $ failedTransferFile t
|
||||||
createAnnexDirectory $ takeDirectory failedtfile
|
createAnnexDirectory $ takeDirectory failedtfile
|
||||||
liftIO $ writeTransferInfoFile info failedtfile
|
liftIO $ writeTransferInfoFile info failedtfile
|
||||||
retry oldinfo metervar run = do
|
retry oldinfo metervar run = do
|
||||||
v <- tryAnnex run
|
v <- tryAnnex run
|
||||||
case v of
|
case v of
|
||||||
Right b -> return b
|
Right b -> return b
|
||||||
Left _ -> do
|
Left _ -> do
|
||||||
b <- getbytescomplete metervar
|
b <- getbytescomplete metervar
|
||||||
let newinfo = oldinfo { bytesComplete = Just b }
|
let newinfo = oldinfo { bytesComplete = Just b }
|
||||||
if shouldretry oldinfo newinfo
|
if shouldretry oldinfo newinfo
|
||||||
then retry newinfo metervar run
|
then retry newinfo metervar run
|
||||||
else return False
|
else return False
|
||||||
getbytescomplete metervar
|
getbytescomplete metervar
|
||||||
| transferDirection t == Upload =
|
| transferDirection t == Upload =
|
||||||
liftIO $ readMVar metervar
|
liftIO $ readMVar metervar
|
||||||
| otherwise = do
|
| otherwise = do
|
||||||
f <- fromRepo $ gitAnnexTmpLocation (transferKey t)
|
f <- fromRepo $ gitAnnexTmpLocation (transferKey t)
|
||||||
liftIO $ catchDefaultIO 0 $
|
liftIO $ catchDefaultIO 0 $
|
||||||
fromIntegral . fileSize
|
fromIntegral . fileSize <$> getFileStatus f
|
||||||
<$> getFileStatus f
|
|
||||||
|
|
||||||
{- Generates a callback that can be called as transfer progresses to update
|
{- Generates a callback that can be called as transfer progresses to update
|
||||||
- the transfer info file. Also returns the file it'll be updating, and a
|
- the transfer info file. Also returns the file it'll be updating, and a
|
||||||
|
@ -156,20 +155,20 @@ mkProgressUpdater t info = do
|
||||||
_ <- tryAnnex $ createAnnexDirectory $ takeDirectory tfile
|
_ <- tryAnnex $ createAnnexDirectory $ takeDirectory tfile
|
||||||
mvar <- liftIO $ newMVar 0
|
mvar <- liftIO $ newMVar 0
|
||||||
return (liftIO . updater tfile mvar, tfile, mvar)
|
return (liftIO . updater tfile mvar, tfile, mvar)
|
||||||
where
|
where
|
||||||
updater tfile mvar bytes = modifyMVar_ mvar $ \oldbytes -> do
|
updater tfile mvar bytes = modifyMVar_ mvar $ \oldbytes -> do
|
||||||
if (bytes - oldbytes >= mindelta)
|
if (bytes - oldbytes >= mindelta)
|
||||||
then do
|
then do
|
||||||
let info' = info { bytesComplete = Just bytes }
|
let info' = info { bytesComplete = Just bytes }
|
||||||
_ <- tryIO $ writeTransferInfoFile info' tfile
|
_ <- tryIO $ writeTransferInfoFile info' tfile
|
||||||
return bytes
|
return bytes
|
||||||
else return oldbytes
|
else return oldbytes
|
||||||
{- The minimum change in bytesComplete that is worth
|
{- The minimum change in bytesComplete that is worth
|
||||||
- updating a transfer info file for is 1% of the total
|
- updating a transfer info file for is 1% of the total
|
||||||
- keySize, rounded down. -}
|
- keySize, rounded down. -}
|
||||||
mindelta = case keySize (transferKey t) of
|
mindelta = case keySize (transferKey t) of
|
||||||
Just sz -> sz `div` 100
|
Just sz -> sz `div` 100
|
||||||
Nothing -> 100 * 1024 -- arbitrarily, 100 kb
|
Nothing -> 100 * 1024 -- arbitrarily, 100 kb
|
||||||
|
|
||||||
startTransferInfo :: Maybe FilePath -> IO TransferInfo
|
startTransferInfo :: Maybe FilePath -> IO TransferInfo
|
||||||
startTransferInfo file = TransferInfo
|
startTransferInfo file = TransferInfo
|
||||||
|
@ -206,25 +205,23 @@ getTransfers = do
|
||||||
infos <- mapM checkTransfer transfers
|
infos <- mapM checkTransfer transfers
|
||||||
return $ map (\(t, Just i) -> (t, i)) $
|
return $ map (\(t, Just i) -> (t, i)) $
|
||||||
filter running $ zip transfers infos
|
filter running $ zip transfers infos
|
||||||
where
|
where
|
||||||
findfiles = liftIO . mapM dirContentsRecursive
|
findfiles = liftIO . mapM dirContentsRecursive
|
||||||
=<< mapM (fromRepo . transferDir)
|
=<< mapM (fromRepo . transferDir) [Download, Upload]
|
||||||
[Download, Upload]
|
running (_, i) = isJust i
|
||||||
running (_, i) = isJust i
|
|
||||||
|
|
||||||
{- Gets failed transfers for a given remote UUID. -}
|
{- Gets failed transfers for a given remote UUID. -}
|
||||||
getFailedTransfers :: UUID -> Annex [(Transfer, TransferInfo)]
|
getFailedTransfers :: UUID -> Annex [(Transfer, TransferInfo)]
|
||||||
getFailedTransfers u = catMaybes <$> (liftIO . getpairs =<< concat <$> findfiles)
|
getFailedTransfers u = catMaybes <$> (liftIO . getpairs =<< concat <$> findfiles)
|
||||||
where
|
where
|
||||||
getpairs = mapM $ \f -> do
|
getpairs = mapM $ \f -> do
|
||||||
let mt = parseTransferFile f
|
let mt = parseTransferFile f
|
||||||
mi <- readTransferInfoFile Nothing f
|
mi <- readTransferInfoFile Nothing f
|
||||||
return $ case (mt, mi) of
|
return $ case (mt, mi) of
|
||||||
(Just t, Just i) -> Just (t, i)
|
(Just t, Just i) -> Just (t, i)
|
||||||
_ -> Nothing
|
_ -> Nothing
|
||||||
findfiles = liftIO . mapM dirContentsRecursive
|
findfiles = liftIO . mapM dirContentsRecursive
|
||||||
=<< mapM (fromRepo . failedTransferDir u)
|
=<< mapM (fromRepo . failedTransferDir u) [Download, Upload]
|
||||||
[Download, Upload]
|
|
||||||
|
|
||||||
removeFailedTransfer :: Transfer -> Annex ()
|
removeFailedTransfer :: Transfer -> Annex ()
|
||||||
removeFailedTransfer t = do
|
removeFailedTransfer t = do
|
||||||
|
@ -257,8 +254,8 @@ parseTransferFile file
|
||||||
<*> pure (toUUID u)
|
<*> pure (toUUID u)
|
||||||
<*> fileKey key
|
<*> fileKey key
|
||||||
_ -> Nothing
|
_ -> Nothing
|
||||||
where
|
where
|
||||||
bits = splitDirectories file
|
bits = splitDirectories file
|
||||||
|
|
||||||
writeTransferInfoFile :: TransferInfo -> FilePath -> IO ()
|
writeTransferInfoFile :: TransferInfo -> FilePath -> IO ()
|
||||||
writeTransferInfoFile info tfile = do
|
writeTransferInfoFile info tfile = do
|
||||||
|
@ -295,16 +292,16 @@ readTransferInfo mpid s = TransferInfo
|
||||||
<*> bytes
|
<*> bytes
|
||||||
<*> pure (if null filename then Nothing else Just filename)
|
<*> pure (if null filename then Nothing else Just filename)
|
||||||
<*> pure False
|
<*> pure False
|
||||||
where
|
where
|
||||||
(firstline, filename) = separate (== '\n') s
|
(firstline, filename) = separate (== '\n') s
|
||||||
bits = split " " firstline
|
bits = split " " firstline
|
||||||
numbits = length bits
|
numbits = length bits
|
||||||
time = if numbits > 0
|
time = if numbits > 0
|
||||||
then Just <$> parsePOSIXTime =<< headMaybe bits
|
then Just <$> parsePOSIXTime =<< headMaybe bits
|
||||||
else pure Nothing -- not failure
|
else pure Nothing -- not failure
|
||||||
bytes = if numbits > 1
|
bytes = if numbits > 1
|
||||||
then Just <$> readish =<< headMaybe (drop 1 bits)
|
then Just <$> readish =<< headMaybe (drop 1 bits)
|
||||||
else pure Nothing -- not failure
|
else pure Nothing -- not failure
|
||||||
|
|
||||||
parsePOSIXTime :: String -> Maybe POSIXTime
|
parsePOSIXTime :: String -> Maybe POSIXTime
|
||||||
parsePOSIXTime s = utcTimeToPOSIXSeconds
|
parsePOSIXTime s = utcTimeToPOSIXSeconds
|
||||||
|
|
|
@ -87,11 +87,10 @@ trustMapLoad = do
|
||||||
let m = M.union overrides $ M.union configured logged
|
let m = M.union overrides $ M.union configured logged
|
||||||
Annex.changeState $ \s -> s { Annex.trustmap = Just m }
|
Annex.changeState $ \s -> s { Annex.trustmap = Just m }
|
||||||
return m
|
return m
|
||||||
where
|
where
|
||||||
configuredtrust r =
|
configuredtrust r = maybe Nothing (\l -> Just (Types.Remote.uuid r, l))
|
||||||
maybe Nothing (\l -> Just (Types.Remote.uuid r, l)) <$>
|
<$> maybe Nothing readTrustLevel
|
||||||
maybe Nothing readTrustLevel
|
<$> getTrustLevel (Types.Remote.repo r)
|
||||||
<$> getTrustLevel (Types.Remote.repo r)
|
|
||||||
|
|
||||||
{- Does not include forcetrust or git config values, just those from the
|
{- Does not include forcetrust or git config values, just those from the
|
||||||
- log file. -}
|
- log file. -}
|
||||||
|
@ -103,11 +102,11 @@ trustMapRaw = simpleMap . parseLog (Just . parseTrustLog)
|
||||||
- trust status, which is why this defaults to Trusted. -}
|
- trust status, which is why this defaults to Trusted. -}
|
||||||
parseTrustLog :: String -> TrustLevel
|
parseTrustLog :: String -> TrustLevel
|
||||||
parseTrustLog s = maybe Trusted parse $ headMaybe $ words s
|
parseTrustLog s = maybe Trusted parse $ headMaybe $ words s
|
||||||
where
|
where
|
||||||
parse "1" = Trusted
|
parse "1" = Trusted
|
||||||
parse "0" = UnTrusted
|
parse "0" = UnTrusted
|
||||||
parse "X" = DeadTrusted
|
parse "X" = DeadTrusted
|
||||||
parse _ = SemiTrusted
|
parse _ = SemiTrusted
|
||||||
|
|
||||||
showTrustLog :: TrustLevel -> String
|
showTrustLog :: TrustLevel -> String
|
||||||
showTrustLog Trusted = "1"
|
showTrustLog Trusted = "1"
|
||||||
|
|
48
Logs/UUID.hs
48
Logs/UUID.hs
|
@ -53,32 +53,32 @@ describeUUID uuid desc = do
|
||||||
-}
|
-}
|
||||||
fixBadUUID :: Log String -> Log String
|
fixBadUUID :: Log String -> Log String
|
||||||
fixBadUUID = M.fromList . map fixup . M.toList
|
fixBadUUID = M.fromList . map fixup . M.toList
|
||||||
where
|
where
|
||||||
fixup (k, v)
|
fixup (k, v)
|
||||||
| isbad = (fixeduuid, LogEntry (Date $ newertime v) fixedvalue)
|
| isbad = (fixeduuid, LogEntry (Date $ newertime v) fixedvalue)
|
||||||
| otherwise = (k, v)
|
| otherwise = (k, v)
|
||||||
where
|
where
|
||||||
kuuid = fromUUID k
|
kuuid = fromUUID k
|
||||||
isbad = not (isuuid kuuid) && isuuid lastword
|
isbad = not (isuuid kuuid) && isuuid lastword
|
||||||
ws = words $ value v
|
ws = words $ value v
|
||||||
lastword = Prelude.last ws
|
lastword = Prelude.last ws
|
||||||
fixeduuid = toUUID lastword
|
fixeduuid = toUUID lastword
|
||||||
fixedvalue = unwords $ kuuid: Prelude.init ws
|
fixedvalue = unwords $ kuuid: Prelude.init ws
|
||||||
-- For the fixed line to take precidence, it should be
|
-- For the fixed line to take precidence, it should be
|
||||||
-- slightly newer, but only slightly.
|
-- slightly newer, but only slightly.
|
||||||
newertime (LogEntry (Date d) _) = d + minimumPOSIXTimeSlice
|
newertime (LogEntry (Date d) _) = d + minimumPOSIXTimeSlice
|
||||||
newertime (LogEntry Unknown _) = minimumPOSIXTimeSlice
|
newertime (LogEntry Unknown _) = minimumPOSIXTimeSlice
|
||||||
minimumPOSIXTimeSlice = 0.000001
|
minimumPOSIXTimeSlice = 0.000001
|
||||||
isuuid s = length s == 36 && length (split "-" s) == 5
|
isuuid s = length s == 36 && length (split "-" s) == 5
|
||||||
|
|
||||||
{- Records the uuid in the log, if it's not already there. -}
|
{- Records the uuid in the log, if it's not already there. -}
|
||||||
recordUUID :: UUID -> Annex ()
|
recordUUID :: UUID -> Annex ()
|
||||||
recordUUID u = go . M.lookup u =<< uuidMap
|
recordUUID u = go . M.lookup u =<< uuidMap
|
||||||
where
|
where
|
||||||
go (Just "") = set
|
go (Just "") = set
|
||||||
go Nothing = set
|
go Nothing = set
|
||||||
go _ = noop
|
go _ = noop
|
||||||
set = describeUUID u ""
|
set = describeUUID u ""
|
||||||
|
|
||||||
{- The map is cached for speed. -}
|
{- The map is cached for speed. -}
|
||||||
uuidMap :: Annex UUIDMap
|
uuidMap :: Annex UUIDMap
|
||||||
|
@ -95,5 +95,5 @@ uuidMapLoad = do
|
||||||
let m' = M.insertWith' preferold u "" m
|
let m' = M.insertWith' preferold u "" m
|
||||||
Annex.changeState $ \s -> s { Annex.uuidmap = Just m' }
|
Annex.changeState $ \s -> s { Annex.uuidmap = Just m' }
|
||||||
return m'
|
return m'
|
||||||
where
|
where
|
||||||
preferold = flip const
|
preferold = flip const
|
||||||
|
|
|
@ -50,36 +50,36 @@ tskey = "timestamp="
|
||||||
|
|
||||||
showLog :: (a -> String) -> Log a -> String
|
showLog :: (a -> String) -> Log a -> String
|
||||||
showLog shower = unlines . map showpair . M.toList
|
showLog shower = unlines . map showpair . M.toList
|
||||||
where
|
where
|
||||||
showpair (k, LogEntry (Date p) v) =
|
showpair (k, LogEntry (Date p) v) =
|
||||||
unwords [fromUUID k, shower v, tskey ++ show p]
|
unwords [fromUUID k, shower v, tskey ++ show p]
|
||||||
showpair (k, LogEntry Unknown v) =
|
showpair (k, LogEntry Unknown v) =
|
||||||
unwords [fromUUID k, shower v]
|
unwords [fromUUID k, shower v]
|
||||||
|
|
||||||
parseLog :: (String -> Maybe a) -> String -> Log a
|
parseLog :: (String -> Maybe a) -> String -> Log a
|
||||||
parseLog = parseLogWithUUID . const
|
parseLog = parseLogWithUUID . const
|
||||||
|
|
||||||
parseLogWithUUID :: (UUID -> String -> Maybe a) -> String -> Log a
|
parseLogWithUUID :: (UUID -> String -> Maybe a) -> String -> Log a
|
||||||
parseLogWithUUID parser = M.fromListWith best . mapMaybe parse . lines
|
parseLogWithUUID parser = M.fromListWith best . mapMaybe parse . lines
|
||||||
where
|
where
|
||||||
parse line
|
parse line
|
||||||
| null ws = Nothing
|
| null ws = Nothing
|
||||||
| otherwise = parser u (unwords info) >>= makepair
|
| otherwise = parser u (unwords info) >>= makepair
|
||||||
where
|
where
|
||||||
makepair v = Just (u, LogEntry ts v)
|
makepair v = Just (u, LogEntry ts v)
|
||||||
ws = words line
|
ws = words line
|
||||||
u = toUUID $ Prelude.head ws
|
u = toUUID $ Prelude.head ws
|
||||||
t = Prelude.last ws
|
t = Prelude.last ws
|
||||||
ts
|
ts
|
||||||
| tskey `isPrefixOf` t =
|
| tskey `isPrefixOf` t =
|
||||||
pdate $ drop 1 $ dropWhile (/= '=') t
|
pdate $ drop 1 $ dropWhile (/= '=') t
|
||||||
| otherwise = Unknown
|
| otherwise = Unknown
|
||||||
info
|
info
|
||||||
| ts == Unknown = drop 1 ws
|
| ts == Unknown = drop 1 ws
|
||||||
| otherwise = drop 1 $ beginning ws
|
| otherwise = drop 1 $ beginning ws
|
||||||
pdate s = case parseTime defaultTimeLocale "%s%Qs" s of
|
pdate s = case parseTime defaultTimeLocale "%s%Qs" s of
|
||||||
Nothing -> Unknown
|
Nothing -> Unknown
|
||||||
Just d -> Date $ utcTimeToPOSIXSeconds d
|
Just d -> Date $ utcTimeToPOSIXSeconds d
|
||||||
|
|
||||||
changeLog :: POSIXTime -> UUID -> a -> Log a -> Log a
|
changeLog :: POSIXTime -> UUID -> a -> Log a -> Log a
|
||||||
changeLog t u v = M.insert u $ LogEntry (Date t) v
|
changeLog t u v = M.insert u $ LogEntry (Date t) v
|
||||||
|
@ -106,9 +106,9 @@ prop_TimeStamp_sane = Unknown < Date 1
|
||||||
|
|
||||||
prop_addLog_sane :: Bool
|
prop_addLog_sane :: Bool
|
||||||
prop_addLog_sane = newWins && newestWins
|
prop_addLog_sane = newWins && newestWins
|
||||||
where
|
where
|
||||||
newWins = addLog (UUID "foo") (LogEntry (Date 1) "new") l == l2
|
newWins = addLog (UUID "foo") (LogEntry (Date 1) "new") l == l2
|
||||||
newestWins = addLog (UUID "foo") (LogEntry (Date 1) "newest") l2 /= l2
|
newestWins = addLog (UUID "foo") (LogEntry (Date 1) "newest") l2 /= l2
|
||||||
|
|
||||||
l = M.fromList [(UUID "foo", LogEntry (Date 0) "old")]
|
l = M.fromList [(UUID "foo", LogEntry (Date 0) "old")]
|
||||||
l2 = M.fromList [(UUID "foo", LogEntry (Date 1) "new")]
|
l2 = M.fromList [(UUID "foo", LogEntry (Date 1) "new")]
|
||||||
|
|
|
@ -35,13 +35,12 @@ readUnusedLog prefix = do
|
||||||
<$> liftIO (readFile f)
|
<$> liftIO (readFile f)
|
||||||
, return M.empty
|
, return M.empty
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
parse line =
|
parse line = case (readish tag, file2key rest) of
|
||||||
case (readish tag, file2key rest) of
|
(Just num, Just key) -> Just (num, key)
|
||||||
(Just num, Just key) -> Just (num, key)
|
_ -> Nothing
|
||||||
_ -> Nothing
|
where
|
||||||
where
|
(tag, rest) = separate (== ' ') line
|
||||||
(tag, rest) = separate (== ' ') line
|
|
||||||
|
|
||||||
type UnusedMap = M.Map Int Key
|
type UnusedMap = M.Map Int Key
|
||||||
|
|
||||||
|
@ -64,10 +63,10 @@ unusedSpec :: String -> [Int]
|
||||||
unusedSpec spec
|
unusedSpec spec
|
||||||
| "-" `isInfixOf` spec = range $ separate (== '-') spec
|
| "-" `isInfixOf` spec = range $ separate (== '-') spec
|
||||||
| otherwise = catMaybes [readish spec]
|
| otherwise = catMaybes [readish spec]
|
||||||
where
|
where
|
||||||
range (a, b) = case (readish a, readish b) of
|
range (a, b) = case (readish a, readish b) of
|
||||||
(Just x, Just y) -> [x..y]
|
(Just x, Just y) -> [x..y]
|
||||||
_ -> []
|
_ -> []
|
||||||
|
|
||||||
{- Start action for unused content. Finds the number in the maps, and
|
{- Start action for unused content. Finds the number in the maps, and
|
||||||
- calls either of 3 actions, depending on the type of unused file. -}
|
- calls either of 3 actions, depending on the type of unused file. -}
|
||||||
|
@ -81,11 +80,11 @@ startUnused message unused badunused tmpunused maps n = search
|
||||||
, (unusedBadMap maps, badunused)
|
, (unusedBadMap maps, badunused)
|
||||||
, (unusedTmpMap maps, tmpunused)
|
, (unusedTmpMap maps, tmpunused)
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
search [] = stop
|
search [] = stop
|
||||||
search ((m, a):rest) =
|
search ((m, a):rest) =
|
||||||
case M.lookup n m of
|
case M.lookup n m of
|
||||||
Nothing -> search rest
|
Nothing -> search rest
|
||||||
Just key -> do
|
Just key -> do
|
||||||
showStart message (show n)
|
showStart message (show n)
|
||||||
next $ a key
|
next $ a key
|
||||||
|
|
14
Logs/Web.hs
14
Logs/Web.hs
|
@ -37,13 +37,13 @@ oldurlLogs key =
|
||||||
{- Gets all urls that a key might be available from. -}
|
{- Gets all urls that a key might be available from. -}
|
||||||
getUrls :: Key -> Annex [URLString]
|
getUrls :: Key -> Annex [URLString]
|
||||||
getUrls key = go $ urlLog key : oldurlLogs key
|
getUrls key = go $ urlLog key : oldurlLogs key
|
||||||
where
|
where
|
||||||
go [] = return []
|
go [] = return []
|
||||||
go (l:ls) = do
|
go (l:ls) = do
|
||||||
us <- currentLog l
|
us <- currentLog l
|
||||||
if null us
|
if null us
|
||||||
then go ls
|
then go ls
|
||||||
else return us
|
else return us
|
||||||
|
|
||||||
{- Records a change in an url for a key. -}
|
{- Records a change in an url for a key. -}
|
||||||
setUrl :: Key -> URLString -> LogStatus -> Annex ()
|
setUrl :: Key -> URLString -> LogStatus -> Annex ()
|
||||||
|
|
|
@ -20,9 +20,9 @@ import qualified Utility.JSONStream as Stream
|
||||||
start :: String -> Maybe String -> IO ()
|
start :: String -> Maybe String -> IO ()
|
||||||
start command file =
|
start command file =
|
||||||
putStr $ Stream.start $ ("command", command) : filepart file
|
putStr $ Stream.start $ ("command", command) : filepart file
|
||||||
where
|
where
|
||||||
filepart Nothing = []
|
filepart Nothing = []
|
||||||
filepart (Just f) = [("file", f)]
|
filepart (Just f) = [("file", f)]
|
||||||
|
|
||||||
end :: Bool -> IO ()
|
end :: Bool -> IO ()
|
||||||
end b = putStr $ Stream.add [("success", b)] ++ Stream.end
|
end b = putStr $ Stream.add [("success", b)] ++ Stream.end
|
||||||
|
|
|
@ -143,9 +143,9 @@ retrieveEncrypted buprepo (cipher, enck) _ f = liftIO $ catchBoolIO $
|
||||||
withHandle StdoutHandle createProcessSuccess p $ \h -> do
|
withHandle StdoutHandle createProcessSuccess p $ \h -> do
|
||||||
withDecryptedContent cipher (L.hGetContents h) $ L.writeFile f
|
withDecryptedContent cipher (L.hGetContents h) $ L.writeFile f
|
||||||
return True
|
return True
|
||||||
where
|
where
|
||||||
params = bupParams "join" buprepo [Param $ bupRef enck]
|
params = bupParams "join" buprepo [Param $ bupRef enck]
|
||||||
p = proc "bup" $ toCommand params
|
p = proc "bup" $ toCommand params
|
||||||
|
|
||||||
remove :: Key -> Annex Bool
|
remove :: Key -> Annex Bool
|
||||||
remove _ = do
|
remove _ = do
|
||||||
|
@ -164,10 +164,11 @@ checkPresent r bupr k
|
||||||
return $ Right ok
|
return $ Right ok
|
||||||
| otherwise = liftIO $ catchMsgIO $
|
| otherwise = liftIO $ catchMsgIO $
|
||||||
boolSystem "git" $ Git.Command.gitCommandLine params bupr
|
boolSystem "git" $ Git.Command.gitCommandLine params bupr
|
||||||
where
|
where
|
||||||
params =
|
params =
|
||||||
[ Params "show-ref --quiet --verify"
|
[ Params "show-ref --quiet --verify"
|
||||||
, Param $ "refs/heads/" ++ bupRef k]
|
, Param $ "refs/heads/" ++ bupRef k
|
||||||
|
]
|
||||||
|
|
||||||
{- Store UUID in the annex.uuid setting of the bup repository. -}
|
{- Store UUID in the annex.uuid setting of the bup repository. -}
|
||||||
storeBupUUID :: UUID -> BupRepo -> Annex ()
|
storeBupUUID :: UUID -> BupRepo -> Annex ()
|
||||||
|
@ -185,8 +186,8 @@ storeBupUUID u buprepo = do
|
||||||
when (olduuid == "") $
|
when (olduuid == "") $
|
||||||
Git.Command.run "config"
|
Git.Command.run "config"
|
||||||
[Param "annex.uuid", Param v] r'
|
[Param "annex.uuid", Param v] r'
|
||||||
where
|
where
|
||||||
v = fromUUID u
|
v = fromUUID u
|
||||||
|
|
||||||
onBupRemote :: Git.Repo -> (FilePath -> [CommandParam] -> IO a) -> FilePath -> [CommandParam] -> Annex a
|
onBupRemote :: Git.Repo -> (FilePath -> [CommandParam] -> IO a) -> FilePath -> [CommandParam] -> Annex a
|
||||||
onBupRemote r a command params = do
|
onBupRemote r a command params = do
|
||||||
|
@ -227,17 +228,17 @@ bup2GitRemote r
|
||||||
then Git.Construct.fromAbsPath r
|
then Git.Construct.fromAbsPath r
|
||||||
else error "please specify an absolute path"
|
else error "please specify an absolute path"
|
||||||
| otherwise = Git.Construct.fromUrl $ "ssh://" ++ host ++ slash dir
|
| otherwise = Git.Construct.fromUrl $ "ssh://" ++ host ++ slash dir
|
||||||
where
|
where
|
||||||
bits = split ":" r
|
bits = split ":" r
|
||||||
host = Prelude.head bits
|
host = Prelude.head bits
|
||||||
dir = join ":" $ drop 1 bits
|
dir = join ":" $ drop 1 bits
|
||||||
-- "host:~user/dir" is not supported specially by bup;
|
-- "host:~user/dir" is not supported specially by bup;
|
||||||
-- "host:dir" is relative to the home directory;
|
-- "host:dir" is relative to the home directory;
|
||||||
-- "host:" goes in ~/.bup
|
-- "host:" goes in ~/.bup
|
||||||
slash d
|
slash d
|
||||||
| null d = "/~/.bup"
|
| null d = "/~/.bup"
|
||||||
| "/" `isPrefixOf` d = d
|
| "/" `isPrefixOf` d = d
|
||||||
| otherwise = "/~/" ++ d
|
| otherwise = "/~/" ++ d
|
||||||
|
|
||||||
{- Converts a key into a git ref name, which bup-split -n will use to point
|
{- Converts a key into a git ref name, which bup-split -n will use to point
|
||||||
- to it. -}
|
- to it. -}
|
||||||
|
@ -245,8 +246,8 @@ bupRef :: Key -> String
|
||||||
bupRef k
|
bupRef k
|
||||||
| Git.Ref.legal True shown = shown
|
| Git.Ref.legal True shown = shown
|
||||||
| otherwise = "git-annex-" ++ showDigest (sha256 (fromString shown))
|
| otherwise = "git-annex-" ++ showDigest (sha256 (fromString shown))
|
||||||
where
|
where
|
||||||
shown = key2file k
|
shown = key2file k
|
||||||
|
|
||||||
bupLocal :: BupRepo -> Bool
|
bupLocal :: BupRepo -> Bool
|
||||||
bupLocal = notElem ':'
|
bupLocal = notElem ':'
|
||||||
|
|
|
@ -57,7 +57,6 @@ gen r u c = do
|
||||||
readonly = False,
|
readonly = False,
|
||||||
remotetype = remote
|
remotetype = remote
|
||||||
}
|
}
|
||||||
where
|
|
||||||
|
|
||||||
type ChunkSize = Maybe Int64
|
type ChunkSize = Maybe Int64
|
||||||
|
|
||||||
|
@ -101,25 +100,25 @@ chunkCount f = f ++ ".chunkcount"
|
||||||
withCheckedFiles :: (FilePath -> IO Bool) -> ChunkSize -> FilePath -> Key -> ([FilePath] -> IO Bool) -> IO Bool
|
withCheckedFiles :: (FilePath -> IO Bool) -> ChunkSize -> FilePath -> Key -> ([FilePath] -> IO Bool) -> IO Bool
|
||||||
withCheckedFiles _ _ [] _ _ = return False
|
withCheckedFiles _ _ [] _ _ = return False
|
||||||
withCheckedFiles check Nothing d k a = go $ locations d k
|
withCheckedFiles check Nothing d k a = go $ locations d k
|
||||||
where
|
where
|
||||||
go [] = return False
|
go [] = return False
|
||||||
go (f:fs) = ifM (check f) ( a [f] , go fs )
|
go (f:fs) = ifM (check f) ( a [f] , go fs )
|
||||||
withCheckedFiles check (Just _) d k a = go $ locations d k
|
withCheckedFiles check (Just _) d k a = go $ locations d k
|
||||||
where
|
where
|
||||||
go [] = return False
|
go [] = return False
|
||||||
go (f:fs) = do
|
go (f:fs) = do
|
||||||
let chunkcount = chunkCount f
|
let chunkcount = chunkCount f
|
||||||
use <- check chunkcount
|
ifM (check chunkcount)
|
||||||
if use
|
( do
|
||||||
then do
|
count <- readcount chunkcount
|
||||||
count <- readcount chunkcount
|
let chunks = take count $ chunkStream f
|
||||||
let chunks = take count $ chunkStream f
|
ifM (all id <$> mapM check chunks)
|
||||||
ifM (all id <$> mapM check chunks)
|
( a chunks , return False )
|
||||||
( a chunks , return False )
|
, go fs
|
||||||
else go fs
|
)
|
||||||
readcount f = fromMaybe (error $ "cannot parse " ++ f)
|
readcount f = fromMaybe (error $ "cannot parse " ++ f)
|
||||||
. (readish :: String -> Maybe Int)
|
. (readish :: String -> Maybe Int)
|
||||||
<$> readFile f
|
<$> readFile f
|
||||||
|
|
||||||
withStoredFiles :: ChunkSize -> FilePath -> Key -> ([FilePath] -> IO Bool) -> IO Bool
|
withStoredFiles :: ChunkSize -> FilePath -> Key -> ([FilePath] -> IO Bool) -> IO Bool
|
||||||
withStoredFiles = withCheckedFiles doesFileExist
|
withStoredFiles = withCheckedFiles doesFileExist
|
||||||
|
@ -170,39 +169,39 @@ storeSplit' _ _ _ [] c = return $ reverse c
|
||||||
storeSplit' meterupdate chunksize (d:dests) bs c = do
|
storeSplit' meterupdate chunksize (d:dests) bs c = do
|
||||||
bs' <- E.bracket (openFile d WriteMode) hClose (feed chunksize bs)
|
bs' <- E.bracket (openFile d WriteMode) hClose (feed chunksize bs)
|
||||||
storeSplit' meterupdate chunksize dests bs' (d:c)
|
storeSplit' meterupdate chunksize dests bs' (d:c)
|
||||||
where
|
where
|
||||||
feed _ [] _ = return []
|
feed _ [] _ = return []
|
||||||
feed sz (l:ls) h = do
|
feed sz (l:ls) h = do
|
||||||
let s = fromIntegral $ S.length l
|
let s = fromIntegral $ S.length l
|
||||||
if s <= sz
|
if s <= sz
|
||||||
then do
|
then do
|
||||||
S.hPut h l
|
S.hPut h l
|
||||||
meterupdate $ toInteger s
|
meterupdate $ toInteger s
|
||||||
feed (sz - s) ls h
|
feed (sz - s) ls h
|
||||||
else return (l:ls)
|
else return (l:ls)
|
||||||
|
|
||||||
{- Write a L.ByteString to a file, updating a progress meter
|
{- Write a L.ByteString to a file, updating a progress meter
|
||||||
- after each chunk of the L.ByteString, typically every 64 kb or so. -}
|
- after each chunk of the L.ByteString, typically every 64 kb or so. -}
|
||||||
meteredWriteFile :: MeterUpdate -> FilePath -> L.ByteString -> IO ()
|
meteredWriteFile :: MeterUpdate -> FilePath -> L.ByteString -> IO ()
|
||||||
meteredWriteFile meterupdate dest b =
|
meteredWriteFile meterupdate dest b =
|
||||||
meteredWriteFile' meterupdate dest (L.toChunks b) feeder
|
meteredWriteFile' meterupdate dest (L.toChunks b) feeder
|
||||||
where
|
where
|
||||||
feeder chunks = return ([], chunks)
|
feeder chunks = return ([], chunks)
|
||||||
|
|
||||||
{- Writes a series of S.ByteString chunks to a file, updating a progress
|
{- Writes a series of S.ByteString chunks to a file, updating a progress
|
||||||
- meter after each chunk. The feeder is called to get more chunks. -}
|
- meter after each chunk. The feeder is called to get more chunks. -}
|
||||||
meteredWriteFile' :: MeterUpdate -> FilePath -> s -> (s -> IO (s, [S.ByteString])) -> IO ()
|
meteredWriteFile' :: MeterUpdate -> FilePath -> s -> (s -> IO (s, [S.ByteString])) -> IO ()
|
||||||
meteredWriteFile' meterupdate dest startstate feeder =
|
meteredWriteFile' meterupdate dest startstate feeder =
|
||||||
E.bracket (openFile dest WriteMode) hClose (feed startstate [])
|
E.bracket (openFile dest WriteMode) hClose (feed startstate [])
|
||||||
where
|
where
|
||||||
feed state [] h = do
|
feed state [] h = do
|
||||||
(state', cs) <- feeder state
|
(state', cs) <- feeder state
|
||||||
unless (null cs) $
|
unless (null cs) $
|
||||||
feed state' cs h
|
feed state' cs h
|
||||||
feed state (c:cs) h = do
|
feed state (c:cs) h = do
|
||||||
S.hPut h c
|
S.hPut h c
|
||||||
meterupdate $ toInteger $ S.length c
|
meterupdate $ toInteger $ S.length c
|
||||||
feed state cs h
|
feed state cs h
|
||||||
|
|
||||||
{- Generates a list of destinations to write to in order to store a key.
|
{- Generates a list of destinations to write to in order to store a key.
|
||||||
- When chunksize is specified, this list will be a list of chunks.
|
- When chunksize is specified, this list will be a list of chunks.
|
||||||
|
@ -213,36 +212,36 @@ meteredWriteFile' meterupdate dest startstate feeder =
|
||||||
-}
|
-}
|
||||||
storeHelper :: FilePath -> ChunkSize -> Key -> ([FilePath] -> IO [FilePath]) -> Annex Bool
|
storeHelper :: FilePath -> ChunkSize -> Key -> ([FilePath] -> IO [FilePath]) -> Annex Bool
|
||||||
storeHelper d chunksize key a = prep <&&> check <&&> go
|
storeHelper d chunksize key a = prep <&&> check <&&> go
|
||||||
where
|
where
|
||||||
desttemplate = Prelude.head $ locations d key
|
desttemplate = Prelude.head $ locations d key
|
||||||
dir = parentDir desttemplate
|
dir = parentDir desttemplate
|
||||||
tmpdests = case chunksize of
|
tmpdests = case chunksize of
|
||||||
Nothing -> [desttemplate ++ tmpprefix]
|
Nothing -> [desttemplate ++ tmpprefix]
|
||||||
Just _ -> map (++ tmpprefix) (chunkStream desttemplate)
|
Just _ -> map (++ tmpprefix) (chunkStream desttemplate)
|
||||||
tmpprefix = ".tmp"
|
tmpprefix = ".tmp"
|
||||||
detmpprefix f = take (length f - tmpprefixlen) f
|
detmpprefix f = take (length f - tmpprefixlen) f
|
||||||
tmpprefixlen = length tmpprefix
|
tmpprefixlen = length tmpprefix
|
||||||
prep = liftIO $ catchBoolIO $ do
|
prep = liftIO $ catchBoolIO $ do
|
||||||
createDirectoryIfMissing True dir
|
createDirectoryIfMissing True dir
|
||||||
allowWrite dir
|
allowWrite dir
|
||||||
return True
|
return True
|
||||||
{- The size is not exactly known when encrypting the key;
|
{- The size is not exactly known when encrypting the key;
|
||||||
- this assumes that at least the size of the key is
|
- this assumes that at least the size of the key is
|
||||||
- needed as free space. -}
|
- needed as free space. -}
|
||||||
check = checkDiskSpace (Just dir) key 0
|
check = checkDiskSpace (Just dir) key 0
|
||||||
go = liftIO $ catchBoolIO $ do
|
go = liftIO $ catchBoolIO $ do
|
||||||
stored <- a tmpdests
|
stored <- a tmpdests
|
||||||
forM_ stored $ \f -> do
|
forM_ stored $ \f -> do
|
||||||
let dest = detmpprefix f
|
let dest = detmpprefix f
|
||||||
renameFile f dest
|
renameFile f dest
|
||||||
preventWrite dest
|
preventWrite dest
|
||||||
when (chunksize /= Nothing) $ do
|
when (chunksize /= Nothing) $ do
|
||||||
let chunkcount = chunkCount desttemplate
|
let chunkcount = chunkCount desttemplate
|
||||||
_ <- tryIO $ allowWrite chunkcount
|
_ <- tryIO $ allowWrite chunkcount
|
||||||
writeFile chunkcount (show $ length stored)
|
writeFile chunkcount (show $ length stored)
|
||||||
preventWrite chunkcount
|
preventWrite chunkcount
|
||||||
preventWrite dir
|
preventWrite dir
|
||||||
return (not $ null stored)
|
return (not $ null stored)
|
||||||
|
|
||||||
retrieve :: FilePath -> ChunkSize -> Key -> AssociatedFile -> FilePath -> Annex Bool
|
retrieve :: FilePath -> ChunkSize -> Key -> AssociatedFile -> FilePath -> Annex Bool
|
||||||
retrieve d chunksize k _ f = metered Nothing k $ \meterupdate ->
|
retrieve d chunksize k _ f = metered Nothing k $ \meterupdate ->
|
||||||
|
@ -250,11 +249,11 @@ retrieve d chunksize k _ f = metered Nothing k $ \meterupdate ->
|
||||||
catchBoolIO $ do
|
catchBoolIO $ do
|
||||||
meteredWriteFile' meterupdate f files feeder
|
meteredWriteFile' meterupdate f files feeder
|
||||||
return True
|
return True
|
||||||
where
|
where
|
||||||
feeder [] = return ([], [])
|
feeder [] = return ([], [])
|
||||||
feeder (x:xs) = do
|
feeder (x:xs) = do
|
||||||
chunks <- L.toChunks <$> L.readFile x
|
chunks <- L.toChunks <$> L.readFile x
|
||||||
return (xs, chunks)
|
return (xs, chunks)
|
||||||
|
|
||||||
retrieveEncrypted :: FilePath -> ChunkSize -> (Cipher, Key) -> Key -> FilePath -> Annex Bool
|
retrieveEncrypted :: FilePath -> ChunkSize -> (Cipher, Key) -> Key -> FilePath -> Annex Bool
|
||||||
retrieveEncrypted d chunksize (cipher, enck) k f = metered Nothing k $ \meterupdate ->
|
retrieveEncrypted d chunksize (cipher, enck) k f = metered Nothing k $ \meterupdate ->
|
||||||
|
@ -267,20 +266,20 @@ retrieveEncrypted d chunksize (cipher, enck) k f = metered Nothing k $ \meterupd
|
||||||
retrieveCheap :: FilePath -> ChunkSize -> Key -> FilePath -> Annex Bool
|
retrieveCheap :: FilePath -> ChunkSize -> Key -> FilePath -> Annex Bool
|
||||||
retrieveCheap _ (Just _) _ _ = return False -- no cheap retrieval for chunks
|
retrieveCheap _ (Just _) _ _ = return False -- no cheap retrieval for chunks
|
||||||
retrieveCheap d _ k f = liftIO $ withStoredFiles Nothing d k go
|
retrieveCheap d _ k f = liftIO $ withStoredFiles Nothing d k go
|
||||||
where
|
where
|
||||||
go [file] = catchBoolIO $ createSymbolicLink file f >> return True
|
go [file] = catchBoolIO $ createSymbolicLink file f >> return True
|
||||||
go _files = return False
|
go _files = return False
|
||||||
|
|
||||||
remove :: FilePath -> ChunkSize -> Key -> Annex Bool
|
remove :: FilePath -> ChunkSize -> Key -> Annex Bool
|
||||||
remove d chunksize k = liftIO $ withStoredFiles chunksize d k go
|
remove d chunksize k = liftIO $ withStoredFiles chunksize d k go
|
||||||
where
|
where
|
||||||
go = all id <$$> mapM removefile
|
go = all id <$$> mapM removefile
|
||||||
removefile file = catchBoolIO $ do
|
removefile file = catchBoolIO $ do
|
||||||
let dir = parentDir file
|
let dir = parentDir file
|
||||||
allowWrite dir
|
allowWrite dir
|
||||||
removeFile file
|
removeFile file
|
||||||
_ <- tryIO $ removeDirectory dir
|
_ <- tryIO $ removeDirectory dir
|
||||||
return True
|
return True
|
||||||
|
|
||||||
checkPresent :: FilePath -> ChunkSize -> Key -> Annex (Either String Bool)
|
checkPresent :: FilePath -> ChunkSize -> Key -> Annex (Either String Bool)
|
||||||
checkPresent d chunksize k = liftIO $ catchMsgIO $ withStoredFiles chunksize d k $
|
checkPresent d chunksize k = liftIO $ catchMsgIO $ withStoredFiles chunksize d k $
|
||||||
|
|
|
@ -32,12 +32,12 @@ encryptionSetup c = case (M.lookup "encryption" c, extractCipher c) of
|
||||||
(Just "shared", Nothing) -> use "encryption setup" $ genSharedCipher
|
(Just "shared", Nothing) -> use "encryption setup" $ genSharedCipher
|
||||||
(Just keyid, Nothing) -> use "encryption setup" $ genEncryptedCipher keyid
|
(Just keyid, Nothing) -> use "encryption setup" $ genEncryptedCipher keyid
|
||||||
(Just keyid, Just v) -> use "encryption updated" $ updateEncryptedCipher keyid v
|
(Just keyid, Just v) -> use "encryption updated" $ updateEncryptedCipher keyid v
|
||||||
where
|
where
|
||||||
cannotchange = error "Cannot change encryption type of existing remote."
|
cannotchange = error "Cannot change encryption type of existing remote."
|
||||||
use m a = do
|
use m a = do
|
||||||
cipher <- liftIO a
|
cipher <- liftIO a
|
||||||
showNote $ m ++ " " ++ describeCipher cipher
|
showNote $ m ++ " " ++ describeCipher cipher
|
||||||
return $ M.delete "encryption" $ storeCipher c cipher
|
return $ M.delete "encryption" $ storeCipher c cipher
|
||||||
|
|
||||||
{- Modifies a Remote to support encryption.
|
{- Modifies a Remote to support encryption.
|
||||||
-
|
-
|
||||||
|
@ -58,35 +58,35 @@ encryptableRemote c storeKeyEncrypted retrieveKeyFileEncrypted r =
|
||||||
hasKey = withkey $ hasKey r,
|
hasKey = withkey $ hasKey r,
|
||||||
cost = cost r + encryptedRemoteCostAdj
|
cost = cost r + encryptedRemoteCostAdj
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
store k f p = cip k >>= maybe
|
store k f p = cip k >>= maybe
|
||||||
(storeKey r k f p)
|
(storeKey r k f p)
|
||||||
(\enck -> storeKeyEncrypted enck k p)
|
(\enck -> storeKeyEncrypted enck k p)
|
||||||
retrieve k f d = cip k >>= maybe
|
retrieve k f d = cip k >>= maybe
|
||||||
(retrieveKeyFile r k f d)
|
(retrieveKeyFile r k f d)
|
||||||
(\enck -> retrieveKeyFileEncrypted enck k d)
|
(\enck -> retrieveKeyFileEncrypted enck k d)
|
||||||
retrieveCheap k d = cip k >>= maybe
|
retrieveCheap k d = cip k >>= maybe
|
||||||
(retrieveKeyFileCheap r k d)
|
(retrieveKeyFileCheap r k d)
|
||||||
(\_ -> return False)
|
(\_ -> return False)
|
||||||
withkey a k = cip k >>= maybe (a k) (a . snd)
|
withkey a k = cip k >>= maybe (a k) (a . snd)
|
||||||
cip = cipherKey c
|
cip = cipherKey c
|
||||||
|
|
||||||
{- Gets encryption Cipher. The decrypted Ciphers are cached in the Annex
|
{- Gets encryption Cipher. The decrypted Ciphers are cached in the Annex
|
||||||
- state. -}
|
- state. -}
|
||||||
remoteCipher :: RemoteConfig -> Annex (Maybe Cipher)
|
remoteCipher :: RemoteConfig -> Annex (Maybe Cipher)
|
||||||
remoteCipher c = go $ extractCipher c
|
remoteCipher c = go $ extractCipher c
|
||||||
where
|
where
|
||||||
go Nothing = return Nothing
|
go Nothing = return Nothing
|
||||||
go (Just encipher) = do
|
go (Just encipher) = do
|
||||||
cache <- Annex.getState Annex.ciphers
|
cache <- Annex.getState Annex.ciphers
|
||||||
case M.lookup encipher cache of
|
case M.lookup encipher cache of
|
||||||
Just cipher -> return $ Just cipher
|
Just cipher -> return $ Just cipher
|
||||||
Nothing -> decrypt encipher cache
|
Nothing -> decrypt encipher cache
|
||||||
decrypt encipher cache = do
|
decrypt encipher cache = do
|
||||||
showNote "gpg"
|
showNote "gpg"
|
||||||
cipher <- liftIO $ decryptCipher encipher
|
cipher <- liftIO $ decryptCipher encipher
|
||||||
Annex.changeState (\s -> s { Annex.ciphers = M.insert encipher cipher cache })
|
Annex.changeState (\s -> s { Annex.ciphers = M.insert encipher cipher cache })
|
||||||
return $ Just cipher
|
return $ Just cipher
|
||||||
|
|
||||||
{- Checks if there is a trusted (non-shared) cipher. -}
|
{- Checks if there is a trusted (non-shared) cipher. -}
|
||||||
isTrustedCipher :: RemoteConfig -> Bool
|
isTrustedCipher :: RemoteConfig -> Bool
|
||||||
|
@ -97,16 +97,16 @@ isTrustedCipher c =
|
||||||
cipherKey :: Maybe RemoteConfig -> Key -> Annex (Maybe (Cipher, Key))
|
cipherKey :: Maybe RemoteConfig -> Key -> Annex (Maybe (Cipher, Key))
|
||||||
cipherKey Nothing _ = return Nothing
|
cipherKey Nothing _ = return Nothing
|
||||||
cipherKey (Just c) k = maybe Nothing encrypt <$> remoteCipher c
|
cipherKey (Just c) k = maybe Nothing encrypt <$> remoteCipher c
|
||||||
where
|
where
|
||||||
encrypt ciphertext = Just (ciphertext, encryptKey ciphertext k)
|
encrypt ciphertext = Just (ciphertext, encryptKey ciphertext k)
|
||||||
|
|
||||||
{- Stores an StorableCipher in a remote's configuration. -}
|
{- Stores an StorableCipher in a remote's configuration. -}
|
||||||
storeCipher :: RemoteConfig -> StorableCipher -> RemoteConfig
|
storeCipher :: RemoteConfig -> StorableCipher -> RemoteConfig
|
||||||
storeCipher c (SharedCipher t) = M.insert "cipher" (toB64 t) c
|
storeCipher c (SharedCipher t) = M.insert "cipher" (toB64 t) c
|
||||||
storeCipher c (EncryptedCipher t ks) =
|
storeCipher c (EncryptedCipher t ks) =
|
||||||
M.insert "cipher" (toB64 t) $ M.insert "cipherkeys" (showkeys ks) c
|
M.insert "cipher" (toB64 t) $ M.insert "cipherkeys" (showkeys ks) c
|
||||||
where
|
where
|
||||||
showkeys (KeyIds l) = join "," l
|
showkeys (KeyIds l) = join "," l
|
||||||
|
|
||||||
{- Extracts an StorableCipher from a remote's configuration. -}
|
{- Extracts an StorableCipher from a remote's configuration. -}
|
||||||
extractCipher :: RemoteConfig -> Maybe StorableCipher
|
extractCipher :: RemoteConfig -> Maybe StorableCipher
|
||||||
|
@ -115,5 +115,5 @@ extractCipher c =
|
||||||
(Just t, Just ks) -> Just $ EncryptedCipher (fromB64 t) (readkeys ks)
|
(Just t, Just ks) -> Just $ EncryptedCipher (fromB64 t) (readkeys ks)
|
||||||
(Just t, Nothing) -> Just $ SharedCipher (fromB64 t)
|
(Just t, Nothing) -> Just $ SharedCipher (fromB64 t)
|
||||||
_ -> Nothing
|
_ -> Nothing
|
||||||
where
|
where
|
||||||
readkeys = KeyIds . split ","
|
readkeys = KeyIds . split ","
|
||||||
|
|
|
@ -25,16 +25,16 @@ addHooks r = addHooks' r <$> lookupHook r "start" <*> lookupHook r "stop"
|
||||||
addHooks' :: Remote -> Maybe String -> Maybe String -> Remote
|
addHooks' :: Remote -> Maybe String -> Maybe String -> Remote
|
||||||
addHooks' r Nothing Nothing = r
|
addHooks' r Nothing Nothing = r
|
||||||
addHooks' r starthook stophook = r'
|
addHooks' r starthook stophook = r'
|
||||||
where
|
where
|
||||||
r' = r
|
r' = r
|
||||||
{ storeKey = \k f p -> wrapper $ storeKey r k f p
|
{ storeKey = \k f p -> wrapper $ storeKey r k f p
|
||||||
, retrieveKeyFile = \k f d -> wrapper $ retrieveKeyFile r k f d
|
, retrieveKeyFile = \k f d -> wrapper $ retrieveKeyFile r k f d
|
||||||
, retrieveKeyFileCheap = \k f -> wrapper $ retrieveKeyFileCheap r k f
|
, retrieveKeyFileCheap = \k f -> wrapper $ retrieveKeyFileCheap r k f
|
||||||
, removeKey = \k -> wrapper $ removeKey r k
|
, removeKey = \k -> wrapper $ removeKey r k
|
||||||
, hasKey = \k -> wrapper $ hasKey r k
|
, hasKey = \k -> wrapper $ hasKey r k
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
wrapper = runHooks r' starthook stophook
|
wrapper = runHooks r' starthook stophook
|
||||||
|
|
||||||
runHooks :: Remote -> Maybe String -> Maybe String -> Annex a -> Annex a
|
runHooks :: Remote -> Maybe String -> Maybe String -> Annex a -> Annex a
|
||||||
runHooks r starthook stophook a = do
|
runHooks r starthook stophook a = do
|
||||||
|
@ -44,50 +44,49 @@ runHooks r starthook stophook a = do
|
||||||
liftIO $ createDirectoryIfMissing True dir
|
liftIO $ createDirectoryIfMissing True dir
|
||||||
firstrun lck
|
firstrun lck
|
||||||
a
|
a
|
||||||
where
|
where
|
||||||
remoteid = show (uuid r)
|
remoteid = show (uuid r)
|
||||||
run Nothing = noop
|
run Nothing = noop
|
||||||
run (Just command) = void $ liftIO $
|
run (Just command) = void $ liftIO $
|
||||||
boolSystem "sh" [Param "-c", Param command]
|
boolSystem "sh" [Param "-c", Param command]
|
||||||
firstrun lck = do
|
firstrun lck = do
|
||||||
-- Take a shared lock; This indicates that git-annex
|
-- Take a shared lock; This indicates that git-annex
|
||||||
-- is using the remote, and prevents other instances
|
-- is using the remote, and prevents other instances
|
||||||
-- of it from running the stophook. If another
|
-- of it from running the stophook. If another
|
||||||
-- instance is shutting down right now, this
|
-- instance is shutting down right now, this
|
||||||
-- will block waiting for its exclusive lock to clear.
|
-- will block waiting for its exclusive lock to clear.
|
||||||
lockFile lck
|
lockFile lck
|
||||||
|
|
||||||
-- The starthook is run even if some other git-annex
|
-- The starthook is run even if some other git-annex
|
||||||
-- is already running, and ran it before.
|
-- is already running, and ran it before.
|
||||||
-- It would be difficult to use locking to ensure
|
-- It would be difficult to use locking to ensure
|
||||||
-- it's only run once, and it's also possible for
|
-- it's only run once, and it's also possible for
|
||||||
-- git-annex to be interrupted before it can run the
|
-- git-annex to be interrupted before it can run the
|
||||||
-- stophook, in which case the starthook
|
-- stophook, in which case the starthook
|
||||||
-- would be run again by the next git-annex.
|
-- would be run again by the next git-annex.
|
||||||
-- So, requiring idempotency is the right approach.
|
-- So, requiring idempotency is the right approach.
|
||||||
run starthook
|
run starthook
|
||||||
|
|
||||||
Annex.addCleanup (remoteid ++ "-stop-command") $
|
Annex.addCleanup (remoteid ++ "-stop-command") $ runstop lck
|
||||||
runstop lck
|
runstop lck = do
|
||||||
runstop lck = do
|
-- Drop any shared lock we have, and take an
|
||||||
-- Drop any shared lock we have, and take an
|
-- exclusive lock, without blocking. If the lock
|
||||||
-- exclusive lock, without blocking. If the lock
|
-- succeeds, we're the only process using this remote,
|
||||||
-- succeeds, we're the only process using this remote,
|
-- so can stop it.
|
||||||
-- so can stop it.
|
unlockFile lck
|
||||||
unlockFile lck
|
mode <- annexFileMode
|
||||||
mode <- annexFileMode
|
fd <- liftIO $ noUmask mode $
|
||||||
fd <- liftIO $ noUmask mode $
|
openFd lck ReadWrite (Just mode) defaultFileFlags
|
||||||
openFd lck ReadWrite (Just mode) defaultFileFlags
|
v <- liftIO $ tryIO $
|
||||||
v <- liftIO $ tryIO $
|
setLock fd (WriteLock, AbsoluteSeek, 0, 0)
|
||||||
setLock fd (WriteLock, AbsoluteSeek, 0, 0)
|
case v of
|
||||||
case v of
|
Left _ -> noop
|
||||||
Left _ -> noop
|
Right _ -> run stophook
|
||||||
Right _ -> run stophook
|
liftIO $ closeFd fd
|
||||||
liftIO $ closeFd fd
|
|
||||||
|
|
||||||
lookupHook :: Remote -> String -> Annex (Maybe String)
|
lookupHook :: Remote -> String -> Annex (Maybe String)
|
||||||
lookupHook r n = go =<< getRemoteConfig (repo r) hookname ""
|
lookupHook r n = go =<< getRemoteConfig (repo r) hookname ""
|
||||||
where
|
where
|
||||||
go "" = return Nothing
|
go "" = return Nothing
|
||||||
go command = return $ Just command
|
go command = return $ Just command
|
||||||
hookname = n ++ "-command"
|
hookname = n ++ "-command"
|
||||||
|
|
|
@ -23,18 +23,18 @@ findSpecialRemotes :: String -> Annex [Git.Repo]
|
||||||
findSpecialRemotes s = do
|
findSpecialRemotes s = do
|
||||||
m <- fromRepo Git.config
|
m <- fromRepo Git.config
|
||||||
liftIO $ mapM construct $ remotepairs m
|
liftIO $ mapM construct $ remotepairs m
|
||||||
where
|
where
|
||||||
remotepairs = M.toList . M.filterWithKey match
|
remotepairs = M.toList . M.filterWithKey match
|
||||||
construct (k,_) = Git.Construct.remoteNamedFromKey k Git.Construct.fromUnknown
|
construct (k,_) = Git.Construct.remoteNamedFromKey k Git.Construct.fromUnknown
|
||||||
match k _ = startswith "remote." k && endswith (".annex-"++s) k
|
match k _ = startswith "remote." k && endswith (".annex-"++s) k
|
||||||
|
|
||||||
{- Sets up configuration for a special remote in .git/config. -}
|
{- Sets up configuration for a special remote in .git/config. -}
|
||||||
gitConfigSpecialRemote :: UUID -> RemoteConfig -> String -> String -> Annex ()
|
gitConfigSpecialRemote :: UUID -> RemoteConfig -> String -> String -> Annex ()
|
||||||
gitConfigSpecialRemote u c k v = do
|
gitConfigSpecialRemote u c k v = do
|
||||||
set ("annex-"++k) v
|
set ("annex-"++k) v
|
||||||
set ("annex-uuid") (fromUUID u)
|
set ("annex-uuid") (fromUUID u)
|
||||||
where
|
where
|
||||||
set a b = inRepo $ Git.Command.run "config"
|
set a b = inRepo $ Git.Command.run "config"
|
||||||
[Param (configsetting a), Param b]
|
[Param (configsetting a), Param b]
|
||||||
remotename = fromJust (M.lookup "name" c)
|
remotename = fromJust (M.lookup "name" c)
|
||||||
configsetting s = "remote." ++ remotename ++ "." ++ s
|
configsetting s = "remote." ++ remotename ++ "." ++ s
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- git-annex remote access with ssh
|
{- git-annex remote access with ssh
|
||||||
-
|
-
|
||||||
- Copyright 2011.2012 Joey Hess <joey@kitenet.net>
|
- Copyright 2011,2012 Joey Hess <joey@kitenet.net>
|
||||||
-
|
-
|
||||||
- Licensed under the GNU GPL version 3 or higher.
|
- Licensed under the GNU GPL version 3 or higher.
|
||||||
-}
|
-}
|
||||||
|
@ -34,22 +34,22 @@ git_annex_shell r command params fields
|
||||||
sshparams <- sshToRepo r [Param $ sshcmd uuid ]
|
sshparams <- sshToRepo r [Param $ sshcmd uuid ]
|
||||||
return $ Just ("ssh", sshparams)
|
return $ Just ("ssh", sshparams)
|
||||||
| otherwise = return Nothing
|
| otherwise = return Nothing
|
||||||
where
|
where
|
||||||
dir = Git.repoPath r
|
dir = Git.repoPath r
|
||||||
shellcmd = "git-annex-shell"
|
shellcmd = "git-annex-shell"
|
||||||
shellopts = Param command : File dir : params
|
shellopts = Param command : File dir : params
|
||||||
sshcmd uuid = unwords $
|
sshcmd uuid = unwords $
|
||||||
shellcmd : map shellEscape (toCommand shellopts) ++
|
shellcmd : map shellEscape (toCommand shellopts) ++
|
||||||
uuidcheck uuid ++
|
uuidcheck uuid ++
|
||||||
map shellEscape (toCommand fieldopts)
|
map shellEscape (toCommand fieldopts)
|
||||||
uuidcheck NoUUID = []
|
uuidcheck NoUUID = []
|
||||||
uuidcheck (UUID u) = ["--uuid", u]
|
uuidcheck (UUID u) = ["--uuid", u]
|
||||||
fieldopts
|
fieldopts
|
||||||
| null fields = []
|
| null fields = []
|
||||||
| otherwise = fieldsep : map fieldopt fields ++ [fieldsep]
|
| otherwise = fieldsep : map fieldopt fields ++ [fieldsep]
|
||||||
fieldsep = Param "--"
|
fieldsep = Param "--"
|
||||||
fieldopt (field, value) = Param $
|
fieldopt (field, value) = Param $
|
||||||
fieldName field ++ "=" ++ value
|
fieldName field ++ "=" ++ value
|
||||||
|
|
||||||
{- Uses a supplied function (such as boolSystem) to run a git-annex-shell
|
{- Uses a supplied function (such as boolSystem) to run a git-annex-shell
|
||||||
- command on a remote.
|
- command on a remote.
|
||||||
|
|
|
@ -64,19 +64,18 @@ hookSetup u c = do
|
||||||
|
|
||||||
hookEnv :: Key -> Maybe FilePath -> IO (Maybe [(String, String)])
|
hookEnv :: Key -> Maybe FilePath -> IO (Maybe [(String, String)])
|
||||||
hookEnv k f = Just <$> mergeenv (fileenv f ++ keyenv)
|
hookEnv k f = Just <$> mergeenv (fileenv f ++ keyenv)
|
||||||
where
|
where
|
||||||
mergeenv l = M.toList .
|
mergeenv l = M.toList . M.union (M.fromList l)
|
||||||
M.union (M.fromList l)
|
<$> M.fromList <$> getEnvironment
|
||||||
<$> M.fromList <$> getEnvironment
|
env s v = ("ANNEX_" ++ s, v)
|
||||||
env s v = ("ANNEX_" ++ s, v)
|
keyenv = catMaybes
|
||||||
keyenv = catMaybes
|
[ Just $ env "KEY" (key2file k)
|
||||||
[ Just $ env "KEY" (key2file k)
|
, env "HASH_1" <$> headMaybe hashbits
|
||||||
, env "HASH_1" <$> headMaybe hashbits
|
, env "HASH_2" <$> headMaybe (drop 1 hashbits)
|
||||||
, env "HASH_2" <$> headMaybe (drop 1 hashbits)
|
]
|
||||||
]
|
fileenv Nothing = []
|
||||||
fileenv Nothing = []
|
fileenv (Just file) = [env "FILE" file]
|
||||||
fileenv (Just file) = [env "FILE" file]
|
hashbits = map takeDirectory $ splitPath $ hashDirMixed k
|
||||||
hashbits = map takeDirectory $ splitPath $ hashDirMixed k
|
|
||||||
|
|
||||||
lookupHook :: String -> String -> Annex (Maybe String)
|
lookupHook :: String -> String -> Annex (Maybe String)
|
||||||
lookupHook hooktype hook =do
|
lookupHook hooktype hook =do
|
||||||
|
@ -86,22 +85,20 @@ lookupHook hooktype hook =do
|
||||||
warning $ "missing configuration for " ++ hookname
|
warning $ "missing configuration for " ++ hookname
|
||||||
return Nothing
|
return Nothing
|
||||||
else return $ Just command
|
else return $ Just command
|
||||||
where
|
where
|
||||||
hookname = hooktype ++ "-" ++ hook ++ "-hook"
|
hookname = hooktype ++ "-" ++ hook ++ "-hook"
|
||||||
|
|
||||||
runHook :: String -> String -> Key -> Maybe FilePath -> Annex Bool -> Annex Bool
|
runHook :: String -> String -> Key -> Maybe FilePath -> Annex Bool -> Annex Bool
|
||||||
runHook hooktype hook k f a = maybe (return False) run =<< lookupHook hooktype hook
|
runHook hooktype hook k f a = maybe (return False) run =<< lookupHook hooktype hook
|
||||||
where
|
where
|
||||||
run command = do
|
run command = do
|
||||||
showOutput -- make way for hook output
|
showOutput -- make way for hook output
|
||||||
ifM (liftIO $
|
ifM (liftIO $ boolSystemEnv "sh" [Param "-c", Param command] =<< hookEnv k f)
|
||||||
boolSystemEnv "sh" [Param "-c", Param command]
|
( a
|
||||||
=<< hookEnv k f)
|
, do
|
||||||
( a
|
warning $ hook ++ " hook exited nonzero!"
|
||||||
, do
|
return False
|
||||||
warning $ hook ++ " hook exited nonzero!"
|
)
|
||||||
return False
|
|
||||||
)
|
|
||||||
|
|
||||||
store :: String -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
|
store :: String -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
|
||||||
store h k _f _p = do
|
store h k _f _p = do
|
||||||
|
@ -134,9 +131,9 @@ checkPresent r h k = do
|
||||||
showAction $ "checking " ++ Git.repoDescribe r
|
showAction $ "checking " ++ Git.repoDescribe r
|
||||||
v <- lookupHook h "checkpresent"
|
v <- lookupHook h "checkpresent"
|
||||||
liftIO $ catchMsgIO $ check v
|
liftIO $ catchMsgIO $ check v
|
||||||
where
|
where
|
||||||
findkey s = key2file k `elem` lines s
|
findkey s = key2file k `elem` lines s
|
||||||
check Nothing = error "checkpresent hook misconfigured"
|
check Nothing = error "checkpresent hook misconfigured"
|
||||||
check (Just hook) = do
|
check (Just hook) = do
|
||||||
env <- hookEnv k Nothing
|
env <- hookEnv k Nothing
|
||||||
findkey <$> readProcessEnv "sh" ["-c", hook] env
|
findkey <$> readProcessEnv "sh" ["-c", hook] env
|
||||||
|
|
|
@ -56,8 +56,8 @@ remoteList = do
|
||||||
Annex.changeState $ \s -> s { Annex.remotes = rs' }
|
Annex.changeState $ \s -> s { Annex.remotes = rs' }
|
||||||
return rs'
|
return rs'
|
||||||
else return rs
|
else return rs
|
||||||
where
|
where
|
||||||
process m t = enumerate t >>= mapM (remoteGen m t)
|
process m t = enumerate t >>= mapM (remoteGen m t)
|
||||||
|
|
||||||
{- Forces the remoteList to be re-generated, re-reading the git config. -}
|
{- Forces the remoteList to be re-generated, re-reading the git config. -}
|
||||||
remoteListRefresh :: Annex [Remote]
|
remoteListRefresh :: Annex [Remote]
|
||||||
|
@ -81,11 +81,11 @@ updateRemote remote = do
|
||||||
m <- readRemoteLog
|
m <- readRemoteLog
|
||||||
remote' <- updaterepo $ repo remote
|
remote' <- updaterepo $ repo remote
|
||||||
remoteGen m (remotetype remote) remote'
|
remoteGen m (remotetype remote) remote'
|
||||||
where
|
where
|
||||||
updaterepo r
|
updaterepo r
|
||||||
| Git.repoIsLocal r || Git.repoIsLocalUnknown r =
|
| Git.repoIsLocal r || Git.repoIsLocalUnknown r =
|
||||||
Remote.Git.configRead r
|
Remote.Git.configRead r
|
||||||
| otherwise = return r
|
| otherwise = return r
|
||||||
|
|
||||||
{- All remotes that are not ignored. -}
|
{- All remotes that are not ignored. -}
|
||||||
enabledRemoteList :: Annex [Remote]
|
enabledRemoteList :: Annex [Remote]
|
||||||
|
|
|
@ -72,14 +72,14 @@ genRsyncOpts r c = do
|
||||||
<$> getRemoteConfig r "rsync-options" ""
|
<$> getRemoteConfig r "rsync-options" ""
|
||||||
let escape = maybe True (\m -> M.lookup "shellescape" m /= Just "no") c
|
let escape = maybe True (\m -> M.lookup "shellescape" m /= Just "no") c
|
||||||
return $ RsyncOpts url opts escape
|
return $ RsyncOpts url opts escape
|
||||||
where
|
where
|
||||||
safe o
|
safe o
|
||||||
-- Don't allow user to pass --delete to rsync;
|
-- Don't allow user to pass --delete to rsync;
|
||||||
-- that could cause it to delete other keys
|
-- that could cause it to delete other keys
|
||||||
-- in the same hash bucket as a key it sends.
|
-- in the same hash bucket as a key it sends.
|
||||||
| o == "--delete" = False
|
| o == "--delete" = False
|
||||||
| o == "--delete-excluded" = False
|
| o == "--delete-excluded" = False
|
||||||
| otherwise = True
|
| otherwise = True
|
||||||
|
|
||||||
rsyncSetup :: UUID -> RemoteConfig -> Annex RemoteConfig
|
rsyncSetup :: UUID -> RemoteConfig -> Annex RemoteConfig
|
||||||
rsyncSetup u c = do
|
rsyncSetup u c = do
|
||||||
|
@ -100,9 +100,9 @@ rsyncEscape o s
|
||||||
|
|
||||||
rsyncUrls :: RsyncOpts -> Key -> [String]
|
rsyncUrls :: RsyncOpts -> Key -> [String]
|
||||||
rsyncUrls o k = map use annexHashes
|
rsyncUrls o k = map use annexHashes
|
||||||
where
|
where
|
||||||
use h = rsyncUrl o </> h k </> rsyncEscape o (f </> f)
|
use h = rsyncUrl o </> h k </> rsyncEscape o (f </> f)
|
||||||
f = keyFile k
|
f = keyFile k
|
||||||
|
|
||||||
store :: RsyncOpts -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
|
store :: RsyncOpts -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
|
||||||
store o k _f p = rsyncSend o p k <=< inRepo $ gitAnnexLocation k
|
store o k _f p = rsyncSend o p k <=< inRepo $ gitAnnexLocation k
|
||||||
|
@ -146,18 +146,18 @@ remove o k = withRsyncScratchDir $ \tmp -> liftIO $ do
|
||||||
, Param $ addTrailingPathSeparator dummy
|
, Param $ addTrailingPathSeparator dummy
|
||||||
, Param $ rsyncUrl o
|
, Param $ rsyncUrl o
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
{- Specify include rules to match the directories where the
|
{- Specify include rules to match the directories where the
|
||||||
- content could be. Note that the parent directories have
|
- content could be. Note that the parent directories have
|
||||||
- to also be explicitly included, due to how rsync
|
- to also be explicitly included, due to how rsync
|
||||||
- traverses directories. -}
|
- traverses directories. -}
|
||||||
includes = concatMap use annexHashes
|
includes = concatMap use annexHashes
|
||||||
use h = let dir = h k in
|
use h = let dir = h k in
|
||||||
[ parentDir dir
|
[ parentDir dir
|
||||||
, dir
|
, dir
|
||||||
-- match content directory and anything in it
|
-- match content directory and anything in it
|
||||||
, dir </> keyFile k </> "***"
|
, dir </> keyFile k </> "***"
|
||||||
]
|
]
|
||||||
|
|
||||||
checkPresent :: Git.Repo -> RsyncOpts -> Key -> Annex (Either String Bool)
|
checkPresent :: Git.Repo -> RsyncOpts -> Key -> Annex (Either String Bool)
|
||||||
checkPresent r o k = do
|
checkPresent r o k = do
|
||||||
|
@ -165,13 +165,13 @@ checkPresent r o k = do
|
||||||
-- note: Does not currently differentiate between rsync failing
|
-- note: Does not currently differentiate between rsync failing
|
||||||
-- to connect, and the file not being present.
|
-- to connect, and the file not being present.
|
||||||
Right <$> check
|
Right <$> check
|
||||||
where
|
where
|
||||||
check = untilTrue (rsyncUrls o k) $ \u ->
|
check = untilTrue (rsyncUrls o k) $ \u ->
|
||||||
liftIO $ catchBoolIO $ do
|
liftIO $ catchBoolIO $ do
|
||||||
withQuietOutput createProcessSuccess $
|
withQuietOutput createProcessSuccess $
|
||||||
proc "rsync" $ toCommand $
|
proc "rsync" $ toCommand $
|
||||||
rsyncOptions o ++ [Param u]
|
rsyncOptions o ++ [Param u]
|
||||||
return True
|
return True
|
||||||
|
|
||||||
{- Rsync params to enable resumes of sending files safely,
|
{- Rsync params to enable resumes of sending files safely,
|
||||||
- ensure that files are only moved into place once complete
|
- ensure that files are only moved into place once complete
|
||||||
|
@ -190,9 +190,9 @@ withRsyncScratchDir a = do
|
||||||
nuke tmp
|
nuke tmp
|
||||||
liftIO $ createDirectoryIfMissing True tmp
|
liftIO $ createDirectoryIfMissing True tmp
|
||||||
nuke tmp `after` a tmp
|
nuke tmp `after` a tmp
|
||||||
where
|
where
|
||||||
nuke d = liftIO $ whenM (doesDirectoryExist d) $
|
nuke d = liftIO $ whenM (doesDirectoryExist d) $
|
||||||
removeDirectoryRecursive d
|
removeDirectoryRecursive d
|
||||||
|
|
||||||
rsyncRemote :: RsyncOpts -> (Maybe MeterUpdate) -> [CommandParam] -> Annex Bool
|
rsyncRemote :: RsyncOpts -> (Maybe MeterUpdate) -> [CommandParam] -> Annex Bool
|
||||||
rsyncRemote o callback params = do
|
rsyncRemote o callback params = do
|
||||||
|
@ -203,9 +203,9 @@ rsyncRemote o callback params = do
|
||||||
showLongNote "rsync failed -- run git annex again to resume file transfer"
|
showLongNote "rsync failed -- run git annex again to resume file transfer"
|
||||||
return False
|
return False
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
defaultParams = [Params "--progress"]
|
defaultParams = [Params "--progress"]
|
||||||
ps = rsyncOptions o ++ defaultParams ++ params
|
ps = rsyncOptions o ++ defaultParams ++ params
|
||||||
|
|
||||||
{- To send a single key is slightly tricky; need to build up a temporary
|
{- To send a single key is slightly tricky; need to build up a temporary
|
||||||
directory structure to pass to rsync so it can create the hash
|
directory structure to pass to rsync so it can create the hash
|
||||||
|
|
239
Remote/S3.hs
239
Remote/S3.hs
|
@ -48,74 +48,71 @@ gen' r u c cst =
|
||||||
(storeEncrypted this)
|
(storeEncrypted this)
|
||||||
(retrieveEncrypted this)
|
(retrieveEncrypted this)
|
||||||
this
|
this
|
||||||
where
|
where
|
||||||
this = Remote {
|
this = Remote {
|
||||||
uuid = u,
|
uuid = u,
|
||||||
cost = cst,
|
cost = cst,
|
||||||
name = Git.repoDescribe r,
|
name = Git.repoDescribe r,
|
||||||
storeKey = store this,
|
storeKey = store this,
|
||||||
retrieveKeyFile = retrieve this,
|
retrieveKeyFile = retrieve this,
|
||||||
retrieveKeyFileCheap = retrieveCheap this,
|
retrieveKeyFileCheap = retrieveCheap this,
|
||||||
removeKey = remove this,
|
removeKey = remove this,
|
||||||
hasKey = checkPresent this,
|
hasKey = checkPresent this,
|
||||||
hasKeyCheap = False,
|
hasKeyCheap = False,
|
||||||
whereisKey = Nothing,
|
whereisKey = Nothing,
|
||||||
config = c,
|
config = c,
|
||||||
repo = r,
|
repo = r,
|
||||||
localpath = Nothing,
|
localpath = Nothing,
|
||||||
readonly = False,
|
readonly = False,
|
||||||
remotetype = remote
|
remotetype = remote
|
||||||
}
|
}
|
||||||
|
|
||||||
s3Setup :: UUID -> RemoteConfig -> Annex RemoteConfig
|
s3Setup :: UUID -> RemoteConfig -> Annex RemoteConfig
|
||||||
s3Setup u c = handlehost $ M.lookup "host" c
|
s3Setup u c = handlehost $ M.lookup "host" c
|
||||||
where
|
where
|
||||||
remotename = fromJust (M.lookup "name" c)
|
remotename = fromJust (M.lookup "name" c)
|
||||||
defbucket = remotename ++ "-" ++ fromUUID u
|
defbucket = remotename ++ "-" ++ fromUUID u
|
||||||
defaults = M.fromList
|
defaults = M.fromList
|
||||||
[ ("datacenter", "US")
|
[ ("datacenter", "US")
|
||||||
, ("storageclass", "STANDARD")
|
, ("storageclass", "STANDARD")
|
||||||
, ("host", defaultAmazonS3Host)
|
, ("host", defaultAmazonS3Host)
|
||||||
, ("port", show defaultAmazonS3Port)
|
, ("port", show defaultAmazonS3Port)
|
||||||
, ("bucket", defbucket)
|
, ("bucket", defbucket)
|
||||||
]
|
]
|
||||||
|
|
||||||
handlehost Nothing = defaulthost
|
handlehost Nothing = defaulthost
|
||||||
handlehost (Just h)
|
handlehost (Just h)
|
||||||
| ".archive.org" `isSuffixOf` map toLower h = archiveorg
|
| ".archive.org" `isSuffixOf` map toLower h = archiveorg
|
||||||
| otherwise = defaulthost
|
| otherwise = defaulthost
|
||||||
|
|
||||||
use fullconfig = do
|
use fullconfig = do
|
||||||
gitConfigSpecialRemote u fullconfig "s3" "true"
|
gitConfigSpecialRemote u fullconfig "s3" "true"
|
||||||
s3SetCreds fullconfig u
|
s3SetCreds fullconfig u
|
||||||
|
|
||||||
defaulthost = do
|
defaulthost = do
|
||||||
c' <- encryptionSetup c
|
c' <- encryptionSetup c
|
||||||
let fullconfig = c' `M.union` defaults
|
let fullconfig = c' `M.union` defaults
|
||||||
genBucket fullconfig u
|
genBucket fullconfig u
|
||||||
use fullconfig
|
use fullconfig
|
||||||
|
|
||||||
archiveorg = do
|
archiveorg = do
|
||||||
showNote "Internet Archive mode"
|
showNote "Internet Archive mode"
|
||||||
maybe (error "specify bucket=") (const noop) $
|
maybe (error "specify bucket=") (const noop) $
|
||||||
M.lookup "bucket" archiveconfig
|
M.lookup "bucket" archiveconfig
|
||||||
use archiveconfig
|
use archiveconfig
|
||||||
where
|
where
|
||||||
archiveconfig =
|
archiveconfig =
|
||||||
-- hS3 does not pass through
|
-- hS3 does not pass through x-archive-* headers
|
||||||
-- x-archive-* headers
|
M.mapKeys (replace "x-archive-" "x-amz-") $
|
||||||
M.mapKeys (replace "x-archive-" "x-amz-") $
|
-- encryption does not make sense here
|
||||||
-- encryption does not make sense here
|
M.insert "encryption" "none" $
|
||||||
M.insert "encryption" "none" $
|
M.union c $
|
||||||
M.union c $
|
-- special constraints on key names
|
||||||
-- special constraints on key names
|
M.insert "mungekeys" "ia" $
|
||||||
M.insert "mungekeys" "ia" $
|
-- bucket created only when files are uploaded
|
||||||
-- bucket created only when files
|
M.insert "x-amz-auto-make-bucket" "1" $
|
||||||
-- are uploaded
|
-- no default bucket name; should be human-readable
|
||||||
M.insert "x-amz-auto-make-bucket" "1" $
|
M.delete "bucket" defaults
|
||||||
-- no default bucket name; should
|
|
||||||
-- be human-readable
|
|
||||||
M.delete "bucket" defaults
|
|
||||||
|
|
||||||
store :: Remote -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
|
store :: Remote -> Key -> AssociatedFile -> MeterUpdate -> Annex Bool
|
||||||
store r k _f _p = s3Action r False $ \(conn, bucket) -> do
|
store r k _f _p = s3Action r False $ \(conn, bucket) -> do
|
||||||
|
@ -143,15 +140,15 @@ storeHelper (conn, bucket) r k file = do
|
||||||
S3Object bucket (bucketFile r k) ""
|
S3Object bucket (bucketFile r k) ""
|
||||||
(("Content-Length", show size) : xheaders) content
|
(("Content-Length", show size) : xheaders) content
|
||||||
sendObject conn object
|
sendObject conn object
|
||||||
where
|
where
|
||||||
storageclass =
|
storageclass =
|
||||||
case fromJust $ M.lookup "storageclass" $ fromJust $ config r of
|
case fromJust $ M.lookup "storageclass" $ fromJust $ config r of
|
||||||
"REDUCED_REDUNDANCY" -> REDUCED_REDUNDANCY
|
"REDUCED_REDUNDANCY" -> REDUCED_REDUNDANCY
|
||||||
_ -> STANDARD
|
_ -> STANDARD
|
||||||
getsize = fileSize <$> (liftIO $ getFileStatus file)
|
getsize = fileSize <$> (liftIO $ getFileStatus file)
|
||||||
|
|
||||||
xheaders = filter isxheader $ M.assocs $ fromJust $ config r
|
xheaders = filter isxheader $ M.assocs $ fromJust $ config r
|
||||||
isxheader (h, _) = "x-amz-" `isPrefixOf` h
|
isxheader (h, _) = "x-amz-" `isPrefixOf` h
|
||||||
|
|
||||||
retrieve :: Remote -> Key -> AssociatedFile -> FilePath -> Annex Bool
|
retrieve :: Remote -> Key -> AssociatedFile -> FilePath -> Annex Bool
|
||||||
retrieve r k _f d = s3Action r False $ \(conn, bucket) -> do
|
retrieve r k _f d = s3Action r False $ \(conn, bucket) -> do
|
||||||
|
@ -188,8 +185,8 @@ checkPresent r k = s3Action r noconn $ \(conn, bucket) -> do
|
||||||
Right _ -> return $ Right True
|
Right _ -> return $ Right True
|
||||||
Left (AWSError _ _) -> return $ Right False
|
Left (AWSError _ _) -> return $ Right False
|
||||||
Left e -> return $ Left (s3Error e)
|
Left e -> return $ Left (s3Error e)
|
||||||
where
|
where
|
||||||
noconn = Left $ error "S3 not configured"
|
noconn = Left $ error "S3 not configured"
|
||||||
|
|
||||||
s3Warning :: ReqError -> Annex Bool
|
s3Warning :: ReqError -> Annex Bool
|
||||||
s3Warning e = do
|
s3Warning e = do
|
||||||
|
@ -215,12 +212,12 @@ s3Action r noconn action = do
|
||||||
|
|
||||||
bucketFile :: Remote -> Key -> FilePath
|
bucketFile :: Remote -> Key -> FilePath
|
||||||
bucketFile r = munge . key2file
|
bucketFile r = munge . key2file
|
||||||
where
|
where
|
||||||
munge s = case M.lookup "mungekeys" c of
|
munge s = case M.lookup "mungekeys" c of
|
||||||
Just "ia" -> iaMunge $ fileprefix ++ s
|
Just "ia" -> iaMunge $ fileprefix ++ s
|
||||||
_ -> fileprefix ++ s
|
_ -> fileprefix ++ s
|
||||||
fileprefix = M.findWithDefault "" "fileprefix" c
|
fileprefix = M.findWithDefault "" "fileprefix" c
|
||||||
c = fromJust $ config r
|
c = fromJust $ config r
|
||||||
|
|
||||||
bucketKey :: Remote -> String -> Key -> S3Object
|
bucketKey :: Remote -> String -> Key -> S3Object
|
||||||
bucketKey r bucket k = S3Object bucket (bucketFile r k) "" [] L.empty
|
bucketKey r bucket k = S3Object bucket (bucketFile r k) "" [] L.empty
|
||||||
|
@ -230,12 +227,12 @@ bucketKey r bucket k = S3Object bucket (bucketFile r k) "" [] L.empty
|
||||||
- encoded. -}
|
- encoded. -}
|
||||||
iaMunge :: String -> String
|
iaMunge :: String -> String
|
||||||
iaMunge = (>>= munge)
|
iaMunge = (>>= munge)
|
||||||
where
|
where
|
||||||
munge c
|
munge c
|
||||||
| isAsciiUpper c || isAsciiLower c || isNumber c = [c]
|
| isAsciiUpper c || isAsciiLower c || isNumber c = [c]
|
||||||
| c `elem` "_-.\"" = [c]
|
| c `elem` "_-.\"" = [c]
|
||||||
| isSpace c = []
|
| isSpace c = []
|
||||||
| otherwise = "&" ++ show (ord c) ++ ";"
|
| otherwise = "&" ++ show (ord c) ++ ";"
|
||||||
|
|
||||||
genBucket :: RemoteConfig -> UUID -> Annex ()
|
genBucket :: RemoteConfig -> UUID -> Annex ()
|
||||||
genBucket c u = do
|
genBucket c u = do
|
||||||
|
@ -251,9 +248,9 @@ genBucket c u = do
|
||||||
case res of
|
case res of
|
||||||
Right _ -> noop
|
Right _ -> noop
|
||||||
Left err -> s3Error err
|
Left err -> s3Error err
|
||||||
where
|
where
|
||||||
bucket = fromJust $ M.lookup "bucket" c
|
bucket = fromJust $ M.lookup "bucket" c
|
||||||
datacenter = fromJust $ M.lookup "datacenter" c
|
datacenter = fromJust $ M.lookup "datacenter" c
|
||||||
|
|
||||||
s3ConnectionRequired :: RemoteConfig -> UUID -> Annex AWSConnection
|
s3ConnectionRequired :: RemoteConfig -> UUID -> Annex AWSConnection
|
||||||
s3ConnectionRequired c u =
|
s3ConnectionRequired c u =
|
||||||
|
@ -267,46 +264,46 @@ s3Connection c u = do
|
||||||
_ -> do
|
_ -> do
|
||||||
warning $ "Set both " ++ s3AccessKey ++ " and " ++ s3SecretKey ++ " to use S3"
|
warning $ "Set both " ++ s3AccessKey ++ " and " ++ s3SecretKey ++ " to use S3"
|
||||||
return Nothing
|
return Nothing
|
||||||
where
|
where
|
||||||
host = fromJust $ M.lookup "host" c
|
host = fromJust $ M.lookup "host" c
|
||||||
port = let s = fromJust $ M.lookup "port" c in
|
port = let s = fromJust $ M.lookup "port" c in
|
||||||
case reads s of
|
case reads s of
|
||||||
[(p, _)] -> p
|
[(p, _)] -> p
|
||||||
_ -> error $ "bad S3 port value: " ++ s
|
_ -> error $ "bad S3 port value: " ++ s
|
||||||
|
|
||||||
{- S3 creds come from the environment if set, otherwise from the cache
|
{- S3 creds come from the environment if set, otherwise from the cache
|
||||||
- in gitAnnexCredsDir, or failing that, might be stored encrypted in
|
- in gitAnnexCredsDir, or failing that, might be stored encrypted in
|
||||||
- the remote's config. -}
|
- the remote's config. -}
|
||||||
s3GetCreds :: RemoteConfig -> UUID -> Annex (Maybe (String, String))
|
s3GetCreds :: RemoteConfig -> UUID -> Annex (Maybe (String, String))
|
||||||
s3GetCreds c u = maybe fromcache (return . Just) =<< liftIO getenv
|
s3GetCreds c u = maybe fromcache (return . Just) =<< liftIO getenv
|
||||||
where
|
where
|
||||||
getenv = liftM2 (,)
|
getenv = liftM2 (,)
|
||||||
<$> get s3AccessKey
|
<$> get s3AccessKey
|
||||||
<*> get s3SecretKey
|
<*> get s3SecretKey
|
||||||
where
|
where
|
||||||
get = catchMaybeIO . getEnv
|
get = catchMaybeIO . getEnv
|
||||||
fromcache = do
|
fromcache = do
|
||||||
d <- fromRepo gitAnnexCredsDir
|
d <- fromRepo gitAnnexCredsDir
|
||||||
let f = d </> fromUUID u
|
let f = d </> fromUUID u
|
||||||
v <- liftIO $ catchMaybeIO $ readFile f
|
v <- liftIO $ catchMaybeIO $ readFile f
|
||||||
case lines <$> v of
|
case lines <$> v of
|
||||||
Just (ak:sk:[]) -> return $ Just (ak, sk)
|
Just (ak:sk:[]) -> return $ Just (ak, sk)
|
||||||
_ -> fromconfig
|
_ -> fromconfig
|
||||||
fromconfig = do
|
fromconfig = do
|
||||||
mcipher <- remoteCipher c
|
mcipher <- remoteCipher c
|
||||||
case (M.lookup "s3creds" c, mcipher) of
|
case (M.lookup "s3creds" c, mcipher) of
|
||||||
(Just s3creds, Just cipher) -> do
|
(Just s3creds, Just cipher) -> do
|
||||||
creds <- liftIO $ decrypt s3creds cipher
|
creds <- liftIO $ decrypt s3creds cipher
|
||||||
case creds of
|
case creds of
|
||||||
[ak, sk] -> do
|
[ak, sk] -> do
|
||||||
s3CacheCreds (ak, sk) u
|
s3CacheCreds (ak, sk) u
|
||||||
return $ Just (ak, sk)
|
return $ Just (ak, sk)
|
||||||
_ -> do error "bad s3creds"
|
_ -> do error "bad s3creds"
|
||||||
_ -> return Nothing
|
_ -> return Nothing
|
||||||
decrypt s3creds cipher = lines <$>
|
decrypt s3creds cipher = lines
|
||||||
withDecryptedContent cipher
|
<$> withDecryptedContent cipher
|
||||||
(return $ L.pack $ fromB64 s3creds)
|
(return $ L.pack $ fromB64 s3creds)
|
||||||
(return . L.unpack)
|
(return . L.unpack)
|
||||||
|
|
||||||
{- Stores S3 creds encrypted in the remote's config if possible to do so
|
{- Stores S3 creds encrypted in the remote's config if possible to do so
|
||||||
- securely, and otherwise locally in gitAnnexCredsDir. -}
|
- securely, and otherwise locally in gitAnnexCredsDir. -}
|
||||||
|
|
|
@ -55,13 +55,13 @@ gen r _ _ =
|
||||||
|
|
||||||
downloadKey :: Key -> AssociatedFile -> FilePath -> Annex Bool
|
downloadKey :: Key -> AssociatedFile -> FilePath -> Annex Bool
|
||||||
downloadKey key _file dest = get =<< getUrls key
|
downloadKey key _file dest = get =<< getUrls key
|
||||||
where
|
where
|
||||||
get [] = do
|
get [] = do
|
||||||
warning "no known url"
|
warning "no known url"
|
||||||
return False
|
return False
|
||||||
get urls = do
|
get urls = do
|
||||||
showOutput -- make way for download progress bar
|
showOutput -- make way for download progress bar
|
||||||
downloadUrl urls dest
|
downloadUrl urls dest
|
||||||
|
|
||||||
downloadKeyCheap :: Key -> FilePath -> Annex Bool
|
downloadKeyCheap :: Key -> FilePath -> Annex Bool
|
||||||
downloadKeyCheap _ _ = return False
|
downloadKeyCheap _ _ = return False
|
||||||
|
|
43
Seek.hs
43
Seek.hs
|
@ -35,21 +35,21 @@ withFilesNotInGit a params = do
|
||||||
seekunless (null ps && not (null params)) ps
|
seekunless (null ps && not (null params)) ps
|
||||||
dotfiles <- seekunless (null dotps) dotps
|
dotfiles <- seekunless (null dotps) dotps
|
||||||
prepFiltered a $ return $ preserveOrder params (files++dotfiles)
|
prepFiltered a $ return $ preserveOrder params (files++dotfiles)
|
||||||
where
|
where
|
||||||
(dotps, ps) = partition dotfile params
|
(dotps, ps) = partition dotfile params
|
||||||
seekunless True _ = return []
|
seekunless True _ = return []
|
||||||
seekunless _ l = do
|
seekunless _ l = do
|
||||||
force <- Annex.getState Annex.force
|
force <- Annex.getState Annex.force
|
||||||
g <- gitRepo
|
g <- gitRepo
|
||||||
liftIO $ Git.Command.leaveZombie <$> LsFiles.notInRepo force l g
|
liftIO $ Git.Command.leaveZombie <$> LsFiles.notInRepo force l g
|
||||||
|
|
||||||
withPathContents :: ((FilePath, FilePath) -> CommandStart) -> CommandSeek
|
withPathContents :: ((FilePath, FilePath) -> CommandStart) -> CommandSeek
|
||||||
withPathContents a params = map a . concat <$> liftIO (mapM get params)
|
withPathContents a params = map a . concat <$> liftIO (mapM get params)
|
||||||
where
|
where
|
||||||
get p = ifM (isDirectory <$> getFileStatus p)
|
get p = ifM (isDirectory <$> getFileStatus p)
|
||||||
( map (\f -> (f, makeRelative p f)) <$> dirContentsRecursive p
|
( map (\f -> (f, makeRelative p f)) <$> dirContentsRecursive p
|
||||||
, return [(p, takeFileName p)]
|
, return [(p, takeFileName p)]
|
||||||
)
|
)
|
||||||
|
|
||||||
withWords :: ([String] -> CommandStart) -> CommandSeek
|
withWords :: ([String] -> CommandStart) -> CommandSeek
|
||||||
withWords a params = return [a params]
|
withWords a params = return [a params]
|
||||||
|
@ -59,10 +59,10 @@ withStrings a params = return $ map a params
|
||||||
|
|
||||||
withPairs :: ((String, String) -> CommandStart) -> CommandSeek
|
withPairs :: ((String, String) -> CommandStart) -> CommandSeek
|
||||||
withPairs a params = return $ map a $ pairs [] params
|
withPairs a params = return $ map a $ pairs [] params
|
||||||
where
|
where
|
||||||
pairs c [] = reverse c
|
pairs c [] = reverse c
|
||||||
pairs c (x:y:xs) = pairs ((x,y):c) xs
|
pairs c (x:y:xs) = pairs ((x,y):c) xs
|
||||||
pairs _ _ = error "expected pairs"
|
pairs _ _ = error "expected pairs"
|
||||||
|
|
||||||
withFilesToBeCommitted :: (String -> CommandStart) -> CommandSeek
|
withFilesToBeCommitted :: (String -> CommandStart) -> CommandSeek
|
||||||
withFilesToBeCommitted a params = prepFiltered a $
|
withFilesToBeCommitted a params = prepFiltered a $
|
||||||
|
@ -83,8 +83,8 @@ withFilesUnlocked' typechanged a params = do
|
||||||
|
|
||||||
withKeys :: (Key -> CommandStart) -> CommandSeek
|
withKeys :: (Key -> CommandStart) -> CommandSeek
|
||||||
withKeys a params = return $ map (a . parse) params
|
withKeys a params = return $ map (a . parse) params
|
||||||
where
|
where
|
||||||
parse p = fromMaybe (error "bad key") $ file2key p
|
parse p = fromMaybe (error "bad key") $ file2key p
|
||||||
|
|
||||||
withValue :: Annex v -> (v -> CommandSeek) -> CommandSeek
|
withValue :: Annex v -> (v -> CommandSeek) -> CommandSeek
|
||||||
withValue v a params = do
|
withValue v a params = do
|
||||||
|
@ -111,10 +111,9 @@ prepFiltered :: (FilePath -> CommandStart) -> Annex [FilePath] -> Annex [Command
|
||||||
prepFiltered a fs = do
|
prepFiltered a fs = do
|
||||||
matcher <- Limit.getMatcher
|
matcher <- Limit.getMatcher
|
||||||
map (process matcher) <$> fs
|
map (process matcher) <$> fs
|
||||||
where
|
where
|
||||||
process matcher f = do
|
process matcher f = ifM (matcher $ Annex.FileInfo f f)
|
||||||
ok <- matcher $ Annex.FileInfo f f
|
( a f , return Nothing )
|
||||||
if ok then a f else return Nothing
|
|
||||||
|
|
||||||
notSymlink :: FilePath -> IO Bool
|
notSymlink :: FilePath -> IO Bool
|
||||||
notSymlink f = liftIO $ not . isSymbolicLink <$> getSymbolicLinkStatus f
|
notSymlink f = liftIO $ not . isSymbolicLink <$> getSymbolicLinkStatus f
|
||||||
|
|
26
Setup.hs
26
Setup.hs
|
@ -30,16 +30,16 @@ myPostInst _ (InstallFlags { installVerbosity }) pkg lbi = do
|
||||||
installGitAnnexShell dest verbosity pkg lbi
|
installGitAnnexShell dest verbosity pkg lbi
|
||||||
installManpages dest verbosity pkg lbi
|
installManpages dest verbosity pkg lbi
|
||||||
installDesktopFile dest verbosity pkg lbi
|
installDesktopFile dest verbosity pkg lbi
|
||||||
where
|
where
|
||||||
dest = NoCopyDest
|
dest = NoCopyDest
|
||||||
verbosity = fromFlag installVerbosity
|
verbosity = fromFlag installVerbosity
|
||||||
|
|
||||||
installGitAnnexShell :: CopyDest -> Verbosity -> PackageDescription -> LocalBuildInfo -> IO ()
|
installGitAnnexShell :: CopyDest -> Verbosity -> PackageDescription -> LocalBuildInfo -> IO ()
|
||||||
installGitAnnexShell copyDest verbosity pkg lbi =
|
installGitAnnexShell copyDest verbosity pkg lbi =
|
||||||
rawSystemExit verbosity "ln"
|
rawSystemExit verbosity "ln"
|
||||||
["-sf", "git-annex", dstBinDir </> "git-annex-shell"]
|
["-sf", "git-annex", dstBinDir </> "git-annex-shell"]
|
||||||
where
|
where
|
||||||
dstBinDir = bindir $ absoluteInstallDirs pkg lbi copyDest
|
dstBinDir = bindir $ absoluteInstallDirs pkg lbi copyDest
|
||||||
|
|
||||||
{- See http://www.haskell.org/haskellwiki/Cabal/Developer-FAQ#Installing_manpages
|
{- See http://www.haskell.org/haskellwiki/Cabal/Developer-FAQ#Installing_manpages
|
||||||
-
|
-
|
||||||
|
@ -49,15 +49,15 @@ installGitAnnexShell copyDest verbosity pkg lbi =
|
||||||
installManpages :: CopyDest -> Verbosity -> PackageDescription -> LocalBuildInfo -> IO ()
|
installManpages :: CopyDest -> Verbosity -> PackageDescription -> LocalBuildInfo -> IO ()
|
||||||
installManpages copyDest verbosity pkg lbi =
|
installManpages copyDest verbosity pkg lbi =
|
||||||
installOrdinaryFiles verbosity dstManDir =<< srcManpages
|
installOrdinaryFiles verbosity dstManDir =<< srcManpages
|
||||||
where
|
where
|
||||||
dstManDir = mandir (absoluteInstallDirs pkg lbi copyDest) </> "man1"
|
dstManDir = mandir (absoluteInstallDirs pkg lbi copyDest) </> "man1"
|
||||||
srcManpages = zip (repeat srcManDir)
|
srcManpages = zip (repeat srcManDir)
|
||||||
<$> filterM doesFileExist manpages
|
<$> filterM doesFileExist manpages
|
||||||
srcManDir = ""
|
srcManDir = ""
|
||||||
manpages = ["git-annex.1", "git-annex-shell.1"]
|
manpages = ["git-annex.1", "git-annex-shell.1"]
|
||||||
|
|
||||||
installDesktopFile :: CopyDest -> Verbosity -> PackageDescription -> LocalBuildInfo -> IO ()
|
installDesktopFile :: CopyDest -> Verbosity -> PackageDescription -> LocalBuildInfo -> IO ()
|
||||||
installDesktopFile copyDest verbosity pkg lbi =
|
installDesktopFile copyDest verbosity pkg lbi =
|
||||||
InstallDesktopFile.install $ dstBinDir </> "git-annex"
|
InstallDesktopFile.install $ dstBinDir </> "git-annex"
|
||||||
where
|
where
|
||||||
dstBinDir = bindir $ absoluteInstallDirs pkg lbi copyDest
|
dstBinDir = bindir $ absoluteInstallDirs pkg lbi copyDest
|
||||||
|
|
40
Types/Key.hs
40
Types/Key.hs
|
@ -46,33 +46,33 @@ fieldSep = '-'
|
||||||
key2file :: Key -> FilePath
|
key2file :: Key -> FilePath
|
||||||
key2file Key { keyBackendName = b, keySize = s, keyMtime = m, keyName = n } =
|
key2file Key { keyBackendName = b, keySize = s, keyMtime = m, keyName = n } =
|
||||||
b +++ ('s' ?: s) +++ ('m' ?: m) +++ (fieldSep : n)
|
b +++ ('s' ?: s) +++ ('m' ?: m) +++ (fieldSep : n)
|
||||||
where
|
where
|
||||||
"" +++ y = y
|
"" +++ y = y
|
||||||
x +++ "" = x
|
x +++ "" = x
|
||||||
x +++ y = x ++ fieldSep:y
|
x +++ y = x ++ fieldSep:y
|
||||||
c ?: (Just v) = c : show v
|
c ?: (Just v) = c : show v
|
||||||
_ ?: _ = ""
|
_ ?: _ = ""
|
||||||
|
|
||||||
file2key :: FilePath -> Maybe Key
|
file2key :: FilePath -> Maybe Key
|
||||||
file2key s = if key == Just stubKey then Nothing else key
|
file2key s = if key == Just stubKey then Nothing else key
|
||||||
where
|
where
|
||||||
key = startbackend stubKey s
|
key = startbackend stubKey s
|
||||||
|
|
||||||
startbackend k v = sepfield k v addbackend
|
startbackend k v = sepfield k v addbackend
|
||||||
|
|
||||||
sepfield k v a = case span (/= fieldSep) v of
|
sepfield k v a = case span (/= fieldSep) v of
|
||||||
(v', _:r) -> findfields r $ a k v'
|
(v', _:r) -> findfields r $ a k v'
|
||||||
_ -> Nothing
|
_ -> Nothing
|
||||||
|
|
||||||
findfields (c:v) (Just k)
|
findfields (c:v) (Just k)
|
||||||
| c == fieldSep = Just $ k { keyName = v }
|
| c == fieldSep = Just $ k { keyName = v }
|
||||||
| otherwise = sepfield k v $ addfield c
|
| otherwise = sepfield k v $ addfield c
|
||||||
findfields _ v = v
|
findfields _ v = v
|
||||||
|
|
||||||
addbackend k v = Just k { keyBackendName = v }
|
addbackend k v = Just k { keyBackendName = v }
|
||||||
addfield 's' k v = Just k { keySize = readish v }
|
addfield 's' k v = Just k { keySize = readish v }
|
||||||
addfield 'm' k v = Just k { keyMtime = readish v }
|
addfield 'm' k v = Just k { keyMtime = readish v }
|
||||||
addfield _ _ _ = Nothing
|
addfield _ _ _ = Nothing
|
||||||
|
|
||||||
prop_idempotent_key_encode :: Key -> Bool
|
prop_idempotent_key_encode :: Key -> Bool
|
||||||
prop_idempotent_key_encode k = Just k == (file2key . key2file) k
|
prop_idempotent_key_encode k = Just k == (file2key . key2file) k
|
||||||
|
|
10
Upgrade.hs
10
Upgrade.hs
|
@ -15,8 +15,8 @@ import qualified Upgrade.V2
|
||||||
|
|
||||||
upgrade :: Annex Bool
|
upgrade :: Annex Bool
|
||||||
upgrade = go =<< getVersion
|
upgrade = go =<< getVersion
|
||||||
where
|
where
|
||||||
go (Just "0") = Upgrade.V0.upgrade
|
go (Just "0") = Upgrade.V0.upgrade
|
||||||
go (Just "1") = Upgrade.V1.upgrade
|
go (Just "1") = Upgrade.V1.upgrade
|
||||||
go (Just "2") = Upgrade.V2.upgrade
|
go (Just "2") = Upgrade.V2.upgrade
|
||||||
go _ = return True
|
go _ = return True
|
||||||
|
|
|
@ -40,10 +40,10 @@ getKeysPresent0 dir = ifM (liftIO $ doesDirectoryExist dir)
|
||||||
<$> (filterM present =<< getDirectoryContents dir)
|
<$> (filterM present =<< getDirectoryContents dir)
|
||||||
, return []
|
, return []
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
present d = do
|
present d = do
|
||||||
result <- tryIO $
|
result <- tryIO $
|
||||||
getFileStatus $ dir ++ "/" ++ takeFileName d
|
getFileStatus $ dir ++ "/" ++ takeFileName d
|
||||||
case result of
|
case result of
|
||||||
Right s -> return $ isRegularFile s
|
Right s -> return $ isRegularFile s
|
||||||
Left _ -> return False
|
Left _ -> return False
|
||||||
|
|
164
Upgrade/V1.hs
164
Upgrade/V1.hs
|
@ -70,14 +70,14 @@ moveContent = do
|
||||||
showAction "moving content"
|
showAction "moving content"
|
||||||
files <- getKeyFilesPresent1
|
files <- getKeyFilesPresent1
|
||||||
forM_ files move
|
forM_ files move
|
||||||
where
|
where
|
||||||
move f = do
|
move f = do
|
||||||
let k = fileKey1 (takeFileName f)
|
let k = fileKey1 (takeFileName f)
|
||||||
let d = parentDir f
|
let d = parentDir f
|
||||||
liftIO $ allowWrite d
|
liftIO $ allowWrite d
|
||||||
liftIO $ allowWrite f
|
liftIO $ allowWrite f
|
||||||
moveAnnex k f
|
moveAnnex k f
|
||||||
liftIO $ removeDirectory d
|
liftIO $ removeDirectory d
|
||||||
|
|
||||||
updateSymlinks :: Annex ()
|
updateSymlinks :: Annex ()
|
||||||
updateSymlinks = do
|
updateSymlinks = do
|
||||||
|
@ -86,54 +86,54 @@ updateSymlinks = do
|
||||||
(files, cleanup) <- inRepo $ LsFiles.inRepo [top]
|
(files, cleanup) <- inRepo $ LsFiles.inRepo [top]
|
||||||
forM_ files fixlink
|
forM_ files fixlink
|
||||||
void $ liftIO cleanup
|
void $ liftIO cleanup
|
||||||
where
|
where
|
||||||
fixlink f = do
|
fixlink f = do
|
||||||
r <- lookupFile1 f
|
r <- lookupFile1 f
|
||||||
case r of
|
case r of
|
||||||
Nothing -> noop
|
Nothing -> noop
|
||||||
Just (k, _) -> do
|
Just (k, _) -> do
|
||||||
link <- calcGitLink f k
|
link <- calcGitLink f k
|
||||||
liftIO $ removeFile f
|
liftIO $ removeFile f
|
||||||
liftIO $ createSymbolicLink link f
|
liftIO $ createSymbolicLink link f
|
||||||
Annex.Queue.addCommand "add" [Param "--"] [f]
|
Annex.Queue.addCommand "add" [Param "--"] [f]
|
||||||
|
|
||||||
moveLocationLogs :: Annex ()
|
moveLocationLogs :: Annex ()
|
||||||
moveLocationLogs = do
|
moveLocationLogs = do
|
||||||
showAction "moving location logs"
|
showAction "moving location logs"
|
||||||
logkeys <- oldlocationlogs
|
logkeys <- oldlocationlogs
|
||||||
forM_ logkeys move
|
forM_ logkeys move
|
||||||
where
|
where
|
||||||
oldlocationlogs = do
|
oldlocationlogs = do
|
||||||
dir <- fromRepo Upgrade.V2.gitStateDir
|
dir <- fromRepo Upgrade.V2.gitStateDir
|
||||||
ifM (liftIO $ doesDirectoryExist dir)
|
ifM (liftIO $ doesDirectoryExist dir)
|
||||||
( mapMaybe oldlog2key
|
( mapMaybe oldlog2key
|
||||||
<$> (liftIO $ getDirectoryContents dir)
|
<$> (liftIO $ getDirectoryContents dir)
|
||||||
, return []
|
, return []
|
||||||
)
|
)
|
||||||
move (l, k) = do
|
move (l, k) = do
|
||||||
dest <- fromRepo $ logFile2 k
|
dest <- fromRepo $ logFile2 k
|
||||||
dir <- fromRepo Upgrade.V2.gitStateDir
|
dir <- fromRepo Upgrade.V2.gitStateDir
|
||||||
let f = dir </> l
|
let f = dir </> l
|
||||||
liftIO $ createDirectoryIfMissing True (parentDir dest)
|
liftIO $ createDirectoryIfMissing True (parentDir dest)
|
||||||
-- could just git mv, but this way deals with
|
-- could just git mv, but this way deals with
|
||||||
-- log files that are not checked into git,
|
-- log files that are not checked into git,
|
||||||
-- as well as merging with already upgraded
|
-- as well as merging with already upgraded
|
||||||
-- logs that have been pulled from elsewhere
|
-- logs that have been pulled from elsewhere
|
||||||
old <- liftIO $ readLog1 f
|
old <- liftIO $ readLog1 f
|
||||||
new <- liftIO $ readLog1 dest
|
new <- liftIO $ readLog1 dest
|
||||||
liftIO $ writeLog1 dest (old++new)
|
liftIO $ writeLog1 dest (old++new)
|
||||||
Annex.Queue.addCommand "add" [Param "--"] [dest]
|
Annex.Queue.addCommand "add" [Param "--"] [dest]
|
||||||
Annex.Queue.addCommand "add" [Param "--"] [f]
|
Annex.Queue.addCommand "add" [Param "--"] [f]
|
||||||
Annex.Queue.addCommand "rm" [Param "--quiet", Param "-f", Param "--"] [f]
|
Annex.Queue.addCommand "rm" [Param "--quiet", Param "-f", Param "--"] [f]
|
||||||
|
|
||||||
oldlog2key :: FilePath -> Maybe (FilePath, Key)
|
oldlog2key :: FilePath -> Maybe (FilePath, Key)
|
||||||
oldlog2key l
|
oldlog2key l
|
||||||
| drop len l == ".log" && sane = Just (l, k)
|
| drop len l == ".log" && sane = Just (l, k)
|
||||||
| otherwise = Nothing
|
| otherwise = Nothing
|
||||||
where
|
where
|
||||||
len = length l - 4
|
len = length l - 4
|
||||||
k = readKey1 (take len l)
|
k = readKey1 (take len l)
|
||||||
sane = (not . null $ keyName k) && (not . null $ keyBackendName k)
|
sane = (not . null $ keyName k) && (not . null $ keyBackendName k)
|
||||||
|
|
||||||
-- WORM backend keys: "WORM:mtime:size:filename"
|
-- WORM backend keys: "WORM:mtime:size:filename"
|
||||||
-- all the rest: "backend:key"
|
-- all the rest: "backend:key"
|
||||||
|
@ -150,25 +150,25 @@ readKey1 v
|
||||||
, keySize = s
|
, keySize = s
|
||||||
, keyMtime = t
|
, keyMtime = t
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
bits = split ":" v
|
bits = split ":" v
|
||||||
b = Prelude.head bits
|
b = Prelude.head bits
|
||||||
n = join ":" $ drop (if wormy then 3 else 1) bits
|
n = join ":" $ drop (if wormy then 3 else 1) bits
|
||||||
t = if wormy
|
t = if wormy
|
||||||
then Just (Prelude.read (bits !! 1) :: EpochTime)
|
then Just (Prelude.read (bits !! 1) :: EpochTime)
|
||||||
else Nothing
|
else Nothing
|
||||||
s = if wormy
|
s = if wormy
|
||||||
then Just (Prelude.read (bits !! 2) :: Integer)
|
then Just (Prelude.read (bits !! 2) :: Integer)
|
||||||
else Nothing
|
else Nothing
|
||||||
wormy = Prelude.head bits == "WORM"
|
wormy = Prelude.head bits == "WORM"
|
||||||
mixup = wormy && isUpper (Prelude.head $ bits !! 1)
|
mixup = wormy && isUpper (Prelude.head $ bits !! 1)
|
||||||
|
|
||||||
showKey1 :: Key -> String
|
showKey1 :: Key -> String
|
||||||
showKey1 Key { keyName = n , keyBackendName = b, keySize = s, keyMtime = t } =
|
showKey1 Key { keyName = n , keyBackendName = b, keySize = s, keyMtime = t } =
|
||||||
join ":" $ filter (not . null) [b, showifhere t, showifhere s, n]
|
join ":" $ filter (not . null) [b, showifhere t, showifhere s, n]
|
||||||
where
|
where
|
||||||
showifhere Nothing = ""
|
showifhere Nothing = ""
|
||||||
showifhere (Just v) = show v
|
showifhere (Just v) = show v
|
||||||
|
|
||||||
keyFile1 :: Key -> FilePath
|
keyFile1 :: Key -> FilePath
|
||||||
keyFile1 key = replace "/" "%" $ replace "%" "&s" $ replace "&" "&a" $ showKey1 key
|
keyFile1 key = replace "/" "%" $ replace "%" "&s" $ replace "&" "&a" $ showKey1 key
|
||||||
|
@ -190,21 +190,21 @@ lookupFile1 file = do
|
||||||
case tl of
|
case tl of
|
||||||
Left _ -> return Nothing
|
Left _ -> return Nothing
|
||||||
Right l -> makekey l
|
Right l -> makekey l
|
||||||
where
|
where
|
||||||
getsymlink = takeFileName <$> readSymbolicLink file
|
getsymlink = takeFileName <$> readSymbolicLink file
|
||||||
makekey l = case maybeLookupBackendName bname of
|
makekey l = case maybeLookupBackendName bname of
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
unless (null kname || null bname ||
|
unless (null kname || null bname ||
|
||||||
not (isLinkToAnnex l)) $
|
not (isLinkToAnnex l)) $
|
||||||
warning skip
|
warning skip
|
||||||
return Nothing
|
return Nothing
|
||||||
Just backend -> return $ Just (k, backend)
|
Just backend -> return $ Just (k, backend)
|
||||||
where
|
where
|
||||||
k = fileKey1 l
|
k = fileKey1 l
|
||||||
bname = keyBackendName k
|
bname = keyBackendName k
|
||||||
kname = keyName k
|
kname = keyName k
|
||||||
skip = "skipping " ++ file ++
|
skip = "skipping " ++ file ++
|
||||||
" (unknown backend " ++ bname ++ ")"
|
" (unknown backend " ++ bname ++ ")"
|
||||||
|
|
||||||
getKeyFilesPresent1 :: Annex [FilePath]
|
getKeyFilesPresent1 :: Annex [FilePath]
|
||||||
getKeyFilesPresent1 = getKeyFilesPresent1' =<< fromRepo gitAnnexObjectDir
|
getKeyFilesPresent1 = getKeyFilesPresent1' =<< fromRepo gitAnnexObjectDir
|
||||||
|
@ -217,12 +217,12 @@ getKeyFilesPresent1' dir =
|
||||||
liftIO $ filterM present files
|
liftIO $ filterM present files
|
||||||
, return []
|
, return []
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
present f = do
|
present f = do
|
||||||
result <- tryIO $ getFileStatus f
|
result <- tryIO $ getFileStatus f
|
||||||
case result of
|
case result of
|
||||||
Right s -> return $ isRegularFile s
|
Right s -> return $ isRegularFile s
|
||||||
Left _ -> return False
|
Left _ -> return False
|
||||||
|
|
||||||
logFile1 :: Git.Repo -> Key -> String
|
logFile1 :: Git.Repo -> Key -> String
|
||||||
logFile1 repo key = Upgrade.V2.gitStateDir repo ++ keyFile1 key ++ ".log"
|
logFile1 repo key = Upgrade.V2.gitStateDir repo ++ keyFile1 key ++ ".log"
|
||||||
|
|
|
@ -70,10 +70,10 @@ locationLogs = do
|
||||||
levelb <- mapM tryDirContents levela
|
levelb <- mapM tryDirContents levela
|
||||||
files <- mapM tryDirContents (concat levelb)
|
files <- mapM tryDirContents (concat levelb)
|
||||||
return $ mapMaybe islogfile (concat files)
|
return $ mapMaybe islogfile (concat files)
|
||||||
where
|
where
|
||||||
tryDirContents d = catchDefaultIO [] $ dirContents d
|
tryDirContents d = catchDefaultIO [] $ dirContents d
|
||||||
islogfile f = maybe Nothing (\k -> Just (k, f)) $
|
islogfile f = maybe Nothing (\k -> Just (k, f)) $
|
||||||
logFileKey $ takeFileName f
|
logFileKey $ takeFileName f
|
||||||
|
|
||||||
inject :: FilePath -> FilePath -> Annex ()
|
inject :: FilePath -> FilePath -> Annex ()
|
||||||
inject source dest = do
|
inject source dest = do
|
||||||
|
|
12
git-annex.hs
12
git-annex.hs
|
@ -13,9 +13,9 @@ import qualified GitAnnexShell
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = run =<< getProgName
|
main = run =<< getProgName
|
||||||
where
|
where
|
||||||
run n
|
run n
|
||||||
| isshell n = go GitAnnexShell.run
|
| isshell n = go GitAnnexShell.run
|
||||||
| otherwise = go GitAnnex.run
|
| otherwise = go GitAnnex.run
|
||||||
isshell n = takeFileName n == "git-annex-shell"
|
isshell n = takeFileName n == "git-annex-shell"
|
||||||
go a = a =<< getArgs
|
go a = a =<< getArgs
|
||||||
|
|
386
test.hs
386
test.hs
|
@ -133,45 +133,45 @@ blackbox = TestLabel "blackbox" $ TestList
|
||||||
test_init :: Test
|
test_init :: Test
|
||||||
test_init = "git-annex init" ~: TestCase $ innewrepo $ do
|
test_init = "git-annex init" ~: TestCase $ innewrepo $ do
|
||||||
git_annex "init" [reponame] @? "init failed"
|
git_annex "init" [reponame] @? "init failed"
|
||||||
where
|
where
|
||||||
reponame = "test repo"
|
reponame = "test repo"
|
||||||
|
|
||||||
test_add :: Test
|
test_add :: Test
|
||||||
test_add = "git-annex add" ~: TestList [basic, sha1dup, subdirs]
|
test_add = "git-annex add" ~: TestList [basic, sha1dup, subdirs]
|
||||||
where
|
where
|
||||||
-- this test case runs in the main repo, to set up a basic
|
-- this test case runs in the main repo, to set up a basic
|
||||||
-- annexed file that later tests will use
|
-- annexed file that later tests will use
|
||||||
basic = TestCase $ inmainrepo $ do
|
basic = TestCase $ inmainrepo $ do
|
||||||
writeFile annexedfile $ content annexedfile
|
writeFile annexedfile $ content annexedfile
|
||||||
git_annex "add" [annexedfile] @? "add failed"
|
git_annex "add" [annexedfile] @? "add failed"
|
||||||
annexed_present annexedfile
|
annexed_present annexedfile
|
||||||
writeFile sha1annexedfile $ content sha1annexedfile
|
writeFile sha1annexedfile $ content sha1annexedfile
|
||||||
git_annex "add" [sha1annexedfile, "--backend=SHA1"] @? "add with SHA1 failed"
|
git_annex "add" [sha1annexedfile, "--backend=SHA1"] @? "add with SHA1 failed"
|
||||||
annexed_present sha1annexedfile
|
annexed_present sha1annexedfile
|
||||||
checkbackend sha1annexedfile backendSHA1
|
checkbackend sha1annexedfile backendSHA1
|
||||||
writeFile wormannexedfile $ content wormannexedfile
|
writeFile wormannexedfile $ content wormannexedfile
|
||||||
git_annex "add" [wormannexedfile, "--backend=WORM"] @? "add with WORM failed"
|
git_annex "add" [wormannexedfile, "--backend=WORM"] @? "add with WORM failed"
|
||||||
annexed_present wormannexedfile
|
annexed_present wormannexedfile
|
||||||
checkbackend wormannexedfile backendWORM
|
checkbackend wormannexedfile backendWORM
|
||||||
boolSystem "git" [Params "rm --force -q", File wormannexedfile] @? "git rm failed"
|
boolSystem "git" [Params "rm --force -q", File wormannexedfile] @? "git rm failed"
|
||||||
writeFile ingitfile $ content ingitfile
|
writeFile ingitfile $ content ingitfile
|
||||||
boolSystem "git" [Param "add", File ingitfile] @? "git add failed"
|
boolSystem "git" [Param "add", File ingitfile] @? "git add failed"
|
||||||
boolSystem "git" [Params "commit -q -a -m commit"] @? "git commit failed"
|
boolSystem "git" [Params "commit -q -a -m commit"] @? "git commit failed"
|
||||||
git_annex "add" [ingitfile] @? "add ingitfile should be no-op"
|
git_annex "add" [ingitfile] @? "add ingitfile should be no-op"
|
||||||
unannexed ingitfile
|
unannexed ingitfile
|
||||||
sha1dup = TestCase $ intmpclonerepo $ do
|
sha1dup = TestCase $ intmpclonerepo $ do
|
||||||
writeFile sha1annexedfiledup $ content sha1annexedfiledup
|
writeFile sha1annexedfiledup $ content sha1annexedfiledup
|
||||||
git_annex "add" [sha1annexedfiledup, "--backend=SHA1"] @? "add of second file with same SHA1 failed"
|
git_annex "add" [sha1annexedfiledup, "--backend=SHA1"] @? "add of second file with same SHA1 failed"
|
||||||
annexed_present sha1annexedfiledup
|
annexed_present sha1annexedfiledup
|
||||||
annexed_present sha1annexedfile
|
annexed_present sha1annexedfile
|
||||||
subdirs = TestCase $ intmpclonerepo $ do
|
subdirs = TestCase $ intmpclonerepo $ do
|
||||||
createDirectory "dir"
|
createDirectory "dir"
|
||||||
writeFile "dir/foo" $ content annexedfile
|
writeFile "dir/foo" $ content annexedfile
|
||||||
git_annex "add" ["dir"] @? "add of subdir failed"
|
git_annex "add" ["dir"] @? "add of subdir failed"
|
||||||
createDirectory "dir2"
|
createDirectory "dir2"
|
||||||
writeFile "dir2/foo" $ content annexedfile
|
writeFile "dir2/foo" $ content annexedfile
|
||||||
changeWorkingDirectory "dir"
|
changeWorkingDirectory "dir"
|
||||||
git_annex "add" ["../dir2"] @? "add of ../subdir failed"
|
git_annex "add" ["../dir2"] @? "add of ../subdir failed"
|
||||||
|
|
||||||
test_reinject :: Test
|
test_reinject :: Test
|
||||||
test_reinject = "git-annex reinject/fromkey" ~: TestCase $ intmpclonerepo $ do
|
test_reinject = "git-annex reinject/fromkey" ~: TestCase $ intmpclonerepo $ do
|
||||||
|
@ -183,53 +183,53 @@ test_reinject = "git-annex reinject/fromkey" ~: TestCase $ intmpclonerepo $ do
|
||||||
git_annex "reinject" [tmp, sha1annexedfile] @? "reinject failed"
|
git_annex "reinject" [tmp, sha1annexedfile] @? "reinject failed"
|
||||||
git_annex "fromkey" [key, sha1annexedfiledup] @? "fromkey failed"
|
git_annex "fromkey" [key, sha1annexedfiledup] @? "fromkey failed"
|
||||||
annexed_present sha1annexedfiledup
|
annexed_present sha1annexedfiledup
|
||||||
where
|
where
|
||||||
tmp = "tmpfile"
|
tmp = "tmpfile"
|
||||||
|
|
||||||
test_unannex :: Test
|
test_unannex :: Test
|
||||||
test_unannex = "git-annex unannex" ~: TestList [nocopy, withcopy]
|
test_unannex = "git-annex unannex" ~: TestList [nocopy, withcopy]
|
||||||
where
|
where
|
||||||
nocopy = "no content" ~: intmpclonerepo $ do
|
nocopy = "no content" ~: intmpclonerepo $ do
|
||||||
annexed_notpresent annexedfile
|
annexed_notpresent annexedfile
|
||||||
git_annex "unannex" [annexedfile] @? "unannex failed with no copy"
|
git_annex "unannex" [annexedfile] @? "unannex failed with no copy"
|
||||||
annexed_notpresent annexedfile
|
annexed_notpresent annexedfile
|
||||||
withcopy = "with content" ~: intmpclonerepo $ do
|
withcopy = "with content" ~: intmpclonerepo $ do
|
||||||
git_annex "get" [annexedfile] @? "get failed"
|
git_annex "get" [annexedfile] @? "get failed"
|
||||||
annexed_present annexedfile
|
annexed_present annexedfile
|
||||||
git_annex "unannex" [annexedfile, sha1annexedfile] @? "unannex failed"
|
git_annex "unannex" [annexedfile, sha1annexedfile] @? "unannex failed"
|
||||||
unannexed annexedfile
|
unannexed annexedfile
|
||||||
git_annex "unannex" [annexedfile] @? "unannex failed on non-annexed file"
|
git_annex "unannex" [annexedfile] @? "unannex failed on non-annexed file"
|
||||||
unannexed annexedfile
|
unannexed annexedfile
|
||||||
git_annex "unannex" [ingitfile] @? "unannex ingitfile should be no-op"
|
git_annex "unannex" [ingitfile] @? "unannex ingitfile should be no-op"
|
||||||
unannexed ingitfile
|
unannexed ingitfile
|
||||||
|
|
||||||
test_drop :: Test
|
test_drop :: Test
|
||||||
test_drop = "git-annex drop" ~: TestList [noremote, withremote, untrustedremote]
|
test_drop = "git-annex drop" ~: TestList [noremote, withremote, untrustedremote]
|
||||||
where
|
where
|
||||||
noremote = "no remotes" ~: TestCase $ intmpclonerepo $ do
|
noremote = "no remotes" ~: TestCase $ intmpclonerepo $ do
|
||||||
git_annex "get" [annexedfile] @? "get failed"
|
git_annex "get" [annexedfile] @? "get failed"
|
||||||
boolSystem "git" [Params "remote rm origin"]
|
boolSystem "git" [Params "remote rm origin"]
|
||||||
@? "git remote rm origin failed"
|
@? "git remote rm origin failed"
|
||||||
not <$> git_annex "drop" [annexedfile] @? "drop wrongly succeeded with no known copy of file"
|
not <$> git_annex "drop" [annexedfile] @? "drop wrongly succeeded with no known copy of file"
|
||||||
annexed_present annexedfile
|
annexed_present annexedfile
|
||||||
git_annex "drop" ["--force", annexedfile] @? "drop --force failed"
|
git_annex "drop" ["--force", annexedfile] @? "drop --force failed"
|
||||||
annexed_notpresent annexedfile
|
annexed_notpresent annexedfile
|
||||||
git_annex "drop" [annexedfile] @? "drop of dropped file failed"
|
git_annex "drop" [annexedfile] @? "drop of dropped file failed"
|
||||||
git_annex "drop" [ingitfile] @? "drop ingitfile should be no-op"
|
git_annex "drop" [ingitfile] @? "drop ingitfile should be no-op"
|
||||||
unannexed ingitfile
|
unannexed ingitfile
|
||||||
withremote = "with remote" ~: TestCase $ intmpclonerepo $ do
|
withremote = "with remote" ~: TestCase $ intmpclonerepo $ do
|
||||||
git_annex "get" [annexedfile] @? "get failed"
|
git_annex "get" [annexedfile] @? "get failed"
|
||||||
annexed_present annexedfile
|
annexed_present annexedfile
|
||||||
git_annex "drop" [annexedfile] @? "drop failed though origin has copy"
|
git_annex "drop" [annexedfile] @? "drop failed though origin has copy"
|
||||||
annexed_notpresent annexedfile
|
annexed_notpresent annexedfile
|
||||||
inmainrepo $ annexed_present annexedfile
|
inmainrepo $ annexed_present annexedfile
|
||||||
untrustedremote = "untrusted remote" ~: TestCase $ intmpclonerepo $ do
|
untrustedremote = "untrusted remote" ~: TestCase $ intmpclonerepo $ do
|
||||||
git_annex "untrust" ["origin"] @? "untrust of origin failed"
|
git_annex "untrust" ["origin"] @? "untrust of origin failed"
|
||||||
git_annex "get" [annexedfile] @? "get failed"
|
git_annex "get" [annexedfile] @? "get failed"
|
||||||
annexed_present annexedfile
|
annexed_present annexedfile
|
||||||
not <$> git_annex "drop" [annexedfile] @? "drop wrongly suceeded with only an untrusted copy of the file"
|
not <$> git_annex "drop" [annexedfile] @? "drop wrongly suceeded with only an untrusted copy of the file"
|
||||||
annexed_present annexedfile
|
annexed_present annexedfile
|
||||||
inmainrepo $ annexed_present annexedfile
|
inmainrepo $ annexed_present annexedfile
|
||||||
|
|
||||||
test_get :: Test
|
test_get :: Test
|
||||||
test_get = "git-annex get" ~: TestCase $ intmpclonerepo $ do
|
test_get = "git-annex get" ~: TestCase $ intmpclonerepo $ do
|
||||||
|
@ -326,27 +326,27 @@ test_lock = "git-annex unlock/lock" ~: intmpclonerepo $ do
|
||||||
|
|
||||||
test_edit :: Test
|
test_edit :: Test
|
||||||
test_edit = "git-annex edit/commit" ~: TestList [t False, t True]
|
test_edit = "git-annex edit/commit" ~: TestList [t False, t True]
|
||||||
where t precommit = TestCase $ intmpclonerepo $ do
|
where t precommit = TestCase $ intmpclonerepo $ do
|
||||||
git_annex "get" [annexedfile] @? "get of file failed"
|
git_annex "get" [annexedfile] @? "get of file failed"
|
||||||
annexed_present annexedfile
|
annexed_present annexedfile
|
||||||
git_annex "edit" [annexedfile] @? "edit failed"
|
git_annex "edit" [annexedfile] @? "edit failed"
|
||||||
unannexed annexedfile
|
unannexed annexedfile
|
||||||
changecontent annexedfile
|
changecontent annexedfile
|
||||||
if precommit
|
if precommit
|
||||||
then do
|
then do
|
||||||
-- pre-commit depends on the file being
|
-- pre-commit depends on the file being
|
||||||
-- staged, normally git commit does this
|
-- staged, normally git commit does this
|
||||||
boolSystem "git" [Param "add", File annexedfile]
|
boolSystem "git" [Param "add", File annexedfile]
|
||||||
@? "git add of edited file failed"
|
@? "git add of edited file failed"
|
||||||
git_annex "pre-commit" []
|
git_annex "pre-commit" []
|
||||||
@? "pre-commit failed"
|
@? "pre-commit failed"
|
||||||
else do
|
else do
|
||||||
boolSystem "git" [Params "commit -q -a -m contentchanged"]
|
boolSystem "git" [Params "commit -q -a -m contentchanged"]
|
||||||
@? "git commit of edited file failed"
|
@? "git commit of edited file failed"
|
||||||
runchecks [checklink, checkunwritable] annexedfile
|
runchecks [checklink, checkunwritable] annexedfile
|
||||||
c <- readFile annexedfile
|
c <- readFile annexedfile
|
||||||
assertEqual "content of modified file" c (changedcontent annexedfile)
|
assertEqual "content of modified file" c (changedcontent annexedfile)
|
||||||
not <$> git_annex "drop" [annexedfile] @? "drop wrongly succeeded with no known copy of modified file"
|
not <$> git_annex "drop" [annexedfile] @? "drop wrongly succeeded with no known copy of modified file"
|
||||||
|
|
||||||
test_fix :: Test
|
test_fix :: Test
|
||||||
test_fix = "git-annex fix" ~: intmpclonerepo $ do
|
test_fix = "git-annex fix" ~: intmpclonerepo $ do
|
||||||
|
@ -364,9 +364,9 @@ test_fix = "git-annex fix" ~: intmpclonerepo $ do
|
||||||
runchecks [checklink, checkunwritable] newfile
|
runchecks [checklink, checkunwritable] newfile
|
||||||
c <- readFile newfile
|
c <- readFile newfile
|
||||||
assertEqual "content of moved file" c (content annexedfile)
|
assertEqual "content of moved file" c (content annexedfile)
|
||||||
where
|
where
|
||||||
subdir = "s"
|
subdir = "s"
|
||||||
newfile = subdir ++ "/" ++ annexedfile
|
newfile = subdir ++ "/" ++ annexedfile
|
||||||
|
|
||||||
test_trust :: Test
|
test_trust :: Test
|
||||||
test_trust = "git-annex trust/untrust/semitrust/dead" ~: intmpclonerepo $ do
|
test_trust = "git-annex trust/untrust/semitrust/dead" ~: intmpclonerepo $ do
|
||||||
|
@ -386,89 +386,89 @@ test_trust = "git-annex trust/untrust/semitrust/dead" ~: intmpclonerepo $ do
|
||||||
trustcheck Logs.Trust.SemiTrusted "semitrusted 1"
|
trustcheck Logs.Trust.SemiTrusted "semitrusted 1"
|
||||||
git_annex "semitrust" [repo] @? "semitrust of semitrusted failed"
|
git_annex "semitrust" [repo] @? "semitrust of semitrusted failed"
|
||||||
trustcheck Logs.Trust.SemiTrusted "semitrusted 2"
|
trustcheck Logs.Trust.SemiTrusted "semitrusted 2"
|
||||||
where
|
where
|
||||||
repo = "origin"
|
repo = "origin"
|
||||||
trustcheck expected msg = do
|
trustcheck expected msg = do
|
||||||
present <- annexeval $ do
|
present <- annexeval $ do
|
||||||
l <- Logs.Trust.trustGet expected
|
l <- Logs.Trust.trustGet expected
|
||||||
u <- Remote.nameToUUID repo
|
u <- Remote.nameToUUID repo
|
||||||
return $ u `elem` l
|
return $ u `elem` l
|
||||||
assertBool msg present
|
assertBool msg present
|
||||||
|
|
||||||
test_fsck :: Test
|
test_fsck :: Test
|
||||||
test_fsck = "git-annex fsck" ~: TestList [basicfsck, barefsck, withlocaluntrusted, withremoteuntrusted]
|
test_fsck = "git-annex fsck" ~: TestList [basicfsck, barefsck, withlocaluntrusted, withremoteuntrusted]
|
||||||
where
|
where
|
||||||
basicfsck = TestCase $ intmpclonerepo $ do
|
basicfsck = TestCase $ intmpclonerepo $ do
|
||||||
git_annex "fsck" [] @? "fsck failed"
|
git_annex "fsck" [] @? "fsck failed"
|
||||||
boolSystem "git" [Params "config annex.numcopies 2"] @? "git config failed"
|
boolSystem "git" [Params "config annex.numcopies 2"] @? "git config failed"
|
||||||
fsck_should_fail "numcopies unsatisfied"
|
fsck_should_fail "numcopies unsatisfied"
|
||||||
boolSystem "git" [Params "config annex.numcopies 1"] @? "git config failed"
|
boolSystem "git" [Params "config annex.numcopies 1"] @? "git config failed"
|
||||||
corrupt annexedfile
|
corrupt annexedfile
|
||||||
corrupt sha1annexedfile
|
corrupt sha1annexedfile
|
||||||
barefsck = TestCase $ intmpbareclonerepo $ do
|
barefsck = TestCase $ intmpbareclonerepo $ do
|
||||||
git_annex "fsck" [] @? "fsck failed"
|
git_annex "fsck" [] @? "fsck failed"
|
||||||
withlocaluntrusted = TestCase $ intmpclonerepo $ do
|
withlocaluntrusted = TestCase $ intmpclonerepo $ do
|
||||||
git_annex "get" [annexedfile] @? "get failed"
|
git_annex "get" [annexedfile] @? "get failed"
|
||||||
git_annex "untrust" ["origin"] @? "untrust of origin repo failed"
|
git_annex "untrust" ["origin"] @? "untrust of origin repo failed"
|
||||||
git_annex "untrust" ["."] @? "untrust of current repo failed"
|
git_annex "untrust" ["."] @? "untrust of current repo failed"
|
||||||
fsck_should_fail "content only available in untrusted (current) repository"
|
fsck_should_fail "content only available in untrusted (current) repository"
|
||||||
git_annex "trust" ["."] @? "trust of current repo failed"
|
git_annex "trust" ["."] @? "trust of current repo failed"
|
||||||
git_annex "fsck" [annexedfile] @? "fsck failed on file present in trusted repo"
|
git_annex "fsck" [annexedfile] @? "fsck failed on file present in trusted repo"
|
||||||
withremoteuntrusted = TestCase $ intmpclonerepo $ do
|
withremoteuntrusted = TestCase $ intmpclonerepo $ do
|
||||||
boolSystem "git" [Params "config annex.numcopies 2"] @? "git config failed"
|
boolSystem "git" [Params "config annex.numcopies 2"] @? "git config failed"
|
||||||
git_annex "get" [annexedfile] @? "get failed"
|
git_annex "get" [annexedfile] @? "get failed"
|
||||||
git_annex "get" [sha1annexedfile] @? "get failed"
|
git_annex "get" [sha1annexedfile] @? "get failed"
|
||||||
git_annex "fsck" [] @? "fsck failed with numcopies=2 and 2 copies"
|
git_annex "fsck" [] @? "fsck failed with numcopies=2 and 2 copies"
|
||||||
git_annex "untrust" ["origin"] @? "untrust of origin failed"
|
git_annex "untrust" ["origin"] @? "untrust of origin failed"
|
||||||
fsck_should_fail "content not replicated to enough non-untrusted repositories"
|
fsck_should_fail "content not replicated to enough non-untrusted repositories"
|
||||||
|
|
||||||
corrupt f = do
|
corrupt f = do
|
||||||
git_annex "get" [f] @? "get of file failed"
|
git_annex "get" [f] @? "get of file failed"
|
||||||
Utility.FileMode.allowWrite f
|
Utility.FileMode.allowWrite f
|
||||||
writeFile f (changedcontent f)
|
writeFile f (changedcontent f)
|
||||||
not <$> git_annex "fsck" [] @? "fsck failed to fail with corrupted file content"
|
not <$> git_annex "fsck" [] @? "fsck failed to fail with corrupted file content"
|
||||||
git_annex "fsck" [] @? "fsck unexpectedly failed again; previous one did not fix problem with " ++ f
|
git_annex "fsck" [] @? "fsck unexpectedly failed again; previous one did not fix problem with " ++ f
|
||||||
fsck_should_fail m = do
|
fsck_should_fail m = do
|
||||||
not <$> git_annex "fsck" [] @? "fsck failed to fail with " ++ m
|
not <$> git_annex "fsck" [] @? "fsck failed to fail with " ++ m
|
||||||
|
|
||||||
test_migrate :: Test
|
test_migrate :: Test
|
||||||
test_migrate = "git-annex migrate" ~: TestList [t False, t True]
|
test_migrate = "git-annex migrate" ~: TestList [t False, t True]
|
||||||
where t usegitattributes = TestCase $ intmpclonerepo $ do
|
where t usegitattributes = TestCase $ intmpclonerepo $ do
|
||||||
annexed_notpresent annexedfile
|
annexed_notpresent annexedfile
|
||||||
annexed_notpresent sha1annexedfile
|
annexed_notpresent sha1annexedfile
|
||||||
git_annex "migrate" [annexedfile] @? "migrate of not present failed"
|
git_annex "migrate" [annexedfile] @? "migrate of not present failed"
|
||||||
git_annex "migrate" [sha1annexedfile] @? "migrate of not present failed"
|
git_annex "migrate" [sha1annexedfile] @? "migrate of not present failed"
|
||||||
git_annex "get" [annexedfile] @? "get of file failed"
|
git_annex "get" [annexedfile] @? "get of file failed"
|
||||||
git_annex "get" [sha1annexedfile] @? "get of file failed"
|
git_annex "get" [sha1annexedfile] @? "get of file failed"
|
||||||
annexed_present annexedfile
|
annexed_present annexedfile
|
||||||
annexed_present sha1annexedfile
|
annexed_present sha1annexedfile
|
||||||
if usegitattributes
|
if usegitattributes
|
||||||
then do
|
then do
|
||||||
writeFile ".gitattributes" $ "* annex.backend=SHA1"
|
writeFile ".gitattributes" $ "* annex.backend=SHA1"
|
||||||
git_annex "migrate" [sha1annexedfile]
|
git_annex "migrate" [sha1annexedfile]
|
||||||
@? "migrate sha1annexedfile failed"
|
@? "migrate sha1annexedfile failed"
|
||||||
git_annex "migrate" [annexedfile]
|
git_annex "migrate" [annexedfile]
|
||||||
@? "migrate annexedfile failed"
|
@? "migrate annexedfile failed"
|
||||||
else do
|
else do
|
||||||
git_annex "migrate" [sha1annexedfile, "--backend", "SHA1"]
|
git_annex "migrate" [sha1annexedfile, "--backend", "SHA1"]
|
||||||
@? "migrate sha1annexedfile failed"
|
@? "migrate sha1annexedfile failed"
|
||||||
git_annex "migrate" [annexedfile, "--backend", "SHA1"]
|
git_annex "migrate" [annexedfile, "--backend", "SHA1"]
|
||||||
@? "migrate annexedfile failed"
|
@? "migrate annexedfile failed"
|
||||||
annexed_present annexedfile
|
annexed_present annexedfile
|
||||||
annexed_present sha1annexedfile
|
annexed_present sha1annexedfile
|
||||||
checkbackend annexedfile backendSHA1
|
checkbackend annexedfile backendSHA1
|
||||||
checkbackend sha1annexedfile backendSHA1
|
checkbackend sha1annexedfile backendSHA1
|
||||||
|
|
||||||
-- check that reversing a migration works
|
-- check that reversing a migration works
|
||||||
writeFile ".gitattributes" $ "* annex.backend=SHA256"
|
writeFile ".gitattributes" $ "* annex.backend=SHA256"
|
||||||
git_annex "migrate" [sha1annexedfile]
|
git_annex "migrate" [sha1annexedfile]
|
||||||
@? "migrate sha1annexedfile failed"
|
@? "migrate sha1annexedfile failed"
|
||||||
git_annex "migrate" [annexedfile]
|
git_annex "migrate" [annexedfile]
|
||||||
@? "migrate annexedfile failed"
|
@? "migrate annexedfile failed"
|
||||||
annexed_present annexedfile
|
annexed_present annexedfile
|
||||||
annexed_present sha1annexedfile
|
annexed_present sha1annexedfile
|
||||||
checkbackend annexedfile backendSHA256
|
checkbackend annexedfile backendSHA256
|
||||||
checkbackend sha1annexedfile backendSHA256
|
checkbackend sha1annexedfile backendSHA256
|
||||||
|
|
||||||
test_unused :: Test
|
test_unused :: Test
|
||||||
test_unused = "git-annex unused/dropunused" ~: intmpclonerepo $ do
|
test_unused = "git-annex unused/dropunused" ~: intmpclonerepo $ do
|
||||||
|
@ -498,16 +498,16 @@ test_unused = "git-annex unused/dropunused" ~: intmpclonerepo $ do
|
||||||
checkunused [] "after dropunused"
|
checkunused [] "after dropunused"
|
||||||
git_annex "dropunused" ["10", "501"] @? "dropunused failed on bogus numbers"
|
git_annex "dropunused" ["10", "501"] @? "dropunused failed on bogus numbers"
|
||||||
|
|
||||||
where
|
where
|
||||||
checkunused expectedkeys desc = do
|
checkunused expectedkeys desc = do
|
||||||
git_annex "unused" [] @? "unused failed"
|
git_annex "unused" [] @? "unused failed"
|
||||||
unusedmap <- annexeval $ Logs.Unused.readUnusedLog ""
|
unusedmap <- annexeval $ Logs.Unused.readUnusedLog ""
|
||||||
let unusedkeys = M.elems unusedmap
|
let unusedkeys = M.elems unusedmap
|
||||||
assertEqual ("unused keys differ " ++ desc)
|
assertEqual ("unused keys differ " ++ desc)
|
||||||
(sort expectedkeys) (sort unusedkeys)
|
(sort expectedkeys) (sort unusedkeys)
|
||||||
findkey f = do
|
findkey f = do
|
||||||
r <- Backend.lookupFile f
|
r <- Backend.lookupFile f
|
||||||
return $ fst $ fromJust r
|
return $ fst $ fromJust r
|
||||||
|
|
||||||
test_describe :: Test
|
test_describe :: Test
|
||||||
test_describe = "git-annex describe" ~: intmpclonerepo $ do
|
test_describe = "git-annex describe" ~: intmpclonerepo $ do
|
||||||
|
@ -604,11 +604,11 @@ test_hook_remote = "git-annex hook remote" ~: intmpclonerepo $ do
|
||||||
annexed_present annexedfile
|
annexed_present annexedfile
|
||||||
not <$> git_annex "drop" [annexedfile, "--numcopies=2"] @? "drop failed to fail"
|
not <$> git_annex "drop" [annexedfile, "--numcopies=2"] @? "drop failed to fail"
|
||||||
annexed_present annexedfile
|
annexed_present annexedfile
|
||||||
where
|
where
|
||||||
dir = "dir"
|
dir = "dir"
|
||||||
loc = dir ++ "/$ANNEX_KEY"
|
loc = dir ++ "/$ANNEX_KEY"
|
||||||
git_config k v = boolSystem "git" [Param "config", Param k, Param v]
|
git_config k v = boolSystem "git" [Param "config", Param k, Param v]
|
||||||
@? "git config failed"
|
@? "git config failed"
|
||||||
|
|
||||||
test_directory_remote :: Test
|
test_directory_remote :: Test
|
||||||
test_directory_remote = "git-annex directory remote" ~: intmpclonerepo $ do
|
test_directory_remote = "git-annex directory remote" ~: intmpclonerepo $ do
|
||||||
|
@ -692,8 +692,8 @@ git_annex command params = do
|
||||||
case r of
|
case r of
|
||||||
Right _ -> return True
|
Right _ -> return True
|
||||||
Left _ -> return False
|
Left _ -> return False
|
||||||
where
|
where
|
||||||
run = GitAnnex.run (command:"-q":params)
|
run = GitAnnex.run (command:"-q":params)
|
||||||
|
|
||||||
{- Runs git-annex and returns its output. -}
|
{- Runs git-annex and returns its output. -}
|
||||||
git_annex_output :: String -> [String] -> IO String
|
git_annex_output :: String -> [String] -> IO String
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue