summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Command/RecvKey.hs18
-rw-r--r--Command/SendKey.hs20
-rw-r--r--GitAnnexShell.hs29
-rw-r--r--Utility/Rsync.hs23
4 files changed, 61 insertions, 29 deletions
diff --git a/Command/RecvKey.hs b/Command/RecvKey.hs
index c5ff4a9c8..c1c11a550 100644
--- a/Command/RecvKey.hs
+++ b/Command/RecvKey.hs
@@ -11,6 +11,7 @@ import Common.Annex
import Command
import CmdLine
import Annex.Content
+import Annex
import Utility.Rsync
import Logs.Transfer
import Command.SendKey (fieldTransfer)
@@ -19,6 +20,8 @@ import qualified Types.Key
import qualified Types.Backend
import qualified Backend
+import System.Console.GetOpt
+
def :: [Command]
def = [noCommit $ command "recvkey" paramKey seek
SectionPlumbing "runs rsync in server mode to receive content"]
@@ -40,13 +43,16 @@ start key = ifM (inAnnex key)
)
)
where
- go tmp = ifM (liftIO $ rsyncServerReceive tmp)
- ( ifM (isJust <$> Fields.getField Fields.direct)
- ( directcheck tmp
- , return True
+ go tmp = do
+ (opts,_,_) <- getOpt Permute rsyncSafeOptions <$>
+ maybe [] (split " ") <$> getField "RsyncOptions"
+ ifM (liftIO $ rsyncServerReceive (map Param opts) tmp)
+ ( ifM (isJust <$> Fields.getField Fields.direct)
+ ( directcheck tmp
+ , return True
+ )
+ , return False
)
- , return False
- )
{- If the sending repository uses direct mode, the file
- it sends could be modified as it's sending it. So check
- that the right size file was received, and that the key/value
diff --git a/Command/SendKey.hs b/Command/SendKey.hs
index 0a07dcece..42a0377a3 100644
--- a/Command/SendKey.hs
+++ b/Command/SendKey.hs
@@ -10,11 +10,14 @@ module Command.SendKey where
import Common.Annex
import Command
import Annex.Content
+import Annex
import Utility.Rsync
import Logs.Transfer
import qualified Fields
import Utility.Metered
+import System.Console.GetOpt
+
def :: [Command]
def = [noCommit $ command "sendkey" paramKey seek
SectionPlumbing "runs rsync in server mode to send content"]
@@ -23,13 +26,16 @@ seek :: [CommandSeek]
seek = [withKeys start]
start :: Key -> CommandStart
-start key = ifM (inAnnex key)
- ( fieldTransfer Upload key $ \_p ->
- sendAnnex key rollback $ liftIO . rsyncServerSend
- , do
- warning "requested key is not present"
- liftIO exitFailure
- )
+start key = do
+ (opts,_,_) <- getOpt Permute rsyncSafeOptions <$>
+ maybe [] (split " ") <$> getField "RsyncOptions"
+ ifM (inAnnex key)
+ ( fieldTransfer Upload key $ \_p ->
+ sendAnnex key rollback $ liftIO . rsyncServerSend (map Param opts)
+ , do
+ warning "requested key is not present"
+ liftIO exitFailure
+ )
where
{- No need to do any rollback; when sendAnnex fails, a nonzero
- exit will be propigated, and the remote will know the transfer
diff --git a/GitAnnexShell.hs b/GitAnnexShell.hs
index 2661d52e8..d641c6c3f 100644
--- a/GitAnnexShell.hs
+++ b/GitAnnexShell.hs
@@ -15,6 +15,7 @@ import qualified Git.Construct
import CmdLine
import Command
import Annex.UUID
+import Annex (setField)
import qualified Option
import Fields
import Utility.UserInfo
@@ -86,32 +87,38 @@ builtin :: String -> String -> [String] -> IO ()
builtin cmd dir params = do
checkNotReadOnly cmd
checkDirectory $ Just dir
- let (params', fieldparams) = partitionParams params
- let fields = filter checkField $ parseFields fieldparams
- dispatch False (cmd : params') cmds options fields header $
+ let (params', fieldparams, opts) = partitionParams params
+ fields = filter checkField $ parseFields fieldparams
+ cmds' = map (newcmd $ intercalate " " opts) cmds
+ dispatch False (cmd : params') cmds' options fields header $
Git.Construct.repoAbsPath dir >>= Git.Construct.fromAbsPath
+ where
+ newseek opts seek k = setField "RsyncOptions" opts >> seek k
+ newcmd opts c = c { cmdseek = map (newseek opts) (cmdseek c) }
external :: [String] -> IO ()
external params = do
{- Normal git-shell commands all have the directory as their last
- parameter. -}
let lastparam = lastMaybe =<< shellUnEscape <$> lastMaybe params
+ (params', _, _) = partitionParams params
checkDirectory lastparam
checkNotLimited
- unlessM (boolSystem "git-shell" $ map Param $ "-c":fst (partitionParams params)) $
+ unlessM (boolSystem "git-shell" $ map Param $ "-c":params') $
error "git-shell failed"
-{- Parameters between two -- markers are field settings, in the form:
+{- Split the input list into 3 groups separated with a double dash --.
+ - Parameters between two -- markers are field settings, in the form:
- field=value field=value
-
- - Parameters after the last -- are ignored, these tend to be passed by
- - rsync and not be useful.
+ - Parameters after the last -- are the command itself and its arguments e.g.,
+ - rsync --bandwidth=100.
-}
-partitionParams :: [String] -> ([String], [String])
+partitionParams :: [String] -> ([String], [String], [String])
partitionParams ps = case segment (== "--") ps of
- params:fieldparams:_ -> (params, fieldparams)
- [params] -> (params, [])
- _ -> ([], [])
+ params:fieldparams:rest -> ( params, fieldparams, intercalate ["--"] rest )
+ [params] -> (params, [], [])
+ _ -> ([], [], [])
parseFields :: [String] -> [(String, String)]
parseFields = map (separate (== '='))
diff --git a/Utility/Rsync.hs b/Utility/Rsync.hs
index afb3dcbc8..93c63c989 100644
--- a/Utility/Rsync.hs
+++ b/Utility/Rsync.hs
@@ -11,6 +11,7 @@ import Common
import Utility.Metered
import Data.Char
+import System.Console.GetOpt
{- Generates parameters to make rsync use a specified command as its remote
- shell. -}
@@ -23,13 +24,14 @@ rsyncShell command = [Param "-e", Param $ unwords $ map escape (toCommand comman
escape s = "'" ++ join "''" (split "'" s) ++ "'"
{- Runs rsync in server mode to send a file. -}
-rsyncServerSend :: FilePath -> IO Bool
-rsyncServerSend file = rsync $
- rsyncServerParams ++ [Param "--sender", File file]
+rsyncServerSend :: [CommandParam] -> FilePath -> IO Bool
+rsyncServerSend options file = rsync $
+ rsyncServerParams ++ Param "--sender" : options ++ [File file]
{- Runs rsync in server mode to receive a file. -}
-rsyncServerReceive :: FilePath -> IO Bool
-rsyncServerReceive file = rsync $ rsyncServerParams ++ [File file]
+rsyncServerReceive :: [CommandParam] -> FilePath -> IO Bool
+rsyncServerReceive options file = rsync $
+ rsyncServerParams ++ options ++ [File file]
rsyncServerParams :: [CommandParam]
rsyncServerParams =
@@ -127,3 +129,14 @@ parseRsyncProgress = go [] . reverse . progresschunks
([], _) -> Nothing
(_, []) -> Nothing
(b, _) -> readish b
+
+{- To prevent an evil client to run harmful options on the server, we
+ - cherry-pick those that are harmless. Them only are passed to rsync
+ - when executed through 'git-annex-shell'.
+ - Note: Ensure that when calling getopt, the first component of the
+ - outupt is a subset of the input.
+ -}
+rsyncSafeOptions :: [OptDescr String]
+rsyncSafeOptions = [ Option [] ["bwlimit"] (reqArgLong "bwlimit") "" ]
+ where
+ reqArgLong x = ReqArg (\v -> "--" ++ x ++ "=" ++ v) ""