avoid crashing when there are remotes using unparseable urls
authorJoey Hess <joeyh@joeyh.name>
Mon, 18 Jan 2021 18:52:56 +0000 (14:52 -0400)
committerJoey Hess <joeyh@joeyh.name>
Mon, 18 Jan 2021 18:59:08 +0000 (14:59 -0400)
Including the non-standard URI form that git-remote-gcrypt uses for rsync.

Eg, "ook://foo:bar" cannot be parsed because "bar" is not a valid port
number. But git could have a remote with that, it would try to run
git-remote-ook to handle it. So, git-annex has to allow for such things,
rather than crashing.

This commit was sponsored by Luke Shumaker on Patreon.

CHANGELOG
Git.hs
Git/Construct.hs
Git/GCrypt.hs
Git/Types.hs
Git/Url.hs
Remote/GCrypt.hs
doc/bugs/git_annex_doesn__39__t_cope_with_a_gcrypt__58____58__rsync__58____47____47__.mdwn

index 6fa41b9f72b7380c8e2e5e40052a53c296ccfac3..bcdfb29de2ecf83223b7bcbe21d45d7b073f8a6a 100644 (file)
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -28,6 +28,8 @@ git-annex (8.20201130) UNRELEASED; urgency=medium
   * Bug fix: Fix tilde expansion in ssh urls when the tilde is the last
     character in the url.
     Thanks, Grond for the patch.
+  * Avoid crashing when there are remotes using unparseable urls.
+    Including the non-standard URI form that git-remote-gcrypt uses for rsync.
 
  -- Joey Hess <id@joeyh.name>  Mon, 04 Jan 2021 12:52:41 -0400
 
diff --git a/Git.hs b/Git.hs
index 32cf82e7f2dc1ab672e38e19ff87f56feed07746..f8eedc01ea677ec190604b7d451eb86fff8d3f70 100644 (file)
--- a/Git.hs
+++ b/Git.hs
@@ -3,7 +3,7 @@
  - This is written to be completely independant of git-annex and should be
  - suitable for other uses.
  -
- - Copyright 2010-2020 Joey Hess <id@joeyh.name>
+ - Copyright 2010-2021 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU AGPL version 3 or higher.
  -}
@@ -55,6 +55,7 @@ import Utility.FileMode
 repoDescribe :: Repo -> String
 repoDescribe Repo { remoteName = Just name } = name
 repoDescribe Repo { location = Url url } = show url
+repoDescribe Repo { location = UnparseableUrl url } = url
 repoDescribe Repo { location = Local { worktree = Just dir } } = fromRawFilePath dir
 repoDescribe Repo { location = Local { gitdir = dir } } = fromRawFilePath dir
 repoDescribe Repo { location = LocalUnknown dir } = fromRawFilePath dir
@@ -63,13 +64,14 @@ repoDescribe Repo { location = Unknown } = "UNKNOWN"
 {- Location of the repo, either as a path or url. -}
 repoLocation :: Repo -> String
 repoLocation Repo { location = Url url } = show url
+repoLocation Repo { location = UnparseableUrl url } = url
 repoLocation Repo { location = Local { worktree = Just dir } } = fromRawFilePath dir
 repoLocation Repo { location = Local { gitdir = dir } } = fromRawFilePath dir
 repoLocation Repo { location = LocalUnknown dir } = fromRawFilePath dir
 repoLocation Repo { location = Unknown } = error "unknown repoLocation"
 
 {- Path to a repository. For non-bare, this is the worktree, for bare, 
- - it's the gitdir, and for URL repositories, is the path on the remote
+ - it's the gitdit, and for URL repositories, is the path on the remote
  - host. -}
 repoPath :: Repo -> RawFilePath
 repoPath Repo { location = Url u } = toRawFilePath $ unEscapeString $ uriPath u
@@ -77,6 +79,7 @@ repoPath Repo { location = Local { worktree = Just d } } = d
 repoPath Repo { location = Local { gitdir = d } } = d
 repoPath Repo { location = LocalUnknown dir } = dir
 repoPath Repo { location = Unknown } = error "unknown repoPath"
+repoPath Repo { location = UnparseableUrl _u } = error "unknwon repoPath"
 
 repoWorkTree :: Repo -> Maybe RawFilePath
 repoWorkTree Repo { location = Local { worktree = Just d } } = Just d
@@ -91,6 +94,7 @@ localGitDir _ = error "unknown localGitDir"
  - or bare and non-bare, these functions help with that. -}
 repoIsUrl :: Repo -> Bool
 repoIsUrl Repo { location = Url _ } = True
+repoIsUrl Repo { location = UnparseableUrl _ } = True
 repoIsUrl _ = False
 
 repoIsSsh :: Repo -> Bool
index a369bc4a66dc63614eef3f3a5dfdf50b69b1be42..06b1a1176e8c4b503f7496fd8242418242dfb975 100644 (file)
@@ -1,6 +1,6 @@
 {- Construction of Git Repo objects
  -
- - Copyright 2010-2020 Joey Hess <id@joeyh.name>
+ - Copyright 2010-2021 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU AGPL version 3 or higher.
  -}
@@ -87,24 +87,29 @@ fromAbsPath dir
                                else ret dir
                        )
 
-{- Remote Repo constructor. Throws exception on invalid url.
+{- Construct a Repo for a remote's url.
  -
  - Git is somewhat forgiving about urls to repositories, allowing
- - eg spaces that are not normally allowed unescaped in urls.
+ - eg spaces that are not normally allowed unescaped in urls. Such
+ - characters get escaped.
+ -
+ - This will always succeed, even if the url cannot be parsed
+ - or is invalid, because git can also function despite remotes having
+ - such urls, only failing if such a remote is used.
  -}
 fromUrl :: String -> IO Repo
 fromUrl url
-       | not (isURI url) = fromUrlStrict $ escapeURIString isUnescapedInURI url
-       | otherwise = fromUrlStrict url
+       | not (isURI url) = fromUrl' $ escapeURIString isUnescapedInURI url
+       | otherwise = fromUrl' url
 
-fromUrlStrict :: String -> IO Repo
-fromUrlStrict url
-       | "file://" `isPrefixOf` url = fromAbsPath $ toRawFilePath $
-               unEscapeString $ uriPath u
-       | otherwise = pure $ newFrom $ Url u
-  where
-       u = fromMaybe bad $ parseURI url
-       bad = error $ "bad url " ++ url
+fromUrl' :: String -> IO Repo
+fromUrl' url
+       | "file://" `isPrefixOf` url = case parseURI url of
+               Just u -> fromAbsPath $ toRawFilePath $ unEscapeString $ uriPath u
+               Nothing -> pure $ newFrom $ UnparseableUrl url
+       | otherwise = case parseURI url of
+               Just u -> pure $ newFrom $ Url u
+               Nothing -> pure $ newFrom $ UnparseableUrl url
 
 {- Creates a repo that has an unknown location. -}
 fromUnknown :: Repo
index 2c456213b090f670108132d3bff516272ea6a98b..84bb7b9d144a17a0aae5957e217915f957091348 100644 (file)
@@ -28,6 +28,7 @@ urlPrefix = urlScheme ++ ":"
 
 isEncrypted :: Repo -> Bool
 isEncrypted Repo { location = Url url } = urlPrefix `isPrefixOf` show url
+isEncrypted Repo { location = UnparseableUrl url } = urlPrefix `isPrefixOf` url
 isEncrypted _ = False
 
 {- The first Repo is the git repository that has the second Repo
@@ -36,22 +37,24 @@ isEncrypted _ = False
  - When the remote Repo uses gcrypt, returns the actual underlying
  - git repository that gcrypt is using to store its data. 
  -
- - Throws an exception if an url is invalid or the repo does not use
- - gcrypt.
+ - Throws an exception if the repo does not use gcrypt.
  -}
 encryptedRemote :: Repo -> Repo -> IO Repo
 encryptedRemote baserepo = go
   where
-       go Repo { location = Url url }
+       go Repo { location = Url url } = go' (show url)
+       go Repo { location = UnparseableUrl url } = go' url
+       go _ = notencrypted
+
+       go' u
                | urlPrefix `isPrefixOf` u =
                        fromRemoteLocation (drop plen u) baserepo
                | otherwise = notencrypted
-         where
-               u = show url
-               plen = length urlPrefix
-       go _ = notencrypted
+
        notencrypted = giveup "not a gcrypt encrypted repository"
 
+       plen = length urlPrefix
+
 data ProbeResult = Decryptable | NotDecryptable | NotEncrypted
 
 {- Checks if the git repo at a location uses gcrypt.
index 73c4fe62de4ed0cb34bc4bb956ea08d646d6be1f..db1c71b8f1186b3abcaf603e44e45ddbe42329da 100644 (file)
@@ -34,6 +34,7 @@ data RepoLocation
        = Local { gitdir :: RawFilePath, worktree :: Maybe RawFilePath }
        | LocalUnknown RawFilePath
        | Url URI
+       | UnparseableUrl String
        | Unknown
        deriving (Show, Eq, Ord)
 
index 84306557589593dc8b543606e145bff6c009d0e8..367a6139f17b4d14f7b06ac9fe08a07e9aec5675 100644 (file)
@@ -1,6 +1,6 @@
 {- git repository urls
  -
- - Copyright 2010, 2011 Joey Hess <id@joeyh.name>
+ - Copyright 2010-2021 Joey Hess <id@joeyh.name>
  -
  - Licensed under the GNU AGPL version 3 or higher.
  -}
@@ -23,6 +23,7 @@ import Git
 {- Scheme of an URL repo. -}
 scheme :: Repo -> String
 scheme Repo { location = Url u } = uriScheme u
+scheme Repo { location = UnparseableUrl u } = unparseableUrl u
 scheme repo = notUrl repo
 
 {- Work around a bug in the real uriRegName
@@ -65,13 +66,18 @@ authority = authpart assemble
 {- Applies a function to extract part of the uriAuthority of an URL repo. -}
 authpart :: (URIAuth -> a) -> Repo -> Maybe a
 authpart a Repo { location = Url u } = a <$> uriAuthority u
+authpart _ Repo { location = UnparseableUrl u } = unparseableUrl u
 authpart _ repo = notUrl repo
 
 {- Path part of an URL repo. -}
 path :: Repo -> FilePath
 path Repo { location = Url u } = uriPath u
+path Repo { location = UnparseableUrl u } = unparseableUrl u
 path repo = notUrl repo
 
 notUrl :: Repo -> a
 notUrl repo = error $
        "acting on local git repo " ++ repoDescribe repo ++ " not supported"
+
+unparseableUrl :: String -> a
+unparseableUrl u = error $ "unable to parse repo url " ++ u
index 9aff202669523f5ccbeb6a83d312f2c198f9db6a..522f382c1ad6f6d50050defa8fc438903a2fc313 100644 (file)
@@ -67,7 +67,7 @@ remote :: RemoteType
 remote = specialRemoteType $ RemoteType
        { typename = "gcrypt"
        -- Remote.Git takes care of enumerating gcrypt remotes too,
-       -- and will call our gen on them.
+       -- and will call our chainGen on them.
        , enumerate = const (return [])
        , generate = gen
        , configParser = mkRemoteConfigParser $
index f786dbba8529c799e58a0de22e6265ad111e7972..619972addc249ba7d9adacbd02c1d4f5ceebfe68 100644 (file)
@@ -13,3 +13,6 @@ After adding a `gcrypt::rsync://` remote, `git-annex-sync` and `git-annex-info`
 
 ### What version of git-annex are you using? On what operating system?
 git-annex version: 8.20201127
+
+> [[done]], it will no longer crash, and git-annex sync will work
+> --[[Joey]]